2018-07-20 18:04:02 -07:00
/// The expansion from a test function to the appropriate test struct for libtest
/// Ideally, this code would be in libtest but for efficiency and error messages it lives here.
2021-11-24 10:20:23 +01:00
use crate ::util ::{ check_builtin_macro_attribute , warn_on_duplicate_attribute } ;
2020-04-27 23:26:11 +05:30
use rustc_ast as ast ;
2020-11-22 19:32:39 -05:00
use rustc_ast ::ptr ::P ;
2020-01-11 17:02:46 +01:00
use rustc_ast_pretty ::pprust ;
2022-01-15 04:16:13 -05:00
use rustc_errors ::Applicability ;
2019-12-29 17:23:55 +03:00
use rustc_expand ::base ::* ;
2020-07-30 11:27:50 +10:00
use rustc_session ::Session ;
2020-04-19 13:00:18 +02:00
use rustc_span ::symbol ::{ sym , Ident , Symbol } ;
2019-12-31 20:15:40 +03:00
use rustc_span ::Span ;
2018-07-20 18:04:02 -07:00
use std ::iter ;
2022-08-17 14:22:30 +10:00
use thin_vec ::thin_vec ;
2018-07-20 18:04:02 -07:00
2022-11-27 11:15:06 +00:00
/// #[test_case] is used by custom test authors to mark tests
/// When building for test, it needs to make the item public and gensym the name
/// Otherwise, we'll omit the item. This behavior means that any item annotated
/// with #[test_case] is never addressable.
///
/// We mark item with an inert attribute "rustc_test_marker" which the test generation
/// logic will pick up on.
2019-07-18 21:29:15 +03:00
pub fn expand_test_case (
ecx : & mut ExtCtxt < '_ > ,
attr_sp : Span ,
meta_item : & ast ::MetaItem ,
2019-12-24 17:38:22 -05:00
anno_item : Annotatable ,
2019-07-18 21:29:15 +03:00
) -> Vec < Annotatable > {
check_builtin_macro_attribute ( ecx , meta_item , sym ::test_case ) ;
2021-11-24 10:20:23 +01:00
warn_on_duplicate_attribute ( & ecx , & anno_item , sym ::test_case ) ;
2019-07-18 21:29:15 +03:00
2019-12-24 17:38:22 -05:00
if ! ecx . ecfg . should_test {
return vec! [ ] ;
}
2019-07-18 21:29:15 +03:00
2019-08-25 21:03:24 +01:00
let sp = ecx . with_def_site_ctxt ( attr_sp ) ;
2019-07-18 21:29:15 +03:00
let mut item = anno_item . expect_item ( ) ;
item = item . map ( | mut item | {
2022-07-26 17:40:48 -04:00
let test_path_symbol = Symbol ::intern ( & item_path (
// skip the name of the root module
& ecx . current_expansion . module . mod_path [ 1 .. ] ,
& item . ident ,
) ) ;
2020-08-21 19:11:00 -04:00
item . vis = ast ::Visibility {
span : item . vis . span ,
kind : ast ::VisibilityKind ::Public ,
tokens : None ,
} ;
2019-08-25 21:03:24 +01:00
item . ident . span = item . ident . span . with_ctxt ( sp . ctxt ( ) ) ;
Avoid more `MetaItem`-to-`Attribute` conversions.
There is code for converting `Attribute` (syntactic) to `MetaItem`
(semantic). There is also code for the reverse direction. The reverse
direction isn't really necessary; it's currently only used when
generating attributes, e.g. in `derive` code.
This commit adds some new functions for creating `Attributes`s directly,
without involving `MetaItem`s: `mk_attr_word`, `mk_attr_name_value_str`,
`mk_attr_nested_word`, and
`ExtCtxt::attr_{word,name_value_str,nested_word}`.
These new methods replace the old functions for creating `Attribute`s:
`mk_attr_inner`, `mk_attr_outer`, and `ExtCtxt::attribute`. Those
functions took `MetaItem`s as input, and relied on many other functions
that created `MetaItems`, which are also removed: `mk_name_value_item`,
`mk_list_item`, `mk_word_item`, `mk_nested_word_item`,
`{MetaItem,MetaItemKind,NestedMetaItem}::token_trees`,
`MetaItemKind::attr_args`, `MetaItemLit::{from_lit_kind,to_token}`,
`ExtCtxt::meta_word`.
Overall this cuts more than 100 lines of code and makes thing simpler.
2022-11-29 18:43:44 +11:00
item . attrs . push ( ecx . attr_name_value_str ( sym ::rustc_test_marker , test_path_symbol , sp ) ) ;
2019-07-18 21:29:15 +03:00
item
} ) ;
2019-12-24 17:38:22 -05:00
return vec! [ Annotatable ::Item ( item ) ] ;
2019-07-18 21:29:15 +03:00
}
2018-07-20 18:04:02 -07:00
pub fn expand_test (
2019-02-04 21:49:54 +09:00
cx : & mut ExtCtxt < '_ > ,
2018-07-20 18:04:02 -07:00
attr_sp : Span ,
2019-07-19 02:10:36 +03:00
meta_item : & ast ::MetaItem ,
2018-07-20 18:04:02 -07:00
item : Annotatable ,
) -> Vec < Annotatable > {
2019-07-19 02:10:36 +03:00
check_builtin_macro_attribute ( cx , meta_item , sym ::test ) ;
2021-11-24 10:20:23 +01:00
warn_on_duplicate_attribute ( & cx , & item , sym ::test ) ;
2018-07-20 18:04:02 -07:00
expand_test_or_bench ( cx , attr_sp , item , false )
}
pub fn expand_bench (
2019-02-04 21:49:54 +09:00
cx : & mut ExtCtxt < '_ > ,
2018-07-20 18:04:02 -07:00
attr_sp : Span ,
2019-07-19 02:10:36 +03:00
meta_item : & ast ::MetaItem ,
2018-07-20 18:04:02 -07:00
item : Annotatable ,
) -> Vec < Annotatable > {
2019-07-19 02:10:36 +03:00
check_builtin_macro_attribute ( cx , meta_item , sym ::bench ) ;
2021-11-24 10:20:23 +01:00
warn_on_duplicate_attribute ( & cx , & item , sym ::bench ) ;
2018-07-20 18:04:02 -07:00
expand_test_or_bench ( cx , attr_sp , item , true )
}
pub fn expand_test_or_bench (
2019-02-04 21:49:54 +09:00
cx : & mut ExtCtxt < '_ > ,
2018-07-20 18:04:02 -07:00
attr_sp : Span ,
item : Annotatable ,
2019-12-24 17:38:22 -05:00
is_bench : bool ,
2018-07-20 18:04:02 -07:00
) -> Vec < Annotatable > {
// If we're not in test configuration, remove the annotated item
2019-12-24 17:38:22 -05:00
if ! cx . ecfg . should_test {
return vec! [ ] ;
}
2018-07-20 18:04:02 -07:00
2020-11-22 19:32:39 -05:00
let ( item , is_stmt ) = match item {
Annotatable ::Item ( i ) = > ( i , false ) ,
Annotatable ::Stmt ( stmt ) if matches! ( stmt . kind , ast ::StmtKind ::Item ( _ ) ) = > {
// FIXME: Use an 'if let' guard once they are implemented
if let ast ::StmtKind ::Item ( i ) = stmt . into_inner ( ) . kind {
( i , true )
} else {
unreachable! ( )
}
}
2020-03-17 13:27:56 +01:00
other = > {
cx . struct_span_err (
other . span ( ) ,
2019-12-24 17:38:22 -05:00
" `#[test]` attribute is only allowed on non associated functions " ,
)
2020-03-17 13:27:56 +01:00
. emit ( ) ;
return vec! [ other ] ;
}
2019-12-24 17:38:22 -05:00
} ;
2018-07-20 18:04:02 -07:00
2022-01-15 04:16:13 -05:00
// Note: non-associated fn items are already handled by `expand_test_or_bench`
2022-10-23 19:12:23 +02:00
let ast ::ItemKind ::Fn ( fn_ ) = & item . kind else {
2022-03-04 20:34:10 +00:00
let diag = & cx . sess . parse_sess . span_diagnostic ;
let msg = " the `#[test]` attribute may only be used on a non-associated function " ;
let mut err = match item . kind {
// These were a warning before #92959 and need to continue being that to avoid breaking
// stable user code (#94508).
ast ::ItemKind ::MacCall ( _ ) = > diag . struct_span_warn ( attr_sp , msg ) ,
// `.forget_guarantee()` needed to get these two arms to match types. Because of how
// locally close the `.emit()` call is I'm comfortable with it, but if it can be
// reworked in the future to not need it, it'd be nice.
_ = > diag . struct_span_err ( attr_sp , msg ) . forget_guarantee ( ) ,
} ;
2022-10-02 21:40:39 +03:00
err . span_label ( attr_sp , " the `#[test]` macro causes a function to be run on a test and has no effect on non-functions " )
2022-01-15 04:16:13 -05:00
. span_label ( item . span , format! ( " expected a non-associated function, found {} {} " , item . kind . article ( ) , item . kind . descr ( ) ) )
2022-06-13 15:48:40 +09:00
. span_suggestion ( attr_sp , " replace with conditional compilation to make the item only exist when tests are being run " , " #[cfg(test)] " , Applicability ::MaybeIncorrect )
2022-01-15 04:16:13 -05:00
. emit ( ) ;
2018-07-20 18:04:02 -07:00
return vec! [ Annotatable ::Item ( item ) ] ;
2022-10-23 19:12:23 +02:00
} ;
2018-07-20 18:04:02 -07:00
// has_*_signature will report any errors in the type so compilation
// will fail. We shouldn't try to expand in this case because the errors
// would be spurious.
2019-12-24 17:38:22 -05:00
if ( ! is_bench & & ! has_test_signature ( cx , & item ) )
| | ( is_bench & & ! has_bench_signature ( cx , & item ) )
{
2018-07-20 18:04:02 -07:00
return vec! [ Annotatable ::Item ( item ) ] ;
}
2022-10-23 19:12:23 +02:00
let sp = cx . with_def_site_ctxt ( item . span ) ;
let ret_ty_sp = cx . with_def_site_ctxt ( fn_ . sig . decl . output . span ( ) ) ;
let attr_sp = cx . with_def_site_ctxt ( attr_sp ) ;
2018-07-20 18:04:02 -07:00
2020-04-19 13:00:18 +02:00
let test_id = Ident ::new ( sym ::test , attr_sp ) ;
2018-07-20 18:04:02 -07:00
// creates test::$name
2022-10-23 19:12:23 +02:00
let test_path = | name | cx . path ( ret_ty_sp , vec! [ test_id , Ident ::from_str_and_span ( name , sp ) ] ) ;
2018-07-20 18:04:02 -07:00
2019-04-17 12:02:04 +03:00
// creates test::ShouldPanic::$name
2020-07-14 15:05:26 +10:00
let should_panic_path = | name | {
cx . path (
sp ,
vec! [
test_id ,
Ident ::from_str_and_span ( " ShouldPanic " , sp ) ,
Ident ::from_str_and_span ( name , sp ) ,
] ,
)
} ;
2018-07-20 18:04:02 -07:00
2019-09-28 17:07:18 +03:00
// creates test::TestType::$name
2020-07-14 15:05:26 +10:00
let test_type_path = | name | {
cx . path (
sp ,
vec! [
test_id ,
Ident ::from_str_and_span ( " TestType " , sp ) ,
Ident ::from_str_and_span ( name , sp ) ,
] ,
)
} ;
2019-09-28 17:07:18 +03:00
2018-07-20 18:04:02 -07:00
// creates $name: $expr
2020-07-14 15:05:26 +10:00
let field = | name , expr | cx . field_imm ( sp , Ident ::from_str_and_span ( name , sp ) , expr ) ;
2018-07-20 18:04:02 -07:00
let test_fn = if is_bench {
// A simple ident for a lambda
2020-07-14 15:05:26 +10:00
let b = Ident ::from_str_and_span ( " b " , attr_sp ) ;
2018-07-20 18:04:02 -07:00
2019-12-24 17:38:22 -05:00
cx . expr_call (
sp ,
cx . expr_path ( test_path ( " StaticBenchFn " ) ) ,
vec! [
// |b| self::test::assert_test_result(
cx . lambda1 (
sp ,
cx . expr_call (
sp ,
cx . expr_path ( test_path ( " assert_test_result " ) ) ,
vec! [
// super::$test_fn(b)
cx . expr_call (
2022-10-23 19:12:23 +02:00
ret_ty_sp ,
2019-12-24 17:38:22 -05:00
cx . expr_path ( cx . path ( sp , vec! [ item . ident ] ) ) ,
vec! [ cx . expr_ident ( sp , b ) ] ,
) ,
] ,
) ,
b ,
) , // )
] ,
)
2018-07-20 18:04:02 -07:00
} else {
2019-12-24 17:38:22 -05:00
cx . expr_call (
sp ,
cx . expr_path ( test_path ( " StaticTestFn " ) ) ,
vec! [
// || {
cx . lambda0 (
sp ,
// test::assert_test_result(
cx . expr_call (
sp ,
cx . expr_path ( test_path ( " assert_test_result " ) ) ,
vec! [
// $test_fn()
2022-10-23 19:12:23 +02:00
cx . expr_call (
ret_ty_sp ,
cx . expr_path ( cx . path ( sp , vec! [ item . ident ] ) ) ,
vec! [ ] ,
) , // )
2019-12-24 17:38:22 -05:00
] ,
) , // }
) , // )
] ,
)
2018-07-20 18:04:02 -07:00
} ;
2022-07-26 17:40:48 -04:00
let test_path_symbol = Symbol ::intern ( & item_path (
// skip the name of the root module
& cx . current_expansion . module . mod_path [ 1 .. ] ,
& item . ident ,
) ) ;
2019-12-24 17:38:22 -05:00
let mut test_const = cx . item (
sp ,
2020-04-19 13:00:18 +02:00
Ident ::new ( item . ident . name , sp ) ,
2022-08-17 14:22:30 +10:00
thin_vec! [
2018-09-02 09:03:24 -07:00
// #[cfg(test)]
Avoid more `MetaItem`-to-`Attribute` conversions.
There is code for converting `Attribute` (syntactic) to `MetaItem`
(semantic). There is also code for the reverse direction. The reverse
direction isn't really necessary; it's currently only used when
generating attributes, e.g. in `derive` code.
This commit adds some new functions for creating `Attributes`s directly,
without involving `MetaItem`s: `mk_attr_word`, `mk_attr_name_value_str`,
`mk_attr_nested_word`, and
`ExtCtxt::attr_{word,name_value_str,nested_word}`.
These new methods replace the old functions for creating `Attribute`s:
`mk_attr_inner`, `mk_attr_outer`, and `ExtCtxt::attribute`. Those
functions took `MetaItem`s as input, and relied on many other functions
that created `MetaItems`, which are also removed: `mk_name_value_item`,
`mk_list_item`, `mk_word_item`, `mk_nested_word_item`,
`{MetaItem,MetaItemKind,NestedMetaItem}::token_trees`,
`MetaItemKind::attr_args`, `MetaItemLit::{from_lit_kind,to_token}`,
`ExtCtxt::meta_word`.
Overall this cuts more than 100 lines of code and makes thing simpler.
2022-11-29 18:43:44 +11:00
cx . attr_nested_word ( sym ::cfg , sym ::test , attr_sp ) ,
2022-07-26 17:40:48 -04:00
// #[rustc_test_marker = "test_case_sort_key"]
Avoid more `MetaItem`-to-`Attribute` conversions.
There is code for converting `Attribute` (syntactic) to `MetaItem`
(semantic). There is also code for the reverse direction. The reverse
direction isn't really necessary; it's currently only used when
generating attributes, e.g. in `derive` code.
This commit adds some new functions for creating `Attributes`s directly,
without involving `MetaItem`s: `mk_attr_word`, `mk_attr_name_value_str`,
`mk_attr_nested_word`, and
`ExtCtxt::attr_{word,name_value_str,nested_word}`.
These new methods replace the old functions for creating `Attribute`s:
`mk_attr_inner`, `mk_attr_outer`, and `ExtCtxt::attribute`. Those
functions took `MetaItem`s as input, and relied on many other functions
that created `MetaItems`, which are also removed: `mk_name_value_item`,
`mk_list_item`, `mk_word_item`, `mk_nested_word_item`,
`{MetaItem,MetaItemKind,NestedMetaItem}::token_trees`,
`MetaItemKind::attr_args`, `MetaItemLit::{from_lit_kind,to_token}`,
`ExtCtxt::meta_word`.
Overall this cuts more than 100 lines of code and makes thing simpler.
2022-11-29 18:43:44 +11:00
cx . attr_name_value_str ( sym ::rustc_test_marker , test_path_symbol , attr_sp ) ,
2022-12-18 15:01:26 +01:00
] ,
2018-07-20 18:04:02 -07:00
// const $ident: test::TestDescAndFn =
2019-12-24 17:38:22 -05:00
ast ::ItemKind ::Const (
2020-02-23 10:24:30 +01:00
ast ::Defaultness ::Final ,
2019-12-24 17:38:22 -05:00
cx . ty ( sp , ast ::TyKind ::Path ( None , test_path ( " TestDescAndFn " ) ) ) ,
2018-07-20 18:04:02 -07:00
// test::TestDescAndFn {
2020-02-14 14:21:02 +01:00
Some (
cx . expr_struct (
sp ,
test_path ( " TestDescAndFn " ) ,
vec! [
// desc: test::TestDesc {
field (
" desc " ,
cx . expr_struct (
sp ,
test_path ( " TestDesc " ) ,
vec! [
// name: "path::to::test"
field (
" name " ,
cx . expr_call (
2019-12-24 17:38:22 -05:00
sp ,
2020-02-14 14:21:02 +01:00
cx . expr_path ( test_path ( " StaticTestName " ) ) ,
2022-07-26 17:40:48 -04:00
vec! [ cx . expr_str ( sp , test_path_symbol ) ] ,
2019-12-24 17:38:22 -05:00
) ,
2020-02-14 14:21:02 +01:00
) ,
// ignore: true | false
2020-07-30 11:27:50 +10:00
field (
" ignore " ,
cx . expr_bool ( sp , should_ignore ( & cx . sess , & item ) ) ,
) ,
2022-01-10 14:01:12 +08:00
// ignore_message: Some("...") | None
field (
" ignore_message " ,
if let Some ( msg ) = should_ignore_message ( cx , & item ) {
cx . expr_some ( sp , cx . expr_str ( sp , msg ) )
} else {
cx . expr_none ( sp )
} ,
) ,
2021-05-03 14:55:22 +02:00
// compile_fail: true | false
field ( " compile_fail " , cx . expr_bool ( sp , false ) ) ,
// no_run: true | false
field ( " no_run " , cx . expr_bool ( sp , false ) ) ,
2020-02-14 14:21:02 +01:00
// should_panic: ...
field (
" should_panic " ,
match should_panic ( cx , & item ) {
// test::ShouldPanic::No
ShouldPanic ::No = > {
cx . expr_path ( should_panic_path ( " No " ) )
}
// test::ShouldPanic::Yes
ShouldPanic ::Yes ( None ) = > {
cx . expr_path ( should_panic_path ( " Yes " ) )
}
// test::ShouldPanic::YesWithMessage("...")
ShouldPanic ::Yes ( Some ( sym ) ) = > cx . expr_call (
sp ,
cx . expr_path ( should_panic_path ( " YesWithMessage " ) ) ,
vec! [ cx . expr_str ( sp , sym ) ] ,
) ,
} ,
) ,
// test_type: ...
field (
" test_type " ,
match test_type ( cx ) {
// test::TestType::UnitTest
TestType ::UnitTest = > {
cx . expr_path ( test_type_path ( " UnitTest " ) )
}
// test::TestType::IntegrationTest
TestType ::IntegrationTest = > {
cx . expr_path ( test_type_path ( " IntegrationTest " ) )
}
// test::TestPath::Unknown
TestType ::Unknown = > {
cx . expr_path ( test_type_path ( " Unknown " ) )
}
} ,
) ,
// },
] ,
) ,
2019-09-28 17:07:18 +03:00
) ,
2020-02-14 14:21:02 +01:00
// testfn: test::StaticTestFn(...) | test::StaticBenchFn(...)
field ( " testfn " , test_fn ) , // }
] ,
) , // }
) ,
2019-12-24 17:38:22 -05:00
) ,
) ;
test_const = test_const . map ( | mut tc | {
2020-08-21 19:11:00 -04:00
tc . vis . kind = ast ::VisibilityKind ::Public ;
2019-12-24 17:38:22 -05:00
tc
} ) ;
2018-07-20 18:04:02 -07:00
2019-08-25 21:03:24 +01:00
// extern crate test
2022-08-17 12:34:33 +10:00
let test_extern = cx . item ( sp , test_id , ast ::AttrVec ::new ( ) , ast ::ItemKind ::ExternCrate ( None ) ) ;
2018-07-20 18:04:02 -07:00
2022-08-31 13:09:26 +00:00
debug! ( " synthetic test item: \n {} \n " , pprust ::item_to_string ( & test_const ) ) ;
2018-07-20 18:04:02 -07:00
2020-11-22 19:32:39 -05:00
if is_stmt {
vec! [
// Access to libtest under a hygienic name
Annotatable ::Stmt ( P ( cx . stmt_item ( sp , test_extern ) ) ) ,
// The generated test case
Annotatable ::Stmt ( P ( cx . stmt_item ( sp , test_const ) ) ) ,
// The original item
Annotatable ::Stmt ( P ( cx . stmt_item ( sp , item ) ) ) ,
]
} else {
vec! [
// Access to libtest under a hygienic name
Annotatable ::Item ( test_extern ) ,
// The generated test case
Annotatable ::Item ( test_const ) ,
// The original item
Annotatable ::Item ( item ) ,
]
}
2018-07-20 18:04:02 -07:00
}
2020-04-19 13:00:18 +02:00
fn item_path ( mod_path : & [ Ident ] , item_ident : & Ident ) -> String {
2019-12-24 17:38:22 -05:00
mod_path
. iter ( )
. chain ( iter ::once ( item_ident ) )
. map ( | x | x . to_string ( ) )
. collect ::< Vec < String > > ( )
. join ( " :: " )
2018-07-20 18:04:02 -07:00
}
enum ShouldPanic {
No ,
Yes ( Option < Symbol > ) ,
}
2020-07-30 11:27:50 +10:00
fn should_ignore ( sess : & Session , i : & ast ::Item ) -> bool {
sess . contains_name ( & i . attrs , sym ::ignore )
2018-07-20 18:04:02 -07:00
}
2022-01-10 14:01:12 +08:00
fn should_ignore_message ( cx : & ExtCtxt < '_ > , i : & ast ::Item ) -> Option < Symbol > {
match cx . sess . find_by_name ( & i . attrs , sym ::ignore ) {
Some ( attr ) = > {
match attr . meta_item_list ( ) {
// Handle #[ignore(bar = "foo")]
Some ( _ ) = > None ,
// Handle #[ignore] and #[ignore = "message"]
None = > attr . value_str ( ) ,
}
}
None = > None ,
}
}
2019-02-04 21:49:54 +09:00
fn should_panic ( cx : & ExtCtxt < '_ > , i : & ast ::Item ) -> ShouldPanic {
2020-07-30 11:27:50 +10:00
match cx . sess . find_by_name ( & i . attrs , sym ::should_panic ) {
2018-07-20 18:04:02 -07:00
Some ( attr ) = > {
2020-07-30 11:27:50 +10:00
let sd = & cx . sess . parse_sess . span_diagnostic ;
2019-01-02 02:21:05 +03:00
2018-07-20 18:04:02 -07:00
match attr . meta_item_list ( ) {
// Handle #[should_panic(expected = "foo")]
Some ( list ) = > {
2019-12-24 17:38:22 -05:00
let msg = list
. iter ( )
2020-08-02 13:17:20 +03:00
. find ( | mi | mi . has_name ( sym ::expected ) )
2018-07-20 18:04:02 -07:00
. and_then ( | mi | mi . meta_item ( ) )
. and_then ( | mi | mi . value_str ( ) ) ;
if list . len ( ) ! = 1 | | msg . is_none ( ) {
sd . struct_span_warn (
2019-03-03 20:56:24 +03:00
attr . span ,
2018-07-20 18:04:02 -07:00
" argument must be of the form: \
2019-12-24 17:38:22 -05:00
` expected = \ " error message \" ` " ,
)
. note (
2020-01-10 14:13:05 +00:00
" errors in this attribute were erroneously \
2018-07-20 18:04:02 -07:00
allowed and will become a hard error in a \
2021-10-03 15:53:02 +09:00
future release " ,
2019-12-24 17:38:22 -05:00
)
. emit ( ) ;
2018-07-20 18:04:02 -07:00
ShouldPanic ::Yes ( None )
} else {
ShouldPanic ::Yes ( msg )
}
2019-12-24 17:38:22 -05:00
}
2019-01-02 02:21:05 +03:00
// Handle #[should_panic] and #[should_panic = "expected"]
2019-12-24 17:38:22 -05:00
None = > ShouldPanic ::Yes ( attr . value_str ( ) ) ,
2018-07-20 18:04:02 -07:00
}
}
None = > ShouldPanic ::No ,
}
}
2019-09-28 17:07:18 +03:00
enum TestType {
UnitTest ,
IntegrationTest ,
Unknown ,
}
/// Attempts to determine the type of test.
/// Since doctests are created without macro expanding, only possible variants here
/// are `UnitTest`, `IntegrationTest` or `Unknown`.
fn test_type ( cx : & ExtCtxt < '_ > ) -> TestType {
// Root path from context contains the topmost sources directory of the crate.
// I.e., for `project` with sources in `src` and tests in `tests` folders
// (no matter how many nested folders lie inside),
// there will be two different root paths: `/project/src` and `/project/tests`.
let crate_path = cx . root_path . as_path ( ) ;
if crate_path . ends_with ( " src " ) {
// `/src` folder contains unit-tests.
TestType ::UnitTest
} else if crate_path . ends_with ( " tests " ) {
// `/tests` folder contains integration tests.
TestType ::IntegrationTest
} else {
// Crate layout doesn't match expected one, test type is unknown.
TestType ::Unknown
}
}
2019-02-04 21:49:54 +09:00
fn has_test_signature ( cx : & ExtCtxt < '_ > , i : & ast ::Item ) -> bool {
2020-07-30 11:27:50 +10:00
let has_should_panic_attr = cx . sess . contains_name ( & i . attrs , sym ::should_panic ) ;
let sd = & cx . sess . parse_sess . span_diagnostic ;
2022-12-06 13:22:36 +00:00
match & i . kind {
ast ::ItemKind ::Fn ( box ast ::Fn { sig , generics , .. } ) = > {
if let ast ::Unsafe ::Yes ( span ) = sig . header . unsafety {
sd . struct_span_err ( i . span , " unsafe functions cannot be used for tests " )
. span_label ( span , " `unsafe` because of this " )
. emit ( ) ;
return false ;
}
if let ast ::Async ::Yes { span , .. } = sig . header . asyncness {
sd . struct_span_err ( i . span , " async functions cannot be used for tests " )
. span_label ( span , " `async` because of this " )
. emit ( ) ;
return false ;
}
2018-07-20 18:04:02 -07:00
2022-12-06 13:22:36 +00:00
// If the termination trait is active, the compiler will check that the output
// type implements the `Termination` trait as `libtest` enforces that.
let has_output = match & sig . decl . output {
ast ::FnRetTy ::Default ( .. ) = > false ,
ast ::FnRetTy ::Ty ( t ) if t . kind . is_unit ( ) = > false ,
_ = > true ,
} ;
if ! sig . decl . inputs . is_empty ( ) {
sd . span_err ( i . span , " functions used as tests can not have any arguments " ) ;
return false ;
2019-12-24 17:38:22 -05:00
}
2022-12-06 13:22:36 +00:00
match ( has_output , has_should_panic_attr ) {
( true , true ) = > {
sd . span_err ( i . span , " functions using `#[should_panic]` must return `()` " ) ;
2019-12-24 17:38:22 -05:00
false
}
2022-12-06 13:22:36 +00:00
( true , false ) = > {
if ! generics . params . is_empty ( ) {
sd . span_err (
i . span ,
" functions used as tests must have signature fn() -> () " ,
) ;
false
} else {
true
}
}
( false , _ ) = > true ,
2019-12-24 17:38:22 -05:00
}
2018-07-20 18:04:02 -07:00
}
2022-12-06 13:22:36 +00:00
_ = > {
// should be unreachable because `is_test_fn_item` should catch all non-fn items
debug_assert! ( false ) ;
false
}
2018-07-20 18:04:02 -07:00
}
}
2019-02-04 21:49:54 +09:00
fn has_bench_signature ( cx : & ExtCtxt < '_ > , i : & ast ::Item ) -> bool {
2022-12-06 13:22:36 +00:00
let has_sig = match & i . kind {
2018-11-27 02:59:49 +00:00
// N.B., inadequate check, but we're running
2018-07-20 18:04:02 -07:00
// well before resolve, can't get too deep.
2022-12-06 13:22:36 +00:00
ast ::ItemKind ::Fn ( box ast ::Fn { sig , .. } ) = > sig . decl . inputs . len ( ) = = 1 ,
_ = > false ,
2018-07-20 18:04:02 -07:00
} ;
if ! has_sig {
2020-07-30 11:27:50 +10:00
cx . sess . parse_sess . span_diagnostic . span_err (
2019-12-24 17:38:22 -05:00
i . span ,
" functions used as benches must have \
signature ` fn ( & mut Bencher ) -> impl Termination ` " ,
) ;
2018-07-20 18:04:02 -07:00
}
has_sig
}