10
10
11
11
use attr:: AttrMetaMethods ;
12
12
use diagnostic:: SpanHandler ;
13
- use feature_gate:: GatedCfg ;
13
+ use feature_gate:: GatedCfgAttr ;
14
14
use fold:: Folder ;
15
15
use { ast, fold, attr} ;
16
+ use visit;
16
17
use codemap:: { Spanned , respan} ;
17
18
use ptr:: P ;
18
19
@@ -28,20 +29,26 @@ struct Context<'a, F> where F: FnMut(&[ast::Attribute]) -> bool {
28
29
// Support conditional compilation by transforming the AST, stripping out
29
30
// any items that do not belong in the current configuration
30
31
pub fn strip_unconfigured_items ( diagnostic : & SpanHandler , krate : ast:: Crate ,
31
- feature_gated_cfgs : & mut Vec < GatedCfg > )
32
+ feature_gated_cfgs : & mut Vec < GatedCfgAttr > )
32
33
-> ast:: Crate
33
34
{
35
+ // Need to do this check here because cfg runs before feature_gates
36
+ check_for_gated_stmt_expr_attributes ( & krate, feature_gated_cfgs) ;
37
+
34
38
let krate = process_cfg_attr ( diagnostic, krate, feature_gated_cfgs) ;
35
39
let config = krate. config . clone ( ) ;
36
40
strip_items ( diagnostic,
37
41
krate,
38
- |attrs| in_cfg ( diagnostic, & config, attrs, feature_gated_cfgs) )
42
+ |attrs| {
43
+ let mut diag = CfgDiagReal {
44
+ diag : diagnostic,
45
+ feature_gated_cfgs : feature_gated_cfgs,
46
+ } ;
47
+ in_cfg ( & config, attrs, & mut diag)
48
+ } )
39
49
}
40
50
41
51
impl < ' a , F > fold:: Folder for Context < ' a , F > where F : FnMut ( & [ ast:: Attribute ] ) -> bool {
42
- fn fold_mod ( & mut self , module : ast:: Mod ) -> ast:: Mod {
43
- fold_mod ( self , module)
44
- }
45
52
fn fold_foreign_mod ( & mut self , foreign_mod : ast:: ForeignMod ) -> ast:: ForeignMod {
46
53
fold_foreign_mod ( self , foreign_mod)
47
54
}
@@ -87,19 +94,6 @@ pub fn strip_items<'a, F>(diagnostic: &'a SpanHandler,
87
94
ctxt. fold_crate ( krate)
88
95
}
89
96
90
- fn fold_mod < F > ( cx : & mut Context < F > ,
91
- ast:: Mod { inner, items} : ast:: Mod )
92
- -> ast:: Mod where
93
- F : FnMut ( & [ ast:: Attribute ] ) -> bool
94
- {
95
- ast:: Mod {
96
- inner : inner,
97
- items : items. into_iter ( ) . flat_map ( |a| {
98
- cx. fold_item ( a) . into_iter ( )
99
- } ) . collect ( )
100
- }
101
- }
102
-
103
97
fn filter_foreign_item < F > ( cx : & mut Context < F > ,
104
98
item : P < ast:: ForeignItem > )
105
99
-> Option < P < ast:: ForeignItem > > where
@@ -271,44 +265,45 @@ fn is_cfg(attr: &ast::Attribute) -> bool {
271
265
272
266
// Determine if an item should be translated in the current crate
273
267
// configuration based on the item's attributes
274
- fn in_cfg ( diagnostic : & SpanHandler ,
275
- cfg : & [ P < ast:: MetaItem > ] ,
276
- attrs : & [ ast:: Attribute ] ,
277
- feature_gated_cfgs : & mut Vec < GatedCfg > ) -> bool {
268
+ fn in_cfg < T : CfgDiag > ( cfg : & [ P < ast:: MetaItem > ] ,
269
+ attrs : & [ ast:: Attribute ] ,
270
+ diag : & mut T ) -> bool {
278
271
attrs. iter ( ) . all ( |attr| {
279
272
let mis = match attr. node . value . node {
280
273
ast:: MetaList ( _, ref mis) if is_cfg ( & attr) => mis,
281
274
_ => return true
282
275
} ;
283
276
284
277
if mis. len ( ) != 1 {
285
- diagnostic. span_err ( attr. span , "expected 1 cfg-pattern" ) ;
278
+ diag. emit_error ( |diagnostic| {
279
+ diagnostic. span_err ( attr. span , "expected 1 cfg-pattern" ) ;
280
+ } ) ;
286
281
return true ;
287
282
}
288
283
289
- attr:: cfg_matches ( diagnostic, cfg, & mis[ 0 ] ,
290
- feature_gated_cfgs)
284
+ attr:: cfg_matches ( cfg, & mis[ 0 ] , diag)
291
285
} )
292
286
}
293
287
294
- struct CfgAttrFolder < ' a , ' b > {
295
- diag : & ' a SpanHandler ,
296
- config : ast:: CrateConfig ,
297
- feature_gated_cfgs : & ' b mut Vec < GatedCfg >
288
+ struct CfgAttrFolder < ' a , T > {
289
+ diag : T ,
290
+ config : & ' a ast:: CrateConfig ,
298
291
}
299
292
300
293
// Process `#[cfg_attr]`.
301
294
fn process_cfg_attr ( diagnostic : & SpanHandler , krate : ast:: Crate ,
302
- feature_gated_cfgs : & mut Vec < GatedCfg > ) -> ast:: Crate {
295
+ feature_gated_cfgs : & mut Vec < GatedCfgAttr > ) -> ast:: Crate {
303
296
let mut fld = CfgAttrFolder {
304
- diag : diagnostic,
305
- config : krate. config . clone ( ) ,
306
- feature_gated_cfgs : feature_gated_cfgs,
297
+ diag : CfgDiagReal {
298
+ diag : diagnostic,
299
+ feature_gated_cfgs : feature_gated_cfgs,
300
+ } ,
301
+ config : & krate. config . clone ( ) ,
307
302
} ;
308
303
fld. fold_crate ( krate)
309
304
}
310
305
311
- impl < ' a , ' b > fold:: Folder for CfgAttrFolder < ' a , ' b > {
306
+ impl < ' a , T : CfgDiag > fold:: Folder for CfgAttrFolder < ' a , T > {
312
307
fn fold_attribute ( & mut self , attr : ast:: Attribute ) -> Option < ast:: Attribute > {
313
308
if !attr. check_name ( "cfg_attr" ) {
314
309
return fold:: noop_fold_attribute ( attr, self ) ;
@@ -317,20 +312,25 @@ impl<'a,'b> fold::Folder for CfgAttrFolder<'a,'b> {
317
312
let attr_list = match attr. meta_item_list ( ) {
318
313
Some ( attr_list) => attr_list,
319
314
None => {
320
- self . diag . span_err ( attr. span , "expected `#[cfg_attr(<cfg pattern>, <attr>)]`" ) ;
315
+ self . diag . emit_error ( |diag| {
316
+ diag. span_err ( attr. span ,
317
+ "expected `#[cfg_attr(<cfg pattern>, <attr>)]`" ) ;
318
+ } ) ;
321
319
return None ;
322
320
}
323
321
} ;
324
322
let ( cfg, mi) = match ( attr_list. len ( ) , attr_list. get ( 0 ) , attr_list. get ( 1 ) ) {
325
323
( 2 , Some ( cfg) , Some ( mi) ) => ( cfg, mi) ,
326
324
_ => {
327
- self . diag . span_err ( attr. span , "expected `#[cfg_attr(<cfg pattern>, <attr>)]`" ) ;
325
+ self . diag . emit_error ( |diag| {
326
+ diag. span_err ( attr. span ,
327
+ "expected `#[cfg_attr(<cfg pattern>, <attr>)]`" ) ;
328
+ } ) ;
328
329
return None ;
329
330
}
330
331
} ;
331
332
332
- if attr:: cfg_matches ( self . diag , & self . config [ ..] , & cfg,
333
- self . feature_gated_cfgs ) {
333
+ if attr:: cfg_matches ( & self . config [ ..] , & cfg, & mut self . diag ) {
334
334
Some ( respan ( mi. span , ast:: Attribute_ {
335
335
id : attr:: mk_attr_id ( ) ,
336
336
style : attr. node . style ,
@@ -347,3 +347,174 @@ impl<'a,'b> fold::Folder for CfgAttrFolder<'a,'b> {
347
347
fold:: noop_fold_mac ( mac, self )
348
348
}
349
349
}
350
+
351
+ fn check_for_gated_stmt_expr_attributes ( krate : & ast:: Crate ,
352
+ discovered : & mut Vec < GatedCfgAttr > ) {
353
+ let mut v = StmtExprAttrFeatureVisitor {
354
+ config : & krate. config ,
355
+ discovered : discovered,
356
+ } ;
357
+ visit:: walk_crate ( & mut v, krate) ;
358
+ }
359
+
360
+ /// To cover this feature, we need to discover all attributes
361
+ /// so we need to run before cfg.
362
+ struct StmtExprAttrFeatureVisitor < ' a , ' b > {
363
+ config : & ' a ast:: CrateConfig ,
364
+ discovered : & ' b mut Vec < GatedCfgAttr > ,
365
+ }
366
+
367
+ // Runs the cfg_attr and cfg folders locally in "silent" mode
368
+ // to discover attribute use on stmts or expressions ahead of time
369
+ impl < ' v , ' a , ' b > visit:: Visitor < ' v > for StmtExprAttrFeatureVisitor < ' a , ' b > {
370
+ fn visit_stmt ( & mut self , s : & ' v ast:: Stmt ) {
371
+ // check if there even are any attributes on this node
372
+ let stmt_attrs = s. node . attrs ( ) ;
373
+ if stmt_attrs. len ( ) > 0 {
374
+ // attributes on items are fine
375
+ if let ast:: StmtDecl ( ref decl, _) = s. node {
376
+ if let ast:: DeclItem ( _) = decl. node {
377
+ visit:: walk_stmt ( self , s) ;
378
+ return ;
379
+ }
380
+ }
381
+
382
+ // flag the offending attributes
383
+ for attr in stmt_attrs {
384
+ self . discovered . push ( GatedCfgAttr :: GatedAttr ( attr. span ) ) ;
385
+ }
386
+
387
+ // if the node does not end up being cfg-d away, walk down
388
+ if node_survives_cfg ( stmt_attrs, self . config ) {
389
+ visit:: walk_stmt ( self , s) ;
390
+ }
391
+ } else {
392
+ visit:: walk_stmt ( self , s) ;
393
+ }
394
+ }
395
+
396
+ fn visit_expr ( & mut self , ex : & ' v ast:: Expr ) {
397
+ // check if there even are any attributes on this node
398
+ let expr_attrs = ex. attrs ( ) ;
399
+ if expr_attrs. len ( ) > 0 {
400
+
401
+ // flag the offending attributes
402
+ for attr in expr_attrs {
403
+ self . discovered . push ( GatedCfgAttr :: GatedAttr ( attr. span ) ) ;
404
+ }
405
+
406
+ // if the node does not end up being cfg-d away, walk down
407
+ if node_survives_cfg ( expr_attrs, self . config ) {
408
+ visit:: walk_expr ( self , ex) ;
409
+ }
410
+ } else {
411
+ visit:: walk_expr ( self , ex) ;
412
+ }
413
+ }
414
+
415
+ fn visit_foreign_item ( & mut self , i : & ' v ast:: ForeignItem ) {
416
+ if node_survives_cfg ( & i. attrs , self . config ) {
417
+ visit:: walk_foreign_item ( self , i) ;
418
+ }
419
+ }
420
+
421
+ fn visit_item ( & mut self , i : & ' v ast:: Item ) {
422
+ if node_survives_cfg ( & i. attrs , self . config ) {
423
+ visit:: walk_item ( self , i) ;
424
+ }
425
+ }
426
+
427
+ fn visit_impl_item ( & mut self , ii : & ' v ast:: ImplItem ) {
428
+ if node_survives_cfg ( & ii. attrs , self . config ) {
429
+ visit:: walk_impl_item ( self , ii) ;
430
+ }
431
+ }
432
+
433
+ fn visit_trait_item ( & mut self , ti : & ' v ast:: TraitItem ) {
434
+ if node_survives_cfg ( & ti. attrs , self . config ) {
435
+ visit:: walk_trait_item ( self , ti) ;
436
+ }
437
+ }
438
+
439
+ fn visit_struct_field ( & mut self , s : & ' v ast:: StructField ) {
440
+ if node_survives_cfg ( & s. node . attrs , self . config ) {
441
+ visit:: walk_struct_field ( self , s) ;
442
+ }
443
+ }
444
+
445
+ fn visit_variant ( & mut self , v : & ' v ast:: Variant ,
446
+ g : & ' v ast:: Generics , item_id : ast:: NodeId ) {
447
+ if node_survives_cfg ( & v. node . attrs , self . config ) {
448
+ visit:: walk_variant ( self , v, g, item_id) ;
449
+ }
450
+ }
451
+
452
+ fn visit_arm ( & mut self , a : & ' v ast:: Arm ) {
453
+ if node_survives_cfg ( & a. attrs , self . config ) {
454
+ visit:: walk_arm ( self , a) ;
455
+ }
456
+ }
457
+
458
+ // This visitor runs pre expansion, so we need to prevent
459
+ // the default panic here
460
+ fn visit_mac ( & mut self , mac : & ' v ast:: Mac ) {
461
+ visit:: walk_mac ( self , mac)
462
+ }
463
+ }
464
+
465
+ pub trait CfgDiag {
466
+ fn emit_error < F > ( & mut self , f : F ) where F : FnMut ( & SpanHandler ) ;
467
+ fn flag_gated < F > ( & mut self , f : F ) where F : FnMut ( & mut Vec < GatedCfgAttr > ) ;
468
+ }
469
+
470
+ pub struct CfgDiagReal < ' a , ' b > {
471
+ pub diag : & ' a SpanHandler ,
472
+ pub feature_gated_cfgs : & ' b mut Vec < GatedCfgAttr > ,
473
+ }
474
+
475
+ impl < ' a , ' b > CfgDiag for CfgDiagReal < ' a , ' b > {
476
+ fn emit_error < F > ( & mut self , mut f : F ) where F : FnMut ( & SpanHandler ) {
477
+ f ( self . diag )
478
+ }
479
+ fn flag_gated < F > ( & mut self , mut f : F ) where F : FnMut ( & mut Vec < GatedCfgAttr > ) {
480
+ f ( self . feature_gated_cfgs )
481
+ }
482
+ }
483
+
484
+ struct CfgDiagSilent {
485
+ error : bool ,
486
+ }
487
+
488
+ impl CfgDiag for CfgDiagSilent {
489
+ fn emit_error < F > ( & mut self , _: F ) where F : FnMut ( & SpanHandler ) {
490
+ self . error = true ;
491
+ }
492
+ fn flag_gated < F > ( & mut self , _: F ) where F : FnMut ( & mut Vec < GatedCfgAttr > ) { }
493
+ }
494
+
495
+ fn node_survives_cfg ( attrs : & [ ast:: Attribute ] ,
496
+ config : & ast:: CrateConfig ) -> bool {
497
+ let mut survives_cfg = true ;
498
+
499
+ for attr in attrs {
500
+ let mut fld = CfgAttrFolder {
501
+ diag : CfgDiagSilent { error : false } ,
502
+ config : config,
503
+ } ;
504
+ let attr = fld. fold_attribute ( attr. clone ( ) ) ;
505
+
506
+ // In case of error we can just return true,
507
+ // since the actual cfg folders will end compilation anyway.
508
+
509
+ if fld. diag . error { return true ; }
510
+
511
+ survives_cfg &= attr. map ( |attr| {
512
+ let mut diag = CfgDiagSilent { error : false } ;
513
+ let r = in_cfg ( config, & [ attr] , & mut diag) ;
514
+ if diag. error { return true ; }
515
+ r
516
+ } ) . unwrap_or ( true )
517
+ }
518
+
519
+ survives_cfg
520
+ }
0 commit comments