Skip to content

draft: fix: unlinked-file diagnostic handling #19398

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 67 additions & 25 deletions crates/ide-diagnostics/src/handlers/unlinked_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,39 +28,40 @@ pub(crate) fn unlinked_file(
file_id: FileId,
) {
let mut range = TextRange::up_to(ctx.sema.db.line_index(file_id).len());
let fixes = fixes(ctx, file_id, range);
// FIXME: This is a hack for the vscode extension to notice whether there is an autofix or not before having to resolve diagnostics.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fwiw this fixme isn't quite applicable anymore currently. The unlinked file handling the vscode client is completely disabled at the moment. (the vscode extension used to scan the content of the message here to do conditional work which is what this FIXME is referring to)

// 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() {
"This file is not included in any crates, so rust-analyzer can't offer IDE services."

let check_crate = ctx.sema.first_crate(file_id);
let is_in_crate = check_crate.is_some();

let is_crate_root = check_crate
.as_ref()
.map(|krate| krate.root_module().definition_source(ctx.sema.db).file_id == file_id)
.unwrap_or(false);

let is_unlinked = !is_in_crate || is_crate_root;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am confused by what you are doing here. If we end up with this diagnostic we have already figured out that the file is in fact unlinked so I am not sure what all of this is actually trying to compute here


Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
eprintln!(
"[DEBUG] file_id: {:?}, is_in_crate: {}, is_crate_root: {}, is_unlinked: {}",
file_id, is_in_crate, is_crate_root, is_unlinked
);

let fixes = if is_unlinked { None } else { fixes(ctx, file_id, range) };

let has_fixes = fixes.is_some();

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
eprintln!("[DEBUG] file_id: {:?}, fixes: {:?}, has_fixes: {}", file_id, fixes, has_fixes);

let message = if has_fixes {
"This file is not linked to a crate, but an autofix is available to add it."
} else {
"This file is not included anywhere in the module tree, so rust-analyzer can't offer IDE services."
"This file is not included in any crates, so rust-analyzer can't provide IDE features. Ensure it's part of your Cargo workspace."
};

let message = format!(
"{message}\n\nIf you're intentionally working on unowned files, you can silence this warning by adding \"unlinked-file\" to rust-analyzer.diagnostics.disabled in your settings."
);

let mut unused = true;

if fixes.is_none() {
// If we don't have a fix, the unlinked-file diagnostic is not
// actionable. This generally means that rust-analyzer hasn't
// finished startup, or we couldn't find the Cargo.toml.
//
// Only show this diagnostic on the first three characters of
// the file, to avoid overwhelming the user during startup.
range = SourceDatabase::file_text(ctx.sema.db, file_id)
.text(ctx.sema.db)
if !has_fixes {
let file_text = ctx.sema.db.file_text(file_id);
let text = file_text.text(ctx.sema.db);

range = text
.char_indices()
.take(3)
.last()
.map(|(i, _)| i)
.map(|i| TextRange::up_to(i.try_into().unwrap()))
.nth(3)
.map(|(i, _)| TextRange::up_to(i.try_into().unwrap()))
.unwrap_or(range);
// Prefer a diagnostic underline over graying out the text,
// since we're only highlighting a small region.
unused = false;
}

acc.push(
Expand All @@ -69,7 +70,7 @@ pub(crate) fn unlinked_file(
message,
FileRange { file_id, range },
)
.with_unused(unused)
.with_unused(!has_fixes)
.with_fixes(fixes),
);
}
Expand Down Expand Up @@ -516,6 +517,47 @@ mod bar {
//- /main.rs
include!("bar/foo/mod.rs");
//- /bar/foo/mod.rs
"#,
);
}

#[test]
fn unlinked_file_suggests_fix() {
check_fix(
r#"
//- /main.rs
fn main() {}
//- /foo.rs
$0
"#,
r#"
mod foo;

fn main() {}
"#,
);
}

#[test]
fn unlinked_file_no_fix_without_cargo_toml() {
check_no_fix(
r#"
//- /foo.rs
fn hello() {}
"#,
);
}

#[test]
fn unlinked_file_respects_unused_flag() {
check_diagnostics(
r#"
//- /main.rs
fn main() {}

//- /foo.rs
fn hello() {}
//^^^^^^^^^^ weak: This file is not included in any crates, so rust-analyzer can't provide IDE features. Ensure it's part of your Cargo workspace.
"#,
);
}
Expand Down
Loading