@@ -20,7 +20,17 @@ pub fn cargo_test(attr: TokenStream, item: TokenStream) -> TokenStream {
20
20
// but they don't really handle the absence of files well.
21
21
let mut ignore = false ;
22
22
let mut requires_reason = false ;
23
- let mut found_reason = false ;
23
+ let mut explicit_reason = None ;
24
+ let mut implicit_reasons = Vec :: new ( ) ;
25
+ macro_rules! set_ignore {
26
+ ( $predicate: expr, $( $arg: tt) * ) => {
27
+ let p = $predicate;
28
+ ignore |= p;
29
+ if p {
30
+ implicit_reasons. push( std:: fmt:: format( format_args!( $( $arg) * ) ) ) ;
31
+ }
32
+ } ;
33
+ }
24
34
let is_not_nightly = !version ( ) . 1 ;
25
35
for rule in split_rules ( attr) {
26
36
match rule. as_str ( ) {
@@ -29,57 +39,81 @@ pub fn cargo_test(attr: TokenStream, item: TokenStream) -> TokenStream {
29
39
// explicit opt-in (these generally only work on linux, and
30
40
// have some extra requirements, and are slow, and can pollute
31
41
// the environment since it downloads dependencies).
32
- ignore |= is_not_nightly;
33
- ignore |= option_env ! ( "CARGO_RUN_BUILD_STD_TESTS" ) . is_none ( ) ;
42
+ set_ignore ! ( is_not_nightly, "requires nightly" ) ;
43
+ set_ignore ! (
44
+ option_env!( "CARGO_RUN_BUILD_STD_TESTS" ) . is_none( ) ,
45
+ "CARGO_RUN_BUILD_STD_TESTS must be set"
46
+ ) ;
34
47
}
35
48
"build_std_mock" => {
36
49
// Only run the "mock" build-std tests on nightly and disable
37
50
// for windows-gnu which is missing object files (see
38
51
// https://github.com/rust-lang/wg-cargo-std-aware/issues/46).
39
- ignore |= is_not_nightly;
40
- ignore |= cfg ! ( all( target_os = "windows" , target_env = "gnu" ) ) ;
52
+ set_ignore ! ( is_not_nightly, "requires nightly" ) ;
53
+ set_ignore ! (
54
+ cfg!( all( target_os = "windows" , target_env = "gnu" ) ) ,
55
+ "does not work on windows-gnu"
56
+ ) ;
41
57
}
42
58
"nightly" => {
43
59
requires_reason = true ;
44
- ignore |= is_not_nightly ;
60
+ set_ignore ! ( is_not_nightly , "requires nightly" ) ;
45
61
}
46
62
s if s. starts_with ( "requires_" ) => {
47
63
let command = & s[ 9 ..] ;
48
- ignore |= ! has_command ( command) ;
64
+ set_ignore ! ( ! has_command( command) , "{command} not installed" ) ;
49
65
}
50
66
s if s. starts_with ( ">=1." ) => {
51
67
requires_reason = true ;
52
68
let min_minor = s[ 4 ..] . parse ( ) . unwrap ( ) ;
53
- ignore |= version ( ) . 0 < min_minor;
69
+ let minor = version ( ) . 0 ;
70
+ set_ignore ! ( minor < min_minor, "requires rustc 1.{minor} or newer" ) ;
54
71
}
55
72
s if s. starts_with ( "reason=" ) => {
56
- found_reason = true ;
73
+ explicit_reason = Some ( s [ 7 .. ] . parse ( ) . unwrap ( ) ) ;
57
74
}
58
75
_ => panic ! ( "unknown rule {:?}" , rule) ,
59
76
}
60
77
}
61
- if requires_reason && !found_reason {
78
+ if requires_reason && explicit_reason . is_none ( ) {
62
79
panic ! (
63
80
"#[cargo_test] with a rule also requires a reason, \
64
81
such as #[cargo_test(nightly, reason = \" needs -Z unstable-thing\" )]"
65
82
) ;
66
83
}
67
84
85
+ // Construct the appropriate attributes.
68
86
let span = Span :: call_site ( ) ;
69
87
let mut ret = TokenStream :: new ( ) ;
70
- let add_attr = |ret : & mut TokenStream , attr_name| {
88
+ let add_attr = |ret : & mut TokenStream , attr_name, attr_input | {
71
89
ret. extend ( Some ( TokenTree :: from ( Punct :: new ( '#' , Spacing :: Alone ) ) ) ) ;
72
90
let attr = TokenTree :: from ( Ident :: new ( attr_name, span) ) ;
91
+ let mut attr_stream: TokenStream = attr. into ( ) ;
92
+ if let Some ( input) = attr_input {
93
+ attr_stream. extend ( input) ;
94
+ }
73
95
ret. extend ( Some ( TokenTree :: from ( Group :: new (
74
96
Delimiter :: Bracket ,
75
- attr . into ( ) ,
97
+ attr_stream ,
76
98
) ) ) ) ;
77
99
} ;
78
- add_attr ( & mut ret, "test" ) ;
100
+ add_attr ( & mut ret, "test" , None ) ;
79
101
if ignore {
80
- add_attr ( & mut ret, "ignore" ) ;
102
+ let reason = explicit_reason
103
+ . or_else ( || {
104
+ ( !implicit_reasons. is_empty ( ) )
105
+ . then ( || TokenTree :: from ( Literal :: string ( & implicit_reasons. join ( ", " ) ) ) . into ( ) )
106
+ } )
107
+ . map ( |reason : TokenStream | {
108
+ let mut stream = TokenStream :: new ( ) ;
109
+ stream. extend ( Some ( TokenTree :: from ( Punct :: new ( '=' , Spacing :: Alone ) ) ) ) ;
110
+ stream. extend ( Some ( reason) ) ;
111
+ stream
112
+ } ) ;
113
+ add_attr ( & mut ret, "ignore" , reason) ;
81
114
}
82
115
116
+ // Find where the function body starts, and add the boilerplate at the start.
83
117
for token in item {
84
118
let group = match token {
85
119
TokenTree :: Group ( g) => {
0 commit comments