11// Code that generates a test runner to run all the tests in a crate
22
33use rustc_ast as ast;
4- use rustc_ast:: entry:: EntryPointType ;
54use rustc_ast:: mut_visit:: { ExpectOne , * } ;
65use rustc_ast:: ptr:: P ;
76use rustc_ast:: { attr, ModKind } ;
87use rustc_expand:: base:: { ExtCtxt , ResolverExpand } ;
98use rustc_expand:: expand:: { AstFragment , ExpansionConfig } ;
109use rustc_feature:: Features ;
10+ use rustc_session:: config:: CrateType ;
1111use rustc_session:: Session ;
1212use rustc_span:: hygiene:: { AstPass , SyntaxContext , Transparency } ;
1313use rustc_span:: symbol:: { sym, Ident , Symbol } ;
@@ -88,8 +88,9 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> {
8888 fn visit_crate ( & mut self , c : & mut ast:: Crate ) {
8989 noop_visit_crate ( c, self ) ;
9090
91- // Create a main function to run our tests
92- c. items . push ( mk_main ( & mut self . cx ) ) ;
91+ ensure_exist_rustc_main_attr ( & mut self . cx , & mut c. attrs ) ;
92+ // Create an entry function to run our tests
93+ mk_entry_fn ( & mut self . cx , c) ;
9394 }
9495
9596 fn flat_map_item ( & mut self , i : P < ast:: Item > ) -> SmallVec < [ P < ast:: Item > ; 1 ] > {
@@ -124,7 +125,7 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> {
124125 Some ( parent) ,
125126 ) ;
126127 for test in & mut tests {
127- // See the comment on `mk_main ` for why we're using
128+ // See the comment on `mk_entry_fn ` for why we're using
128129 // `apply_mark` directly.
129130 test. ident . span = test. ident . span . apply_mark ( expn_id, Transparency :: Opaque ) ;
130131 }
@@ -135,28 +136,32 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> {
135136 }
136137}
137138
138- // Beware, this is duplicated in librustc_passes/entry.rs (with
139- // `rustc_hir::Item`), so make sure to keep them in sync.
140- fn entry_point_type ( sess : & Session , item : & ast:: Item , depth : usize ) -> EntryPointType {
141- match item. kind {
142- ast:: ItemKind :: Fn ( ..) => {
143- if sess. contains_name ( & item. attrs , sym:: start) {
144- EntryPointType :: Start
145- } else if sess. contains_name ( & item. attrs , sym:: main) {
146- EntryPointType :: MainAttr
147- } else if item. ident . name == sym:: main {
148- if depth == 1 {
149- // This is a top-level function so can be 'main'
150- EntryPointType :: MainNamed
151- } else {
152- EntryPointType :: OtherMain
153- }
154- } else {
155- EntryPointType :: None
156- }
157- }
158- _ => EntryPointType :: None ,
139+ /// Remove any #[start] from the AST so it doesn't
140+ /// clash with the one we're going to add, but mark it as
141+ /// #[allow(dead_code)] to avoid printing warnings.
142+ fn strip_start_attr ( sess : & Session , def_site : Span , item : P < ast:: Item > ) -> P < ast:: Item > {
143+ if !matches ! ( item. kind, ast:: ItemKind :: Fn ( ..) ) {
144+ return item;
159145 }
146+ if !sess. contains_name ( & item. attrs , sym:: start) {
147+ return item;
148+ }
149+
150+ item. map ( |item| {
151+ let ast:: Item { id, ident, attrs, kind, vis, span, tokens } = item;
152+
153+ let allow_ident = Ident :: new ( sym:: allow, def_site) ;
154+ let dc_nested = attr:: mk_nested_word_item ( Ident :: new ( sym:: dead_code, def_site) ) ;
155+ let allow_dead_code_item = attr:: mk_list_item ( allow_ident, vec ! [ dc_nested] ) ;
156+ let allow_dead_code = attr:: mk_attr_outer ( allow_dead_code_item) ;
157+ let attrs = attrs
158+ . into_iter ( )
159+ . filter ( |attr| !sess. check_name ( attr, sym:: start) )
160+ . chain ( iter:: once ( allow_dead_code) )
161+ . collect ( ) ;
162+
163+ ast:: Item { id, ident, attrs, kind, vis, span, tokens }
164+ } )
160165}
161166/// A folder used to remove any entry points (like fn main) because the harness
162167/// generator will provide its own
@@ -172,33 +177,7 @@ impl<'a> MutVisitor for EntryPointCleaner<'a> {
172177 self . depth += 1 ;
173178 let item = noop_flat_map_item ( i, self ) . expect_one ( "noop did something" ) ;
174179 self . depth -= 1 ;
175-
176- // Remove any #[main] or #[start] from the AST so it doesn't
177- // clash with the one we're going to add, but mark it as
178- // #[allow(dead_code)] to avoid printing warnings.
179- let item = match entry_point_type ( self . sess , & item, self . depth ) {
180- EntryPointType :: MainNamed | EntryPointType :: MainAttr | EntryPointType :: Start => item
181- . map ( |ast:: Item { id, ident, attrs, kind, vis, span, tokens } | {
182- let allow_ident = Ident :: new ( sym:: allow, self . def_site ) ;
183- let dc_nested =
184- attr:: mk_nested_word_item ( Ident :: new ( sym:: dead_code, self . def_site ) ) ;
185- let allow_dead_code_item = attr:: mk_list_item ( allow_ident, vec ! [ dc_nested] ) ;
186- let allow_dead_code = attr:: mk_attr_outer ( allow_dead_code_item) ;
187- let attrs = attrs
188- . into_iter ( )
189- . filter ( |attr| {
190- !self . sess . check_name ( attr, sym:: main)
191- && !self . sess . check_name ( attr, sym:: start)
192- } )
193- . chain ( iter:: once ( allow_dead_code) )
194- . collect ( ) ;
195-
196- ast:: Item { id, ident, attrs, kind, vis, span, tokens }
197- } ) ,
198- EntryPointType :: None | EntryPointType :: OtherMain => item,
199- } ;
200-
201- smallvec ! [ item]
180+ smallvec ! [ strip_start_attr( self . sess, self . def_site, item) ]
202181 }
203182}
204183
@@ -220,7 +199,7 @@ fn generate_test_harness(
220199 let expn_id = ext_cx. resolver . expansion_for_ast_pass (
221200 DUMMY_SP ,
222201 AstPass :: TestHarness ,
223- & [ sym:: main , sym:: test, sym:: rustc_attrs] ,
202+ & [ sym:: rustc_main , sym:: test, sym:: rustc_attrs] ,
224203 None ,
225204 ) ;
226205 let def_site = DUMMY_SP . with_def_site_ctxt ( expn_id) ;
@@ -246,8 +225,8 @@ fn generate_test_harness(
246225///
247226/// By default this expands to
248227///
249- /// ```
250- /// #[ main]
228+ /// ```rust,ignore (not real code)
229+ /// #![rustc_main(crate::...:: main) ]
251230/// pub fn main() {
252231/// extern crate test;
253232/// test::test_main_static(&[
@@ -271,9 +250,9 @@ fn generate_test_harness(
271250/// [`TestCtxt::reexport_test_harness_main`] provides a different name for the `main`
272251/// function and [`TestCtxt::test_runner`] provides a path that replaces
273252/// `test::test_main_static`.
274- fn mk_main ( cx : & mut TestCtxt < ' _ > ) -> P < ast:: Item > {
253+ fn mk_entry_fn ( cx : & mut TestCtxt < ' _ > , c : & mut ast:: Crate ) {
275254 let sp = cx. def_site ;
276- let ecx = & cx. ext_cx ;
255+ let ecx = & mut cx. ext_cx ;
277256 let test_id = Ident :: new ( sym:: test, sp) ;
278257
279258 let runner_name = match cx. panic_strategy {
@@ -290,17 +269,14 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
290269 test_runner. span = sp;
291270
292271 let test_main_path_expr = ecx. expr_path ( test_runner) ;
293- let call_test_main = ecx. expr_call ( sp, test_main_path_expr, vec ! [ mk_tests_slice( cx, sp) ] ) ;
272+ let call_test_main =
273+ ecx. expr_call ( sp, test_main_path_expr, vec ! [ mk_tests_slice( ecx, & cx. test_cases, sp) ] ) ;
294274 let call_test_main = ecx. stmt_expr ( call_test_main) ;
295275
296276 // extern crate test
297277 let test_extern_stmt =
298278 ecx. stmt_item ( sp, ecx. item ( sp, test_id, vec ! [ ] , ast:: ItemKind :: ExternCrate ( None ) ) ) ;
299279
300- // #[main]
301- let main_meta = ecx. meta_word ( sp, sym:: main) ;
302- let main_attr = ecx. attribute ( main_meta) ;
303-
304280 // pub fn main() { ... }
305281 let main_ret_ty = ecx. ty ( sp, ast:: TyKind :: Tup ( vec ! [ ] ) ) ;
306282
@@ -325,7 +301,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
325301
326302 let main = P ( ast:: Item {
327303 ident : main_id,
328- attrs : vec ! [ main_attr ] ,
304+ attrs : vec ! [ ] ,
329305 id : ast:: DUMMY_NODE_ID ,
330306 kind : main,
331307 vis : ast:: Visibility { span : sp, kind : ast:: VisibilityKind :: Public , tokens : None } ,
@@ -335,18 +311,48 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
335311
336312 // Integrate the new item into existing module structures.
337313 let main = AstFragment :: Items ( smallvec ! [ main] ) ;
338- cx. ext_cx . monotonic_expander ( ) . fully_expand_fragment ( main) . make_items ( ) . pop ( ) . unwrap ( )
314+ let main = ecx. monotonic_expander ( ) . fully_expand_fragment ( main) . make_items ( ) . pop ( ) . unwrap ( ) ;
315+
316+ // #[rustc_main] attr
317+ let main_id_nested_meta = ast:: attr:: mk_nested_word_item ( main_id) ;
318+ let rustc_main_meta = ecx. meta_list ( sp, sym:: rustc_main, vec ! [ main_id_nested_meta] ) ;
319+ let rustc_main_attr = ecx. attribute ( rustc_main_meta) ;
320+ c. attrs . push ( rustc_main_attr) ;
321+ c. items . push ( main) ;
339322}
340323
341- /// Creates a slice containing every test like so:
342- /// &[&test1, &test2]
343- fn mk_tests_slice ( cx : & TestCtxt < ' _ > , sp : Span ) -> P < ast:: Expr > {
344- debug ! ( "building test vector from {} tests" , cx. test_cases. len( ) ) ;
324+ fn ensure_exist_rustc_main_attr ( cx : & mut TestCtxt < ' _ > , attrs : & mut Vec < ast:: Attribute > ) {
325+ let sp = cx. def_site ;
345326 let ecx = & cx. ext_cx ;
346327
328+ if ecx. sess . contains_name ( attrs, sym:: rustc_main) {
329+ return ;
330+ }
331+
332+ let any_exe = ecx. sess . crate_types ( ) . iter ( ) . any ( |ty| * ty == CrateType :: Executable ) ;
333+ if !any_exe {
334+ return ;
335+ }
336+
337+ if ecx. sess . contains_name ( attrs, sym:: no_main) {
338+ return ;
339+ }
340+
341+ let crate_main_nested_meta = ast:: attr:: mk_nested_word_item ( Ident :: new ( sym:: main, DUMMY_SP ) ) ;
342+ let ignore_nested_meta = ast:: attr:: mk_nested_word_item ( Ident :: new ( sym:: ignore, DUMMY_SP ) ) ;
343+ let rustc_main_meta =
344+ ecx. meta_list ( sp, sym:: rustc_main, vec ! [ crate_main_nested_meta, ignore_nested_meta] ) ;
345+ let rustc_main_attr = ecx. attribute ( rustc_main_meta) ;
346+ attrs. push ( rustc_main_attr) ;
347+ }
348+
349+ /// Creates a slice containing every test like so:
350+ /// &[&test1, &test2]
351+ fn mk_tests_slice ( ecx : & ExtCtxt < ' _ > , test_cases : & Vec < Test > , sp : Span ) -> P < ast:: Expr > {
352+ debug ! ( "building test vector from {} tests" , test_cases. len( ) ) ;
347353 ecx. expr_vec_slice (
348354 sp,
349- cx . test_cases
355+ test_cases
350356 . iter ( )
351357 . map ( |test| {
352358 ecx. expr_addr_of ( test. span , ecx. expr_path ( ecx. path ( test. span , vec ! [ test. ident] ) ) )
0 commit comments