@@ -15,6 +15,7 @@ use codemap::{ExpnInfo, MacroBang, MacroAttribute, dummy_spanned, respan};
15
15
use config:: { is_test_or_bench, StripUnconfigured } ;
16
16
use errors:: { Applicability , FatalError } ;
17
17
use ext:: base:: * ;
18
+ use ext:: build:: AstBuilder ;
18
19
use ext:: derive:: { add_derived_markers, collect_derives} ;
19
20
use ext:: hygiene:: { self , Mark , SyntaxContext } ;
20
21
use ext:: placeholders:: { placeholder, PlaceholderExpander } ;
@@ -474,6 +475,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
474
475
cx : self . cx ,
475
476
invocations : Vec :: new ( ) ,
476
477
monotonic : self . monotonic ,
478
+ tests_nameable : true ,
477
479
} ;
478
480
( fragment. fold_with ( & mut collector) , collector. invocations )
479
481
} ;
@@ -1049,6 +1051,11 @@ struct InvocationCollector<'a, 'b: 'a> {
1049
1051
cfg : StripUnconfigured < ' a > ,
1050
1052
invocations : Vec < Invocation > ,
1051
1053
monotonic : bool ,
1054
+
1055
+ /// Test functions need to be nameable. Tests inside functions or in other
1056
+ /// unnameable locations need to be ignored. `tests_nameable` tracks whether
1057
+ /// any test functions found in the current context would be nameable.
1058
+ tests_nameable : bool ,
1052
1059
}
1053
1060
1054
1061
impl < ' a , ' b > InvocationCollector < ' a , ' b > {
@@ -1066,6 +1073,20 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
1066
1073
placeholder ( fragment_kind, NodeId :: placeholder_from_mark ( mark) )
1067
1074
}
1068
1075
1076
+ /// Folds the item allowing tests to be expanded because they are still nameable.
1077
+ /// This should probably only be called with module items
1078
+ fn fold_nameable ( & mut self , item : P < ast:: Item > ) -> SmallVector < P < ast:: Item > > {
1079
+ fold:: noop_fold_item ( item, self )
1080
+ }
1081
+
1082
+ /// Folds the item but doesn't allow tests to occur within it
1083
+ fn fold_unnameable ( & mut self , item : P < ast:: Item > ) -> SmallVector < P < ast:: Item > > {
1084
+ let was_nameable = mem:: replace ( & mut self . tests_nameable , false ) ;
1085
+ let items = fold:: noop_fold_item ( item, self ) ;
1086
+ self . tests_nameable = was_nameable;
1087
+ items
1088
+ }
1089
+
1069
1090
fn collect_bang ( & mut self , mac : ast:: Mac , span : Span , kind : AstFragmentKind ) -> AstFragment {
1070
1091
self . collect ( kind, InvocationKind :: Bang { mac : mac, ident : None , span : span } )
1071
1092
}
@@ -1306,7 +1327,7 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
1306
1327
}
1307
1328
ast:: ItemKind :: Mod ( ast:: Mod { inner, .. } ) => {
1308
1329
if item. ident == keywords:: Invalid . ident ( ) {
1309
- return noop_fold_item ( item, self ) ;
1330
+ return self . fold_nameable ( item) ;
1310
1331
}
1311
1332
1312
1333
let orig_directory_ownership = self . cx . current_expansion . directory_ownership ;
@@ -1346,22 +1367,58 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
1346
1367
1347
1368
let orig_module =
1348
1369
mem:: replace ( & mut self . cx . current_expansion . module , Rc :: new ( module) ) ;
1349
- let result = noop_fold_item ( item, self ) ;
1370
+ let result = self . fold_nameable ( item) ;
1350
1371
self . cx . current_expansion . module = orig_module;
1351
1372
self . cx . current_expansion . directory_ownership = orig_directory_ownership;
1352
1373
result
1353
1374
}
1354
1375
// Ensure that test functions are accessible from the test harness.
1376
+ // #[test] fn foo() {}
1377
+ // becomes:
1378
+ // #[test] pub fn foo_gensym(){}
1379
+ // #[allow(unused)]
1380
+ // use foo_gensym as foo;
1355
1381
ast:: ItemKind :: Fn ( ..) if self . cx . ecfg . should_test => {
1356
- if item. attrs . iter ( ) . any ( |attr| is_test_or_bench ( attr) ) {
1382
+ if self . tests_nameable && item. attrs . iter ( ) . any ( |attr| is_test_or_bench ( attr) ) {
1383
+ let orig_ident = item. ident ;
1384
+ let orig_vis = item. vis . clone ( ) ;
1385
+
1386
+ // Publicize the item under gensymed name to avoid pollution
1357
1387
item = item. map ( |mut item| {
1358
1388
item. vis = respan ( item. vis . span , ast:: VisibilityKind :: Public ) ;
1389
+ item. ident = item. ident . gensym ( ) ;
1359
1390
item
1360
1391
} ) ;
1392
+
1393
+ // Use the gensymed name under the item's original visibility
1394
+ let mut use_item = self . cx . item_use_simple_ (
1395
+ item. ident . span ,
1396
+ orig_vis,
1397
+ Some ( orig_ident) ,
1398
+ self . cx . path ( item. ident . span ,
1399
+ vec ! [ keywords:: SelfValue . ident( ) , item. ident] ) ) ;
1400
+
1401
+ // #[allow(unused)] because the test function probably isn't being referenced
1402
+ use_item = use_item. map ( |mut ui| {
1403
+ ui. attrs . push (
1404
+ self . cx . attribute ( DUMMY_SP , attr:: mk_list_item ( DUMMY_SP ,
1405
+ Ident :: from_str ( "allow" ) , vec ! [
1406
+ attr:: mk_nested_word_item( Ident :: from_str( "unused" ) )
1407
+ ]
1408
+ ) )
1409
+ ) ;
1410
+
1411
+ ui
1412
+ } ) ;
1413
+
1414
+ SmallVector :: many (
1415
+ self . fold_unnameable ( item) . into_iter ( )
1416
+ . chain ( self . fold_unnameable ( use_item) ) )
1417
+ } else {
1418
+ self . fold_unnameable ( item)
1361
1419
}
1362
- noop_fold_item ( item, self )
1363
1420
}
1364
- _ => noop_fold_item ( item, self ) ,
1421
+ _ => self . fold_unnameable ( item) ,
1365
1422
}
1366
1423
}
1367
1424
0 commit comments