11//! Processes out #[cfg] and #[cfg_attr] attributes from the input for the derive macro
22use std:: iter:: Peekable ;
33
4+ use base_db:: CrateId ;
45use cfg:: { CfgAtom , CfgExpr } ;
56use rustc_hash:: FxHashSet ;
67use syntax:: {
78 ast:: { self , Attr , HasAttrs , Meta , VariantList } ,
8- AstNode , NodeOrToken , SyntaxElement , SyntaxNode , T ,
9+ AstNode , NodeOrToken , SyntaxElement , SyntaxKind , SyntaxNode , T ,
910} ;
1011use tracing:: { debug, warn} ;
1112use tt:: SmolStr ;
1213
1314use crate :: { db:: ExpandDatabase , proc_macro:: ProcMacroKind , MacroCallLoc , MacroDefKind } ;
1415
15- fn check_cfg_attr ( attr : & Attr , loc : & MacroCallLoc , db : & dyn ExpandDatabase ) -> Option < bool > {
16+ fn check_cfg ( db : & dyn ExpandDatabase , attr : & Attr , krate : CrateId ) -> Option < bool > {
1617 if !attr. simple_name ( ) . as_deref ( ) . map ( |v| v == "cfg" ) ? {
1718 return None ;
1819 }
19- debug ! ( "Evaluating cfg {}" , attr) ;
2020 let cfg = parse_from_attr_meta ( attr. meta ( ) ?) ?;
21- debug ! ( "Checking cfg {:?}" , cfg) ;
22- let enabled = db. crate_graph ( ) [ loc. krate ] . cfg_options . check ( & cfg) != Some ( false ) ;
21+ let enabled = db. crate_graph ( ) [ krate] . cfg_options . check ( & cfg) != Some ( false ) ;
2322 Some ( enabled)
2423}
2524
26- fn check_cfg_attr_attr ( attr : & Attr , loc : & MacroCallLoc , db : & dyn ExpandDatabase ) -> Option < bool > {
25+ fn check_cfg_attr ( db : & dyn ExpandDatabase , attr : & Attr , krate : CrateId ) -> Option < bool > {
2726 if !attr. simple_name ( ) . as_deref ( ) . map ( |v| v == "cfg_attr" ) ? {
2827 return None ;
2928 }
30- debug ! ( "Evaluating cfg_attr {}" , attr) ;
3129 let cfg_expr = parse_from_attr_meta ( attr. meta ( ) ?) ?;
32- debug ! ( "Checking cfg_attr {:?}" , cfg_expr) ;
33- let enabled = db. crate_graph ( ) [ loc. krate ] . cfg_options . check ( & cfg_expr) != Some ( false ) ;
30+ let enabled = db. crate_graph ( ) [ krate] . cfg_options . check ( & cfg_expr) != Some ( false ) ;
3431 Some ( enabled)
3532}
3633
3734fn process_has_attrs_with_possible_comma < I : HasAttrs > (
38- items : impl Iterator < Item = I > ,
39- loc : & MacroCallLoc ,
4035 db : & dyn ExpandDatabase ,
36+ items : impl Iterator < Item = I > ,
37+ krate : CrateId ,
4138 remove : & mut FxHashSet < SyntaxElement > ,
4239) -> Option < ( ) > {
4340 for item in items {
4441 let field_attrs = item. attrs ( ) ;
4542 ' attrs: for attr in field_attrs {
46- if check_cfg_attr ( & attr, loc, db) . map ( |enabled| !enabled) . unwrap_or_default ( ) {
47- debug ! ( "censoring type {:?}" , item. syntax( ) ) ;
48- remove. insert ( item. syntax ( ) . clone ( ) . into ( ) ) ;
49- // We need to remove the , as well
50- remove_possible_comma ( & item, remove) ;
51- break ' attrs;
43+ if let Some ( enabled) = check_cfg ( db, & attr, krate) {
44+ if enabled {
45+ debug ! ( "censoring {:?}" , attr. syntax( ) ) ;
46+ remove. insert ( attr. syntax ( ) . clone ( ) . into ( ) ) ;
47+ } else {
48+ debug ! ( "censoring {:?}" , item. syntax( ) ) ;
49+ remove. insert ( item. syntax ( ) . clone ( ) . into ( ) ) ;
50+ // We need to remove the , as well
51+ remove_possible_comma ( & item, remove) ;
52+ break ' attrs;
53+ }
5254 }
5355
54- if let Some ( enabled) = check_cfg_attr_attr ( & attr, loc , db ) {
56+ if let Some ( enabled) = check_cfg_attr ( db , & attr, krate ) {
5557 if enabled {
5658 debug ! ( "Removing cfg_attr tokens {:?}" , attr) ;
5759 let meta = attr. meta ( ) ?;
@@ -60,13 +62,13 @@ fn process_has_attrs_with_possible_comma<I: HasAttrs>(
6062 } else {
6163 debug ! ( "censoring type cfg_attr {:?}" , item. syntax( ) ) ;
6264 remove. insert ( attr. syntax ( ) . clone ( ) . into ( ) ) ;
63- continue ;
6465 }
6566 }
6667 }
6768 }
6869 Some ( ( ) )
6970}
71+
7072#[ derive( Debug , PartialEq , Eq , Clone , Copy ) ]
7173enum CfgExprStage {
7274 /// Stripping the CFGExpr part of the attribute
@@ -78,6 +80,7 @@ enum CfgExprStage {
7880 // Related Issue: https://github.com/rust-lang/rust-analyzer/issues/10110
7981 EverythingElse ,
8082}
83+
8184/// This function creates its own set of tokens to remove. To help prevent malformed syntax as input.
8285fn remove_tokens_within_cfg_attr ( meta : Meta ) -> Option < FxHashSet < SyntaxElement > > {
8386 let mut remove: FxHashSet < SyntaxElement > = FxHashSet :: default ( ) ;
@@ -131,23 +134,28 @@ fn remove_possible_comma(item: &impl AstNode, res: &mut FxHashSet<SyntaxElement>
131134 }
132135}
133136fn process_enum (
134- variants : VariantList ,
135- loc : & MacroCallLoc ,
136137 db : & dyn ExpandDatabase ,
138+ variants : VariantList ,
139+ krate : CrateId ,
137140 remove : & mut FxHashSet < SyntaxElement > ,
138141) -> Option < ( ) > {
139142 ' variant: for variant in variants. variants ( ) {
140143 for attr in variant. attrs ( ) {
141- if check_cfg_attr ( & attr, loc, db) . map ( |enabled| !enabled) . unwrap_or_default ( ) {
142- // Rustc does not strip the attribute if it is enabled. So we will leave it
143- debug ! ( "censoring type {:?}" , variant. syntax( ) ) ;
144- remove. insert ( variant. syntax ( ) . clone ( ) . into ( ) ) ;
145- // We need to remove the , as well
146- remove_possible_comma ( & variant, remove) ;
147- continue ' variant;
148- } ;
144+ if let Some ( enabled) = check_cfg ( db, & attr, krate) {
145+ if enabled {
146+ debug ! ( "censoring {:?}" , attr. syntax( ) ) ;
147+ remove. insert ( attr. syntax ( ) . clone ( ) . into ( ) ) ;
148+ } else {
149+ // Rustc does not strip the attribute if it is enabled. So we will leave it
150+ debug ! ( "censoring type {:?}" , variant. syntax( ) ) ;
151+ remove. insert ( variant. syntax ( ) . clone ( ) . into ( ) ) ;
152+ // We need to remove the , as well
153+ remove_possible_comma ( & variant, remove) ;
154+ continue ' variant;
155+ }
156+ }
149157
150- if let Some ( enabled) = check_cfg_attr_attr ( & attr, loc , db ) {
158+ if let Some ( enabled) = check_cfg_attr ( db , & attr, krate ) {
151159 if enabled {
152160 debug ! ( "Removing cfg_attr tokens {:?}" , attr) ;
153161 let meta = attr. meta ( ) ?;
@@ -156,17 +164,16 @@ fn process_enum(
156164 } else {
157165 debug ! ( "censoring type cfg_attr {:?}" , variant. syntax( ) ) ;
158166 remove. insert ( attr. syntax ( ) . clone ( ) . into ( ) ) ;
159- continue ;
160167 }
161168 }
162169 }
163170 if let Some ( fields) = variant. field_list ( ) {
164171 match fields {
165172 ast:: FieldList :: RecordFieldList ( fields) => {
166- process_has_attrs_with_possible_comma ( fields. fields ( ) , loc , db , remove) ?;
173+ process_has_attrs_with_possible_comma ( db , fields. fields ( ) , krate , remove) ?;
167174 }
168175 ast:: FieldList :: TupleFieldList ( fields) => {
169- process_has_attrs_with_possible_comma ( fields. fields ( ) , loc , db , remove) ?;
176+ process_has_attrs_with_possible_comma ( db , fields. fields ( ) , krate , remove) ?;
170177 }
171178 }
172179 }
@@ -175,9 +182,9 @@ fn process_enum(
175182}
176183
177184pub ( crate ) fn process_cfg_attrs (
185+ db : & dyn ExpandDatabase ,
178186 node : & SyntaxNode ,
179187 loc : & MacroCallLoc ,
180- db : & dyn ExpandDatabase ,
181188) -> Option < FxHashSet < SyntaxElement > > {
182189 // FIXME: #[cfg_eval] is not implemented. But it is not stable yet
183190 let is_derive = match loc. def . kind {
@@ -193,36 +200,35 @@ pub(crate) fn process_cfg_attrs(
193200
194201 let item = ast:: Item :: cast ( node. clone ( ) ) ?;
195202 for attr in item. attrs ( ) {
196- if let Some ( enabled) = check_cfg_attr_attr ( & attr, loc, db ) {
203+ if let Some ( enabled) = check_cfg_attr ( db , & attr, loc. krate ) {
197204 if enabled {
198205 debug ! ( "Removing cfg_attr tokens {:?}" , attr) ;
199206 let meta = attr. meta ( ) ?;
200207 let removes_from_cfg_attr = remove_tokens_within_cfg_attr ( meta) ?;
201208 remove. extend ( removes_from_cfg_attr) ;
202209 } else {
203- debug ! ( "censoring type cfg_attr {:?}" , item. syntax( ) ) ;
210+ debug ! ( "Removing type cfg_attr {:?}" , item. syntax( ) ) ;
204211 remove. insert ( attr. syntax ( ) . clone ( ) . into ( ) ) ;
205- continue ;
206212 }
207213 }
208214 }
209215 match item {
210216 ast:: Item :: Struct ( it) => match it. field_list ( ) ? {
211217 ast:: FieldList :: RecordFieldList ( fields) => {
212- process_has_attrs_with_possible_comma ( fields. fields ( ) , loc, db , & mut remove) ?;
218+ process_has_attrs_with_possible_comma ( db , fields. fields ( ) , loc. krate , & mut remove) ?;
213219 }
214220 ast:: FieldList :: TupleFieldList ( fields) => {
215- process_has_attrs_with_possible_comma ( fields. fields ( ) , loc, db , & mut remove) ?;
221+ process_has_attrs_with_possible_comma ( db , fields. fields ( ) , loc. krate , & mut remove) ?;
216222 }
217223 } ,
218224 ast:: Item :: Enum ( it) => {
219- process_enum ( it. variant_list ( ) ?, loc, db , & mut remove) ?;
225+ process_enum ( db , it. variant_list ( ) ?, loc. krate , & mut remove) ?;
220226 }
221227 ast:: Item :: Union ( it) => {
222228 process_has_attrs_with_possible_comma (
223- it. record_field_list ( ) ?. fields ( ) ,
224- loc,
225229 db,
230+ it. record_field_list ( ) ?. fields ( ) ,
231+ loc. krate ,
226232 & mut remove,
227233 ) ?;
228234 }
@@ -234,10 +240,22 @@ pub(crate) fn process_cfg_attrs(
234240/// Parses a `cfg` attribute from the meta
235241fn parse_from_attr_meta ( meta : Meta ) -> Option < CfgExpr > {
236242 let tt = meta. token_tree ( ) ?;
237- let mut iter = tt. token_trees_and_tokens ( ) . skip ( 1 ) . peekable ( ) ;
243+ let mut iter = tt
244+ . token_trees_and_tokens ( )
245+ . filter ( is_not_whitespace)
246+ . skip ( 1 )
247+ . take_while ( is_not_closing_paren)
248+ . peekable ( ) ;
238249 next_cfg_expr_from_syntax ( & mut iter)
239250}
240251
252+ fn is_not_closing_paren ( element : & NodeOrToken < ast:: TokenTree , syntax:: SyntaxToken > ) -> bool {
253+ !matches ! ( element, NodeOrToken :: Token ( token) if ( token. kind( ) == syntax:: T ![ ')' ] ) )
254+ }
255+ fn is_not_whitespace ( element : & NodeOrToken < ast:: TokenTree , syntax:: SyntaxToken > ) -> bool {
256+ !matches ! ( element, NodeOrToken :: Token ( token) if ( token. kind( ) == SyntaxKind :: WHITESPACE ) )
257+ }
258+
241259fn next_cfg_expr_from_syntax < I > ( iter : & mut Peekable < I > ) -> Option < CfgExpr >
242260where
243261 I : Iterator < Item = NodeOrToken < ast:: TokenTree , syntax:: SyntaxToken > > ,
@@ -256,14 +274,13 @@ where
256274 let Some ( NodeOrToken :: Node ( tree) ) = iter. next ( ) else {
257275 return Some ( CfgExpr :: Invalid ) ;
258276 } ;
259- let mut tree_iter = tree. token_trees_and_tokens ( ) . skip ( 1 ) . peekable ( ) ;
260- while tree_iter
261- . peek ( )
262- . filter (
263- |element| matches ! ( element, NodeOrToken :: Token ( token) if ( token. kind( ) != syntax:: T ![ ')' ] ) ) ,
264- )
265- . is_some ( )
266- {
277+ let mut tree_iter = tree
278+ . token_trees_and_tokens ( )
279+ . filter ( is_not_whitespace)
280+ . skip ( 1 )
281+ . take_while ( is_not_closing_paren)
282+ . peekable ( ) ;
283+ while tree_iter. peek ( ) . is_some ( ) {
267284 let pred = next_cfg_expr_from_syntax ( & mut tree_iter) ;
268285 if let Some ( pred) = pred {
269286 preds. push ( pred) ;
0 commit comments