@@ -76,8 +76,9 @@ mod handlers {
76
76
#[ cfg( test) ]
77
77
mod tests;
78
78
79
- use std:: { collections:: hash_map, sync:: LazyLock } ;
79
+ use std:: { collections:: hash_map, iter , sync:: LazyLock } ;
80
80
81
+ use either:: Either ;
81
82
use hir:: { db:: ExpandDatabase , diagnostics:: AnyDiagnostic , Crate , HirFileId , InFile , Semantics } ;
82
83
use ide_db:: {
83
84
assists:: { Assist , AssistId , AssistKind , AssistResolveStrategy } ,
@@ -92,7 +93,7 @@ use ide_db::{
92
93
use itertools:: Itertools ;
93
94
use syntax:: {
94
95
ast:: { self , AstNode , HasAttrs } ,
95
- AstPtr , Edition , SmolStr , SyntaxNode , SyntaxNodePtr , TextRange ,
96
+ AstPtr , Edition , NodeOrToken , SmolStr , SyntaxKind , SyntaxNode , SyntaxNodePtr , TextRange , T ,
96
97
} ;
97
98
98
99
// FIXME: Make this an enum
@@ -647,6 +648,7 @@ fn find_outline_mod_lint_severity(
647
648
let mut result = None ;
648
649
let lint_groups = lint_groups ( & diag. code ) ;
649
650
lint_attrs (
651
+ sema,
650
652
ast:: AnyHasAttrs :: cast ( module_source_file. value ) . expect ( "SourceFile always has attrs" ) ,
651
653
edition,
652
654
)
@@ -763,7 +765,7 @@ fn fill_lint_attrs(
763
765
764
766
if let Some ( ancestor) = ast:: AnyHasAttrs :: cast ( ancestor) {
765
767
// Insert this node's attributes into any outline descendant, including this node.
766
- lint_attrs ( ancestor, edition) . for_each ( |( lint, severity) | {
768
+ lint_attrs ( sema , ancestor, edition) . for_each ( |( lint, severity) | {
767
769
if diag_severity. is_none ( ) && lint_groups ( & diag. code ) . contains ( & & * lint) {
768
770
diag_severity = Some ( severity) ;
769
771
}
@@ -793,7 +795,7 @@ fn fill_lint_attrs(
793
795
cache_stack. pop ( ) ;
794
796
return diag_severity;
795
797
} else if let Some ( ancestor) = ast:: AnyHasAttrs :: cast ( ancestor) {
796
- lint_attrs ( ancestor, edition) . for_each ( |( lint, severity) | {
798
+ lint_attrs ( sema , ancestor, edition) . for_each ( |( lint, severity) | {
797
799
if diag_severity. is_none ( ) && lint_groups ( & diag. code ) . contains ( & & * lint) {
798
800
diag_severity = Some ( severity) ;
799
801
}
@@ -809,20 +811,27 @@ fn fill_lint_attrs(
809
811
}
810
812
}
811
813
812
- fn lint_attrs (
814
+ fn lint_attrs < ' a > (
815
+ sema : & ' a Semantics < ' a , RootDatabase > ,
813
816
ancestor : ast:: AnyHasAttrs ,
814
817
edition : Edition ,
815
- ) -> impl Iterator < Item = ( SmolStr , Severity ) > {
818
+ ) -> impl Iterator < Item = ( SmolStr , Severity ) > + ' a {
816
819
ancestor
817
820
. attrs_including_inner ( )
818
821
. filter_map ( |attr| {
819
822
attr. as_simple_call ( ) . and_then ( |( name, value) | match & * name {
820
- "allow" | "expect" => Some ( ( Severity :: Allow , value) ) ,
821
- "warn" => Some ( ( Severity :: Warning , value) ) ,
822
- "forbid" | "deny" => Some ( ( Severity :: Error , value) ) ,
823
+ "allow" | "expect" => Some ( Either :: Left ( iter:: once ( ( Severity :: Allow , value) ) ) ) ,
824
+ "warn" => Some ( Either :: Left ( iter:: once ( ( Severity :: Warning , value) ) ) ) ,
825
+ "forbid" | "deny" => Some ( Either :: Left ( iter:: once ( ( Severity :: Error , value) ) ) ) ,
826
+ "cfg_attr" => {
827
+ let mut lint_attrs = Vec :: new ( ) ;
828
+ cfg_attr_lint_attrs ( sema, & value, & mut lint_attrs) ;
829
+ Some ( Either :: Right ( lint_attrs. into_iter ( ) ) )
830
+ }
823
831
_ => None ,
824
832
} )
825
833
} )
834
+ . flatten ( )
826
835
. flat_map ( move |( severity, lints) | {
827
836
parse_tt_as_comma_sep_paths ( lints, edition) . into_iter ( ) . flat_map ( move |lints| {
828
837
// Rejoin the idents with `::`, so we have no spaces in between.
@@ -836,6 +845,58 @@ fn lint_attrs(
836
845
} )
837
846
}
838
847
848
+ fn cfg_attr_lint_attrs (
849
+ sema : & Semantics < ' _ , RootDatabase > ,
850
+ value : & ast:: TokenTree ,
851
+ lint_attrs : & mut Vec < ( Severity , ast:: TokenTree ) > ,
852
+ ) {
853
+ let prev_len = lint_attrs. len ( ) ;
854
+
855
+ let mut iter = value. token_trees_and_tokens ( ) . filter ( |it| match it {
856
+ NodeOrToken :: Node ( _) => true ,
857
+ NodeOrToken :: Token ( it) => !it. kind ( ) . is_trivia ( ) ,
858
+ } ) ;
859
+
860
+ // Skip the condition.
861
+ for value in & mut iter {
862
+ if value. as_token ( ) . is_some_and ( |it| it. kind ( ) == T ! [ , ] ) {
863
+ break ;
864
+ }
865
+ }
866
+
867
+ while let Some ( value) = iter. next ( ) {
868
+ if let Some ( token) = value. as_token ( ) {
869
+ if token. kind ( ) == SyntaxKind :: IDENT {
870
+ let severity = match token. text ( ) {
871
+ "allow" | "expect" => Some ( Severity :: Allow ) ,
872
+ "warn" => Some ( Severity :: Warning ) ,
873
+ "forbid" | "deny" => Some ( Severity :: Error ) ,
874
+ "cfg_attr" => {
875
+ if let Some ( NodeOrToken :: Node ( value) ) = iter. next ( ) {
876
+ cfg_attr_lint_attrs ( sema, & value, lint_attrs) ;
877
+ }
878
+ None
879
+ }
880
+ _ => None ,
881
+ } ;
882
+ if let Some ( severity) = severity {
883
+ let lints = iter. next ( ) ;
884
+ if let Some ( NodeOrToken :: Node ( lints) ) = lints {
885
+ lint_attrs. push ( ( severity, lints) ) ;
886
+ }
887
+ }
888
+ }
889
+ }
890
+ }
891
+
892
+ if prev_len != lint_attrs. len ( ) {
893
+ if let Some ( false ) | None = sema. check_cfg_attr ( value) {
894
+ // Discard the attributes when the condition is false.
895
+ lint_attrs. truncate ( prev_len) ;
896
+ }
897
+ }
898
+ }
899
+
839
900
fn lint_groups ( lint : & DiagnosticCode ) -> & ' static [ & ' static str ] {
840
901
match lint {
841
902
DiagnosticCode :: RustcLint ( name) => {
0 commit comments