1
1
//! Processes out #[cfg] and #[cfg_attr] attributes from the input for the derive macro
2
2
use std:: iter:: Peekable ;
3
3
4
+ use base_db:: CrateId ;
4
5
use cfg:: { CfgAtom , CfgExpr } ;
5
6
use rustc_hash:: FxHashSet ;
6
7
use syntax:: {
7
8
ast:: { self , Attr , HasAttrs , Meta , VariantList } ,
8
- AstNode , NodeOrToken , SyntaxElement , SyntaxNode , T ,
9
+ AstNode , NodeOrToken , SyntaxElement , SyntaxKind , SyntaxNode , T ,
9
10
} ;
10
11
use tracing:: { debug, warn} ;
11
12
use tt:: SmolStr ;
12
13
13
14
use crate :: { db:: ExpandDatabase , proc_macro:: ProcMacroKind , MacroCallLoc , MacroDefKind } ;
14
15
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 > {
16
17
if !attr. simple_name ( ) . as_deref ( ) . map ( |v| v == "cfg" ) ? {
17
18
return None ;
18
19
}
19
- debug ! ( "Evaluating cfg {}" , attr) ;
20
20
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 ) ;
23
22
Some ( enabled)
24
23
}
25
24
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 > {
27
26
if !attr. simple_name ( ) . as_deref ( ) . map ( |v| v == "cfg_attr" ) ? {
28
27
return None ;
29
28
}
30
- debug ! ( "Evaluating cfg_attr {}" , attr) ;
31
29
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 ) ;
34
31
Some ( enabled)
35
32
}
36
33
37
34
fn process_has_attrs_with_possible_comma < I : HasAttrs > (
38
- items : impl Iterator < Item = I > ,
39
- loc : & MacroCallLoc ,
40
35
db : & dyn ExpandDatabase ,
36
+ items : impl Iterator < Item = I > ,
37
+ krate : CrateId ,
41
38
remove : & mut FxHashSet < SyntaxElement > ,
42
39
) -> Option < ( ) > {
43
40
for item in items {
44
41
let field_attrs = item. attrs ( ) ;
45
42
' 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
+ }
52
54
}
53
55
54
- if let Some ( enabled) = check_cfg_attr_attr ( & attr, loc , db ) {
56
+ if let Some ( enabled) = check_cfg_attr ( db , & attr, krate ) {
55
57
if enabled {
56
58
debug ! ( "Removing cfg_attr tokens {:?}" , attr) ;
57
59
let meta = attr. meta ( ) ?;
@@ -60,13 +62,13 @@ fn process_has_attrs_with_possible_comma<I: HasAttrs>(
60
62
} else {
61
63
debug ! ( "censoring type cfg_attr {:?}" , item. syntax( ) ) ;
62
64
remove. insert ( attr. syntax ( ) . clone ( ) . into ( ) ) ;
63
- continue ;
64
65
}
65
66
}
66
67
}
67
68
}
68
69
Some ( ( ) )
69
70
}
71
+
70
72
#[ derive( Debug , PartialEq , Eq , Clone , Copy ) ]
71
73
enum CfgExprStage {
72
74
/// Stripping the CFGExpr part of the attribute
@@ -78,6 +80,7 @@ enum CfgExprStage {
78
80
// Related Issue: https://github.com/rust-lang/rust-analyzer/issues/10110
79
81
EverythingElse ,
80
82
}
83
+
81
84
/// This function creates its own set of tokens to remove. To help prevent malformed syntax as input.
82
85
fn remove_tokens_within_cfg_attr ( meta : Meta ) -> Option < FxHashSet < SyntaxElement > > {
83
86
let mut remove: FxHashSet < SyntaxElement > = FxHashSet :: default ( ) ;
@@ -131,23 +134,28 @@ fn remove_possible_comma(item: &impl AstNode, res: &mut FxHashSet<SyntaxElement>
131
134
}
132
135
}
133
136
fn process_enum (
134
- variants : VariantList ,
135
- loc : & MacroCallLoc ,
136
137
db : & dyn ExpandDatabase ,
138
+ variants : VariantList ,
139
+ krate : CrateId ,
137
140
remove : & mut FxHashSet < SyntaxElement > ,
138
141
) -> Option < ( ) > {
139
142
' variant: for variant in variants. variants ( ) {
140
143
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
+ }
149
157
150
- if let Some ( enabled) = check_cfg_attr_attr ( & attr, loc , db ) {
158
+ if let Some ( enabled) = check_cfg_attr ( db , & attr, krate ) {
151
159
if enabled {
152
160
debug ! ( "Removing cfg_attr tokens {:?}" , attr) ;
153
161
let meta = attr. meta ( ) ?;
@@ -156,17 +164,16 @@ fn process_enum(
156
164
} else {
157
165
debug ! ( "censoring type cfg_attr {:?}" , variant. syntax( ) ) ;
158
166
remove. insert ( attr. syntax ( ) . clone ( ) . into ( ) ) ;
159
- continue ;
160
167
}
161
168
}
162
169
}
163
170
if let Some ( fields) = variant. field_list ( ) {
164
171
match fields {
165
172
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) ?;
167
174
}
168
175
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) ?;
170
177
}
171
178
}
172
179
}
@@ -175,9 +182,9 @@ fn process_enum(
175
182
}
176
183
177
184
pub ( crate ) fn process_cfg_attrs (
185
+ db : & dyn ExpandDatabase ,
178
186
node : & SyntaxNode ,
179
187
loc : & MacroCallLoc ,
180
- db : & dyn ExpandDatabase ,
181
188
) -> Option < FxHashSet < SyntaxElement > > {
182
189
// FIXME: #[cfg_eval] is not implemented. But it is not stable yet
183
190
let is_derive = match loc. def . kind {
@@ -193,36 +200,35 @@ pub(crate) fn process_cfg_attrs(
193
200
194
201
let item = ast:: Item :: cast ( node. clone ( ) ) ?;
195
202
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 ) {
197
204
if enabled {
198
205
debug ! ( "Removing cfg_attr tokens {:?}" , attr) ;
199
206
let meta = attr. meta ( ) ?;
200
207
let removes_from_cfg_attr = remove_tokens_within_cfg_attr ( meta) ?;
201
208
remove. extend ( removes_from_cfg_attr) ;
202
209
} else {
203
- debug ! ( "censoring type cfg_attr {:?}" , item. syntax( ) ) ;
210
+ debug ! ( "Removing type cfg_attr {:?}" , item. syntax( ) ) ;
204
211
remove. insert ( attr. syntax ( ) . clone ( ) . into ( ) ) ;
205
- continue ;
206
212
}
207
213
}
208
214
}
209
215
match item {
210
216
ast:: Item :: Struct ( it) => match it. field_list ( ) ? {
211
217
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) ?;
213
219
}
214
220
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) ?;
216
222
}
217
223
} ,
218
224
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) ?;
220
226
}
221
227
ast:: Item :: Union ( it) => {
222
228
process_has_attrs_with_possible_comma (
223
- it. record_field_list ( ) ?. fields ( ) ,
224
- loc,
225
229
db,
230
+ it. record_field_list ( ) ?. fields ( ) ,
231
+ loc. krate ,
226
232
& mut remove,
227
233
) ?;
228
234
}
@@ -234,10 +240,22 @@ pub(crate) fn process_cfg_attrs(
234
240
/// Parses a `cfg` attribute from the meta
235
241
fn parse_from_attr_meta ( meta : Meta ) -> Option < CfgExpr > {
236
242
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 ( ) ;
238
249
next_cfg_expr_from_syntax ( & mut iter)
239
250
}
240
251
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
+
241
259
fn next_cfg_expr_from_syntax < I > ( iter : & mut Peekable < I > ) -> Option < CfgExpr >
242
260
where
243
261
I : Iterator < Item = NodeOrToken < ast:: TokenTree , syntax:: SyntaxToken > > ,
@@ -256,14 +274,13 @@ where
256
274
let Some ( NodeOrToken :: Node ( tree) ) = iter. next ( ) else {
257
275
return Some ( CfgExpr :: Invalid ) ;
258
276
} ;
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 ( ) {
267
284
let pred = next_cfg_expr_from_syntax ( & mut tree_iter) ;
268
285
if let Some ( pred) = pred {
269
286
preds. push ( pred) ;
0 commit comments