@@ -32,6 +32,7 @@ use ext::build::AstBuilder;
32
32
use ext:: expand:: ExpansionConfig ;
33
33
use ext:: hygiene:: { Mark , SyntaxContext } ;
34
34
use fold:: Folder ;
35
+ use feature_gate:: Features ;
35
36
use util:: move_map:: MoveMap ;
36
37
use fold;
37
38
use parse:: { token, ParseSess } ;
@@ -63,6 +64,7 @@ struct TestCtxt<'a> {
63
64
reexport_test_harness_main : Option < Symbol > ,
64
65
is_libtest : bool ,
65
66
ctxt : SyntaxContext ,
67
+ features : & ' a Features ,
66
68
67
69
// top-level re-export submodule, filled out after folding is finished
68
70
toplevel_reexport : Option < Ident > ,
@@ -74,7 +76,8 @@ pub fn modify_for_testing(sess: &ParseSess,
74
76
resolver : & mut Resolver ,
75
77
should_test : bool ,
76
78
krate : ast:: Crate ,
77
- span_diagnostic : & errors:: Handler ) -> ast:: Crate {
79
+ span_diagnostic : & errors:: Handler ,
80
+ features : & Features ) -> ast:: Crate {
78
81
// Check for #[reexport_test_harness_main = "some_name"] which
79
82
// creates a `use some_name = __test::main;`. This needs to be
80
83
// unconditional, so that the attribute is still marked as used in
@@ -84,7 +87,8 @@ pub fn modify_for_testing(sess: &ParseSess,
84
87
"reexport_test_harness_main" ) ;
85
88
86
89
if should_test {
87
- generate_test_harness ( sess, resolver, reexport_test_harness_main, krate, span_diagnostic)
90
+ generate_test_harness ( sess, resolver, reexport_test_harness_main,
91
+ krate, span_diagnostic, features)
88
92
} else {
89
93
krate
90
94
}
@@ -265,23 +269,28 @@ fn generate_test_harness(sess: &ParseSess,
265
269
resolver : & mut Resolver ,
266
270
reexport_test_harness_main : Option < Symbol > ,
267
271
krate : ast:: Crate ,
268
- sd : & errors:: Handler ) -> ast:: Crate {
272
+ sd : & errors:: Handler ,
273
+ features : & Features ) -> ast:: Crate {
269
274
// Remove the entry points
270
275
let mut cleaner = EntryPointCleaner { depth : 0 } ;
271
276
let krate = cleaner. fold_crate ( krate) ;
272
277
273
278
let mark = Mark :: fresh ( Mark :: root ( ) ) ;
274
279
280
+ let mut econfig = ExpansionConfig :: default ( "test" . to_string ( ) ) ;
281
+ econfig. features = Some ( features) ;
282
+
275
283
let cx = TestCtxt {
276
284
span_diagnostic : sd,
277
- ext_cx : ExtCtxt :: new ( sess, ExpansionConfig :: default ( "test" . to_string ( ) ) , resolver) ,
285
+ ext_cx : ExtCtxt :: new ( sess, econfig , resolver) ,
278
286
path : Vec :: new ( ) ,
279
287
testfns : Vec :: new ( ) ,
280
288
reexport_test_harness_main,
281
289
// NB: doesn't consider the value of `--crate-name` passed on the command line.
282
290
is_libtest : attr:: find_crate_name ( & krate. attrs ) . map ( |s| s == "test" ) . unwrap_or ( false ) ,
283
291
toplevel_reexport : None ,
284
292
ctxt : SyntaxContext :: empty ( ) . apply_mark ( mark) ,
293
+ features,
285
294
} ;
286
295
287
296
mark. set_expn_info ( ExpnInfo {
@@ -318,71 +327,105 @@ enum HasTestSignature {
318
327
fn is_test_fn ( cx : & TestCtxt , i : & ast:: Item ) -> bool {
319
328
let has_test_attr = attr:: contains_name ( & i. attrs , "test" ) ;
320
329
321
- fn has_test_signature ( i : & ast:: Item ) -> HasTestSignature {
330
+ fn has_test_signature ( cx : & TestCtxt , i : & ast:: Item ) -> HasTestSignature {
322
331
match i. node {
323
- ast:: ItemKind :: Fn ( ref decl, _, _, _, ref generics, _) => {
324
- let no_output = match decl. output {
325
- ast:: FunctionRetTy :: Default ( ..) => true ,
326
- ast:: FunctionRetTy :: Ty ( ref t) if t. node == ast:: TyKind :: Tup ( vec ! [ ] ) => true ,
327
- _ => false
328
- } ;
329
- if decl. inputs . is_empty ( )
330
- && no_output
331
- && !generics. is_parameterized ( ) {
332
- Yes
333
- } else {
334
- No
332
+ ast:: ItemKind :: Fn ( ref decl, _, _, _, ref generics, _) => {
333
+ // If the termination trait is active, the compiler will check that the output
334
+ // type implements the `Termination` trait as `libtest` enforces that.
335
+ let output_matches = if cx. features . termination_trait {
336
+ true
337
+ } else {
338
+ let no_output = match decl. output {
339
+ ast:: FunctionRetTy :: Default ( ..) => true ,
340
+ ast:: FunctionRetTy :: Ty ( ref t) if t. node == ast:: TyKind :: Tup ( vec ! [ ] ) => true ,
341
+ _ => false
342
+ } ;
343
+
344
+ no_output && !generics. is_parameterized ( )
345
+ } ;
346
+
347
+ if decl. inputs . is_empty ( ) && output_matches {
348
+ Yes
349
+ } else {
350
+ No
351
+ }
335
352
}
336
- }
337
- _ => NotEvenAFunction ,
353
+ _ => NotEvenAFunction ,
338
354
}
339
355
}
340
356
341
- if has_test_attr {
357
+ let has_test_signature = if has_test_attr {
342
358
let diag = cx. span_diagnostic ;
343
- match has_test_signature ( i) {
344
- Yes => { } ,
345
- No => diag. span_err ( i. span , "functions used as tests must have signature fn() -> ()" ) ,
346
- NotEvenAFunction => diag. span_err ( i. span ,
347
- "only functions may be used as tests" ) ,
359
+ match has_test_signature ( cx, i) {
360
+ Yes => true ,
361
+ No => {
362
+ if cx. features . termination_trait {
363
+ diag. span_err ( i. span , "functions used as tests can not have any arguments" ) ;
364
+ } else {
365
+ diag. span_err ( i. span , "functions used as tests must have signature fn() -> ()" ) ;
366
+ }
367
+ false
368
+ } ,
369
+ NotEvenAFunction => {
370
+ diag. span_err ( i. span , "only functions may be used as tests" ) ;
371
+ false
372
+ } ,
348
373
}
349
- }
374
+ } else {
375
+ false
376
+ } ;
350
377
351
- has_test_attr && has_test_signature ( i ) == Yes
378
+ has_test_attr && has_test_signature
352
379
}
353
380
354
381
fn is_bench_fn ( cx : & TestCtxt , i : & ast:: Item ) -> bool {
355
382
let has_bench_attr = attr:: contains_name ( & i. attrs , "bench" ) ;
356
383
357
- fn has_test_signature ( i : & ast:: Item ) -> bool {
384
+ fn has_bench_signature ( cx : & TestCtxt , i : & ast:: Item ) -> bool {
358
385
match i. node {
359
386
ast:: ItemKind :: Fn ( ref decl, _, _, _, ref generics, _) => {
360
387
let input_cnt = decl. inputs . len ( ) ;
361
- let no_output = match decl. output {
362
- ast:: FunctionRetTy :: Default ( ..) => true ,
363
- ast:: FunctionRetTy :: Ty ( ref t) if t. node == ast:: TyKind :: Tup ( vec ! [ ] ) => true ,
364
- _ => false
388
+
389
+ // If the termination trait is active, the compiler will check that the output
390
+ // type implements the `Termination` trait as `libtest` enforces that.
391
+ let output_matches = if cx. features . termination_trait {
392
+ true
393
+ } else {
394
+ let no_output = match decl. output {
395
+ ast:: FunctionRetTy :: Default ( ..) => true ,
396
+ ast:: FunctionRetTy :: Ty ( ref t) if t. node == ast:: TyKind :: Tup ( vec ! [ ] ) => true ,
397
+ _ => false
398
+ } ;
399
+ let tparm_cnt = generics. params . iter ( )
400
+ . filter ( |param| param. is_type_param ( ) )
401
+ . count ( ) ;
402
+
403
+ no_output && tparm_cnt == 0
365
404
} ;
366
- let tparm_cnt = generics. params . iter ( )
367
- . filter ( |param| param. is_type_param ( ) )
368
- . count ( ) ;
369
405
370
406
// NB: inadequate check, but we're running
371
407
// well before resolve, can't get too deep.
372
- input_cnt == 1
373
- && no_output && tparm_cnt == 0
408
+ input_cnt == 1 && output_matches
374
409
}
375
410
_ => false
376
411
}
377
412
}
378
413
379
- if has_bench_attr && !has_test_signature ( i) {
414
+ let has_bench_signature = has_bench_signature ( cx, i) ;
415
+
416
+ if has_bench_attr && !has_bench_signature {
380
417
let diag = cx. span_diagnostic ;
381
- diag. span_err ( i. span , "functions used as benches must have signature \
382
- `fn(&mut Bencher) -> ()`") ;
418
+
419
+ if cx. features . termination_trait {
420
+ diag. span_err ( i. span , "functions used as benches must have signature \
421
+ `fn(&mut Bencher) -> impl Termination`") ;
422
+ } else {
423
+ diag. span_err ( i. span , "functions used as benches must have signature \
424
+ `fn(&mut Bencher) -> ()`") ;
425
+ }
383
426
}
384
427
385
- has_bench_attr && has_test_signature ( i )
428
+ has_bench_attr && has_bench_signature
386
429
}
387
430
388
431
fn is_ignored ( i : & ast:: Item ) -> bool {
@@ -700,9 +743,52 @@ fn mk_test_desc_and_fn_rec(cx: &TestCtxt, test: &Test) -> P<ast::Expr> {
700
743
} ;
701
744
visible_path. extend ( path) ;
702
745
703
- let fn_expr = ecx. expr_path ( ecx. path_global ( span, visible_path) ) ;
746
+ // If termination feature is enabled, create a wrapper that invokes the fn
747
+ // like this:
748
+ //
749
+ // fn wrapper() {
750
+ // assert_eq!(0, real_function().report());
751
+ // }
752
+ //
753
+ // and then put a reference to `wrapper` into the test descriptor. Otherwise,
754
+ // just put a direct reference to `real_function`.
755
+ let fn_expr = {
756
+ let base_fn_expr = ecx. expr_path ( ecx. path_global ( span, visible_path) ) ;
757
+ if cx. features . termination_trait {
758
+ // ::std::Termination::assert_unit_test_successful
759
+ let assert_unit_test_successful = ecx. path_global (
760
+ span,
761
+ vec ! [
762
+ ecx. ident_of( "std" ) ,
763
+ ecx. ident_of( "Termination" ) ,
764
+ ecx. ident_of( "assert_unit_test_successful" ) ,
765
+ ] ,
766
+ ) ;
767
+ // || {..}
768
+ ecx. lambda (
769
+ span,
770
+ vec ! [ ] ,
771
+ // ::std::Termination::assert_unit_test_successful(..)
772
+ ecx. expr_call (
773
+ span,
774
+ ecx. expr_path ( assert_unit_test_successful) ,
775
+ vec ! [
776
+ // $base_fn_expr()
777
+ ecx. expr_call(
778
+ span,
779
+ base_fn_expr,
780
+ vec![ ] ,
781
+ )
782
+ ] ,
783
+ ) ,
784
+ )
785
+ } else {
786
+ base_fn_expr
787
+ }
788
+ } ;
704
789
705
790
let variant_name = if test. bench { "StaticBenchFn" } else { "StaticTestFn" } ;
791
+
706
792
// self::test::$variant_name($fn_expr)
707
793
let testfn_expr = ecx. expr_call ( span, ecx. expr_path ( test_path ( variant_name) ) , vec ! [ fn_expr] ) ;
708
794
0 commit comments