@@ -15,6 +15,7 @@ use codemap::{ExpnInfo, MacroBang, MacroAttribute, dummy_spanned, respan};
1515use config:: { is_test_or_bench, StripUnconfigured } ;
1616use errors:: { Applicability , FatalError } ;
1717use ext:: base:: * ;
18+ use ext:: build:: AstBuilder ;
1819use ext:: derive:: { add_derived_markers, collect_derives} ;
1920use ext:: hygiene:: { self , Mark , SyntaxContext } ;
2021use ext:: placeholders:: { placeholder, PlaceholderExpander } ;
@@ -474,6 +475,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
474475 cx : self . cx ,
475476 invocations : Vec :: new ( ) ,
476477 monotonic : self . monotonic ,
478+ tests_nameable : true ,
477479 } ;
478480 ( fragment. fold_with ( & mut collector) , collector. invocations )
479481 } ;
@@ -1049,6 +1051,11 @@ struct InvocationCollector<'a, 'b: 'a> {
10491051 cfg : StripUnconfigured < ' a > ,
10501052 invocations : Vec < Invocation > ,
10511053 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 ,
10521059}
10531060
10541061impl < ' a , ' b > InvocationCollector < ' a , ' b > {
@@ -1066,6 +1073,20 @@ impl<'a, 'b> InvocationCollector<'a, 'b> {
10661073 placeholder ( fragment_kind, NodeId :: placeholder_from_mark ( mark) )
10671074 }
10681075
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+
10691090 fn collect_bang ( & mut self , mac : ast:: Mac , span : Span , kind : AstFragmentKind ) -> AstFragment {
10701091 self . collect ( kind, InvocationKind :: Bang { mac : mac, ident : None , span : span } )
10711092 }
@@ -1306,7 +1327,7 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
13061327 }
13071328 ast:: ItemKind :: Mod ( ast:: Mod { inner, .. } ) => {
13081329 if item. ident == keywords:: Invalid . ident ( ) {
1309- return noop_fold_item ( item, self ) ;
1330+ return self . fold_nameable ( item) ;
13101331 }
13111332
13121333 let orig_directory_ownership = self . cx . current_expansion . directory_ownership ;
@@ -1346,22 +1367,58 @@ impl<'a, 'b> Folder for InvocationCollector<'a, 'b> {
13461367
13471368 let orig_module =
13481369 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) ;
13501371 self . cx . current_expansion . module = orig_module;
13511372 self . cx . current_expansion . directory_ownership = orig_directory_ownership;
13521373 result
13531374 }
13541375 // 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;
13551381 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
13571387 item = item. map ( |mut item| {
13581388 item. vis = respan ( item. vis . span , ast:: VisibilityKind :: Public ) ;
1389+ item. ident = item. ident . gensym ( ) ;
13591390 item
13601391 } ) ;
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)
13611419 }
1362- noop_fold_item ( item, self )
13631420 }
1364- _ => noop_fold_item ( item, self ) ,
1421+ _ => self . fold_unnameable ( item) ,
13651422 }
13661423 }
13671424
0 commit comments