1
1
// Code that generates a test runner to run all the tests in a crate
2
2
3
3
use rustc_ast as ast;
4
- use rustc_ast:: entry:: EntryPointType ;
5
4
use rustc_ast:: mut_visit:: { ExpectOne , * } ;
6
5
use rustc_ast:: ptr:: P ;
7
6
use rustc_ast:: { attr, ModKind } ;
8
7
use rustc_expand:: base:: { ExtCtxt , ResolverExpand } ;
9
8
use rustc_expand:: expand:: { AstFragment , ExpansionConfig } ;
10
9
use rustc_feature:: Features ;
10
+ use rustc_session:: config:: CrateType ;
11
11
use rustc_session:: Session ;
12
12
use rustc_span:: hygiene:: { AstPass , SyntaxContext , Transparency } ;
13
13
use rustc_span:: symbol:: { sym, Ident , Symbol } ;
@@ -88,8 +88,9 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> {
88
88
fn visit_crate ( & mut self , c : & mut ast:: Crate ) {
89
89
noop_visit_crate ( c, self ) ;
90
90
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) ;
93
94
}
94
95
95
96
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> {
124
125
Some ( parent) ,
125
126
) ;
126
127
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
128
129
// `apply_mark` directly.
129
130
test. ident . span = test. ident . span . apply_mark ( expn_id, Transparency :: Opaque ) ;
130
131
}
@@ -135,28 +136,32 @@ impl<'a> MutVisitor for TestHarnessGenerator<'a> {
135
136
}
136
137
}
137
138
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;
159
145
}
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
+ } )
160
165
}
161
166
/// A folder used to remove any entry points (like fn main) because the harness
162
167
/// generator will provide its own
@@ -172,33 +177,7 @@ impl<'a> MutVisitor for EntryPointCleaner<'a> {
172
177
self . depth += 1 ;
173
178
let item = noop_flat_map_item ( i, self ) . expect_one ( "noop did something" ) ;
174
179
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) ]
202
181
}
203
182
}
204
183
@@ -220,7 +199,7 @@ fn generate_test_harness(
220
199
let expn_id = ext_cx. resolver . expansion_for_ast_pass (
221
200
DUMMY_SP ,
222
201
AstPass :: TestHarness ,
223
- & [ sym:: main , sym:: test, sym:: rustc_attrs] ,
202
+ & [ sym:: rustc_main , sym:: test, sym:: rustc_attrs] ,
224
203
None ,
225
204
) ;
226
205
let def_site = DUMMY_SP . with_def_site_ctxt ( expn_id) ;
@@ -246,8 +225,8 @@ fn generate_test_harness(
246
225
///
247
226
/// By default this expands to
248
227
///
249
- /// ```
250
- /// #[ main]
228
+ /// ```rust,ignore (not real code)
229
+ /// #![rustc_main(crate::...:: main) ]
251
230
/// pub fn main() {
252
231
/// extern crate test;
253
232
/// test::test_main_static(&[
@@ -271,9 +250,9 @@ fn generate_test_harness(
271
250
/// [`TestCtxt::reexport_test_harness_main`] provides a different name for the `main`
272
251
/// function and [`TestCtxt::test_runner`] provides a path that replaces
273
252
/// `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 ) {
275
254
let sp = cx. def_site ;
276
- let ecx = & cx. ext_cx ;
255
+ let ecx = & mut cx. ext_cx ;
277
256
let test_id = Ident :: new ( sym:: test, sp) ;
278
257
279
258
let runner_name = match cx. panic_strategy {
@@ -290,17 +269,14 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
290
269
test_runner. span = sp;
291
270
292
271
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) ] ) ;
294
274
let call_test_main = ecx. stmt_expr ( call_test_main) ;
295
275
296
276
// extern crate test
297
277
let test_extern_stmt =
298
278
ecx. stmt_item ( sp, ecx. item ( sp, test_id, vec ! [ ] , ast:: ItemKind :: ExternCrate ( None ) ) ) ;
299
279
300
- // #[main]
301
- let main_meta = ecx. meta_word ( sp, sym:: main) ;
302
- let main_attr = ecx. attribute ( main_meta) ;
303
-
304
280
// pub fn main() { ... }
305
281
let main_ret_ty = ecx. ty ( sp, ast:: TyKind :: Tup ( vec ! [ ] ) ) ;
306
282
@@ -325,7 +301,7 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
325
301
326
302
let main = P ( ast:: Item {
327
303
ident : main_id,
328
- attrs : vec ! [ main_attr ] ,
304
+ attrs : vec ! [ ] ,
329
305
id : ast:: DUMMY_NODE_ID ,
330
306
kind : main,
331
307
vis : ast:: Visibility { span : sp, kind : ast:: VisibilityKind :: Public , tokens : None } ,
@@ -335,18 +311,48 @@ fn mk_main(cx: &mut TestCtxt<'_>) -> P<ast::Item> {
335
311
336
312
// Integrate the new item into existing module structures.
337
313
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) ;
339
322
}
340
323
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 ;
345
326
let ecx = & cx. ext_cx ;
346
327
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( ) ) ;
347
353
ecx. expr_vec_slice (
348
354
sp,
349
- cx . test_cases
355
+ test_cases
350
356
. iter ( )
351
357
. map ( |test| {
352
358
ecx. expr_addr_of ( test. span , ecx. expr_path ( ecx. path ( test. span , vec ! [ test. ident] ) ) )
0 commit comments