Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-diagnostics/src/handlers/unlinked_file.rs')
-rw-r--r--crates/ide-diagnostics/src/handlers/unlinked_file.rs30
1 files changed, 22 insertions, 8 deletions
diff --git a/crates/ide-diagnostics/src/handlers/unlinked_file.rs b/crates/ide-diagnostics/src/handlers/unlinked_file.rs
index 3d45a75913..271e7ce73b 100644
--- a/crates/ide-diagnostics/src/handlers/unlinked_file.rs
+++ b/crates/ide-diagnostics/src/handlers/unlinked_file.rs
@@ -2,7 +2,7 @@
use std::iter;
-use hir::{db::DefDatabase, InFile, ModuleSource};
+use hir::{db::DefDatabase, DefMap, InFile, ModuleSource};
use ide_db::{
base_db::{FileId, FileLoader, SourceDatabase, SourceDatabaseExt},
source_change::SourceChange,
@@ -10,7 +10,7 @@ use ide_db::{
};
use syntax::{
ast::{self, edit::IndentLevel, HasModuleItem, HasName},
- AstNode, TextRange, TextSize,
+ AstNode, TextRange,
};
use text_edit::TextEdit;
@@ -27,14 +27,28 @@ pub(crate) fn unlinked_file(
) {
// Limit diagnostic to the first few characters in the file. This matches how VS Code
// renders it with the full span, but on other editors, and is less invasive.
+ let fixes = fixes(ctx, file_id);
+ // FIXME: This is a hack for the vscode extension to notice whether there is an autofix or not before having to resolve diagnostics.
+ // This is to prevent project linking popups from appearing when there is an autofix. https://github.com/rust-lang/rust-analyzer/issues/14523
+ let message = if fixes.is_none() {
+ "file not included in crate hierarchy"
+ } else {
+ "file not included in module tree"
+ };
+
let range = ctx.sema.db.parse(file_id).syntax_node().text_range();
- // FIXME: This is wrong if one of the first three characters is not ascii: `//Ы`.
- let range = range.intersect(TextRange::up_to(TextSize::of("..."))).unwrap_or(range);
+ let range = FileLoader::file_text(ctx.sema.db, file_id)
+ .char_indices()
+ .take(3)
+ .last()
+ .map(|(i, _)| i)
+ .map(|i| TextRange::up_to(i.try_into().unwrap()))
+ .unwrap_or(range);
acc.push(
- Diagnostic::new("unlinked-file", "file not included in module tree", range)
+ Diagnostic::new("unlinked-file", message, range)
.severity(Severity::WeakWarning)
- .with_fixes(fixes(ctx, file_id)),
+ .with_fixes(fixes),
);
}
@@ -60,7 +74,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, file_id: FileId) -> Option<Vec<Assist>> {
'crates: for &krate in &*ctx.sema.db.relevant_crates(file_id) {
let crate_def_map = ctx.sema.db.crate_def_map(krate);
- let root_module = &crate_def_map[crate_def_map.root()];
+ let root_module = &crate_def_map[DefMap::ROOT];
let Some(root_file_id) = root_module.origin.file_id() else { continue };
let Some(crate_root_path) = source_root.path_for_file(&root_file_id) else { continue };
let Some(rel) = parent.strip_prefix(&crate_root_path.parent()?) else { continue };
@@ -92,7 +106,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, file_id: FileId) -> Option<Vec<Assist>> {
// if we aren't adding to a crate root, walk backwards such that we support `#[path = ...]` overrides if possible
// build all parent paths of the form `../module_name/mod.rs` and `../module_name.rs`
- let paths = iter::successors(Some(parent.clone()), |prev| prev.parent()).filter_map(|path| {
+ let paths = iter::successors(Some(parent), |prev| prev.parent()).filter_map(|path| {
let parent = path.parent()?;
let (name, _) = path.name_and_extension()?;
Some(([parent.join(&format!("{name}.rs"))?, path.join("mod.rs")?], name.to_owned()))