@@ -78,7 +78,7 @@ mod tests;
7878
7979use std:: { collections:: hash_map, sync:: LazyLock } ;
8080
81- use hir:: { diagnostics:: AnyDiagnostic , HirFileId , InFile , Semantics } ;
81+ use hir:: { db :: ExpandDatabase , diagnostics:: AnyDiagnostic , Crate , HirFileId , InFile , Semantics } ;
8282use ide_db:: {
8383 assists:: { Assist , AssistId , AssistKind , AssistResolveStrategy } ,
8484 base_db:: SourceDatabase ,
@@ -501,6 +501,17 @@ pub fn semantic_diagnostics(
501501
502502 res. retain ( |d| d. severity != Severity :: Allow ) ;
503503
504+ res. retain_mut ( |diag| {
505+ if let Some ( node) = diag
506+ . main_node
507+ . map ( |ptr| ptr. map ( |node| node. to_node ( & ctx. sema . parse_or_expand ( ptr. file_id ) ) ) )
508+ {
509+ handle_diag_from_macros ( & ctx. sema , diag, & node)
510+ } else {
511+ true
512+ }
513+ } ) ;
514+
504515 res
505516}
506517
@@ -517,6 +528,35 @@ pub fn full_diagnostics(
517528 res
518529}
519530
531+ /// Returns whether to keep this diagnostic (or remove it).
532+ fn handle_diag_from_macros (
533+ sema : & Semantics < ' _ , RootDatabase > ,
534+ diag : & mut Diagnostic ,
535+ node : & InFile < SyntaxNode > ,
536+ ) -> bool {
537+ let Some ( macro_file) = node. file_id . macro_file ( ) else { return true } ;
538+ let span_map = sema. db . expansion_span_map ( macro_file) ;
539+ let mut spans = span_map. spans_for_range ( node. text_range ( ) ) ;
540+ if spans. any ( |span| {
541+ sema. db . lookup_intern_syntax_context ( span. ctx ) . outer_expn . is_some_and ( |expansion| {
542+ let macro_call =
543+ sema. db . lookup_intern_macro_call ( expansion. as_macro_file ( ) . macro_call_id ) ;
544+ !Crate :: from ( macro_call. def . krate ) . origin ( sema. db ) . is_local ( )
545+ } )
546+ } ) {
547+ // Disable suggestions for external macros, they'll change library code and it's just bad.
548+ diag. fixes = None ;
549+
550+ // All Clippy lints report in macros, see https://github.com/rust-lang/rust-clippy/blob/903293b199364/declare_clippy_lint/src/lib.rs#L172.
551+ if let DiagnosticCode :: RustcLint ( lint) = diag. code {
552+ if !LINTS_TO_REPORT_IN_EXTERNAL_MACROS . contains ( lint) {
553+ return false ;
554+ }
555+ } ;
556+ }
557+ true
558+ }
559+
520560// `__RA_EVERY_LINT` is a fake lint group to allow every lint in proc macros
521561
522562static RUSTC_LINT_GROUPS_DICT : LazyLock < FxHashMap < & str , Vec < & str > > > =
@@ -525,6 +565,10 @@ static RUSTC_LINT_GROUPS_DICT: LazyLock<FxHashMap<&str, Vec<&str>>> =
525565static CLIPPY_LINT_GROUPS_DICT : LazyLock < FxHashMap < & str , Vec < & str > > > =
526566 LazyLock :: new ( || build_group_dict ( CLIPPY_LINT_GROUPS , & [ "__RA_EVERY_LINT" ] , "clippy::" ) ) ;
527567
568+ // FIXME: Autogenerate this instead of enumerating by hand.
569+ static LINTS_TO_REPORT_IN_EXTERNAL_MACROS : LazyLock < FxHashSet < & str > > =
570+ LazyLock :: new ( || FxHashSet :: from_iter ( [ ] ) ) ;
571+
528572fn build_group_dict (
529573 lint_group : & ' static [ LintGroup ] ,
530574 all_groups : & ' static [ & ' static str ] ,
0 commit comments