@@ -76,8 +76,9 @@ mod handlers {
7676#[ cfg( test) ]
7777mod tests;
7878
79- use std:: { collections:: hash_map, sync:: LazyLock } ;
79+ use std:: { collections:: hash_map, iter , sync:: LazyLock } ;
8080
81+ use either:: Either ;
8182use hir:: { db:: ExpandDatabase , diagnostics:: AnyDiagnostic , Crate , HirFileId , InFile , Semantics } ;
8283use ide_db:: {
8384 assists:: { Assist , AssistId , AssistKind , AssistResolveStrategy } ,
@@ -92,7 +93,7 @@ use ide_db::{
9293use itertools:: Itertools ;
9394use syntax:: {
9495 ast:: { self , AstNode , HasAttrs } ,
95- AstPtr , Edition , SmolStr , SyntaxNode , SyntaxNodePtr , TextRange ,
96+ AstPtr , Edition , NodeOrToken , SmolStr , SyntaxKind , SyntaxNode , SyntaxNodePtr , TextRange , T ,
9697} ;
9798
9899// FIXME: Make this an enum
@@ -647,6 +648,7 @@ fn find_outline_mod_lint_severity(
647648 let mut result = None ;
648649 let lint_groups = lint_groups ( & diag. code ) ;
649650 lint_attrs (
651+ sema,
650652 ast:: AnyHasAttrs :: cast ( module_source_file. value ) . expect ( "SourceFile always has attrs" ) ,
651653 edition,
652654 )
@@ -763,7 +765,7 @@ fn fill_lint_attrs(
763765
764766 if let Some ( ancestor) = ast:: AnyHasAttrs :: cast ( ancestor) {
765767 // 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) | {
767769 if diag_severity. is_none ( ) && lint_groups ( & diag. code ) . contains ( & & * lint) {
768770 diag_severity = Some ( severity) ;
769771 }
@@ -793,7 +795,7 @@ fn fill_lint_attrs(
793795 cache_stack. pop ( ) ;
794796 return diag_severity;
795797 } 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) | {
797799 if diag_severity. is_none ( ) && lint_groups ( & diag. code ) . contains ( & & * lint) {
798800 diag_severity = Some ( severity) ;
799801 }
@@ -809,20 +811,27 @@ fn fill_lint_attrs(
809811 }
810812}
811813
812- fn lint_attrs (
814+ fn lint_attrs < ' a > (
815+ sema : & ' a Semantics < ' a , RootDatabase > ,
813816 ancestor : ast:: AnyHasAttrs ,
814817 edition : Edition ,
815- ) -> impl Iterator < Item = ( SmolStr , Severity ) > {
818+ ) -> impl Iterator < Item = ( SmolStr , Severity ) > + ' a {
816819 ancestor
817820 . attrs_including_inner ( )
818821 . filter_map ( |attr| {
819822 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+ }
823831 _ => None ,
824832 } )
825833 } )
834+ . flatten ( )
826835 . flat_map ( move |( severity, lints) | {
827836 parse_tt_as_comma_sep_paths ( lints, edition) . into_iter ( ) . flat_map ( move |lints| {
828837 // Rejoin the idents with `::`, so we have no spaces in between.
@@ -836,6 +845,58 @@ fn lint_attrs(
836845 } )
837846}
838847
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+
839900fn lint_groups ( lint : & DiagnosticCode ) -> & ' static [ & ' static str ] {
840901 match lint {
841902 DiagnosticCode :: RustcLint ( name) => {
0 commit comments