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