Unnamed repository; edit this file 'description' to name the repository.
Handle errors and lints from external macros
Some lints should not be reported if they originate from an external macro, and quickfixes should be disabled (or they'll change library code).
| -rw-r--r-- | crates/ide-diagnostics/src/handlers/incorrect_case.rs | 17 | ||||
| -rw-r--r-- | crates/ide-diagnostics/src/lib.rs | 46 |
2 files changed, 62 insertions, 1 deletions
diff --git a/crates/ide-diagnostics/src/handlers/incorrect_case.rs b/crates/ide-diagnostics/src/handlers/incorrect_case.rs index ddc19a0121..be9c6c0a1a 100644 --- a/crates/ide-diagnostics/src/handlers/incorrect_case.rs +++ b/crates/ide-diagnostics/src/handlers/incorrect_case.rs @@ -370,6 +370,23 @@ mod F { } #[test] + fn external_macro() { + check_diagnostics( + r#" +//- /library.rs library crate:library +#[macro_export] +macro_rules! trigger_lint { + () => { let FOO: () }; +} +//- /user.rs crate:user deps:library +fn foo() { + library::trigger_lint!(); +} + "#, + ); + } + + #[test] fn complex_ignore() { check_diagnostics( r#" diff --git a/crates/ide-diagnostics/src/lib.rs b/crates/ide-diagnostics/src/lib.rs index 69da744e66..8c576d64ec 100644 --- a/crates/ide-diagnostics/src/lib.rs +++ b/crates/ide-diagnostics/src/lib.rs @@ -78,7 +78,7 @@ mod tests; use std::{collections::hash_map, sync::LazyLock}; -use hir::{diagnostics::AnyDiagnostic, HirFileId, InFile, Semantics}; +use hir::{db::ExpandDatabase, diagnostics::AnyDiagnostic, Crate, HirFileId, InFile, Semantics}; use ide_db::{ assists::{Assist, AssistId, AssistKind, AssistResolveStrategy}, base_db::SourceDatabase, @@ -501,6 +501,17 @@ pub fn semantic_diagnostics( res.retain(|d| d.severity != Severity::Allow); + res.retain_mut(|diag| { + if let Some(node) = diag + .main_node + .map(|ptr| ptr.map(|node| node.to_node(&ctx.sema.parse_or_expand(ptr.file_id)))) + { + handle_diag_from_macros(&ctx.sema, diag, &node) + } else { + true + } + }); + res } @@ -517,6 +528,35 @@ pub fn full_diagnostics( res } +/// Returns whether to keep this diagnostic (or remove it). +fn handle_diag_from_macros( + sema: &Semantics<'_, RootDatabase>, + diag: &mut Diagnostic, + node: &InFile<SyntaxNode>, +) -> bool { + let Some(macro_file) = node.file_id.macro_file() else { return true }; + let span_map = sema.db.expansion_span_map(macro_file); + let mut spans = span_map.spans_for_range(node.text_range()); + if spans.any(|span| { + sema.db.lookup_intern_syntax_context(span.ctx).outer_expn.is_some_and(|expansion| { + let macro_call = + sema.db.lookup_intern_macro_call(expansion.as_macro_file().macro_call_id); + !Crate::from(macro_call.def.krate).origin(sema.db).is_local() + }) + }) { + // Disable suggestions for external macros, they'll change library code and it's just bad. + diag.fixes = None; + + // All Clippy lints report in macros, see https://github.com/rust-lang/rust-clippy/blob/903293b199364/declare_clippy_lint/src/lib.rs#L172. + if let DiagnosticCode::RustcLint(lint) = diag.code { + if !LINTS_TO_REPORT_IN_EXTERNAL_MACROS.contains(lint) { + return false; + } + }; + } + true +} + // `__RA_EVERY_LINT` is a fake lint group to allow every lint in proc macros static RUSTC_LINT_GROUPS_DICT: LazyLock<FxHashMap<&str, Vec<&str>>> = @@ -525,6 +565,10 @@ static RUSTC_LINT_GROUPS_DICT: LazyLock<FxHashMap<&str, Vec<&str>>> = static CLIPPY_LINT_GROUPS_DICT: LazyLock<FxHashMap<&str, Vec<&str>>> = LazyLock::new(|| build_group_dict(CLIPPY_LINT_GROUPS, &["__RA_EVERY_LINT"], "clippy::")); +// FIXME: Autogenerate this instead of enumerating by hand. +static LINTS_TO_REPORT_IN_EXTERNAL_MACROS: LazyLock<FxHashSet<&str>> = + LazyLock::new(|| FxHashSet::from_iter([])); + fn build_group_dict( lint_group: &'static [LintGroup], all_groups: &'static [&'static str], |