Skip to content

Commit 6895110

Browse files
committed
Auto merge of #112366 - lukas-code:test, r=Nilstrieb
`#[test]` function signature verification improvements This PR contains two improvements to the expansion of the `#[test]` macro. The first one fixes #112360 by correctly recovering item statements if the signature verification fails. The second one forbids non-lifetime generics on `#[test]` functions. These were previously allowed if the function returned `()`, but always caused an inference error: before: ```text error[E0282]: type annotations needed --> src/lib.rs:2:1 | 1 | #[test] | ------- in this procedural macro expansion 2 | fn foo<T>() {} | ^^^^^^^^^^^^^^ cannot infer type ``` after: ```text error: functions used as tests can not have any non-lifetime generic parameters --> src/lib.rs:2:1 | 2 | fn foo<T>() {} | ^^^^^^^^^^^^^^ ``` Also includes some basic tests for test function signature verification, because I couldn't find any (???) in the test suite.
2 parents 18a6d91 + 5049743 commit 6895110

File tree

3 files changed

+91
-24
lines changed

3 files changed

+91
-24
lines changed

compiler/rustc_builtin_macros/src/test.rs

+21-24
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::errors;
33
/// Ideally, this code would be in libtest but for efficiency and error messages it lives here.
44
use crate::util::{check_builtin_macro_attribute, warn_on_duplicate_attribute};
55
use rustc_ast::ptr::P;
6-
use rustc_ast::{self as ast, attr};
6+
use rustc_ast::{self as ast, attr, GenericParamKind};
77
use rustc_ast_pretty::pprust;
88
use rustc_errors::Applicability;
99
use rustc_expand::base::*;
@@ -122,11 +122,7 @@ pub fn expand_test_or_bench(
122122
let ast::ItemKind::Fn(fn_) = &item.kind else {
123123
not_testable_error(cx, attr_sp, Some(&item));
124124
return if is_stmt {
125-
vec![Annotatable::Stmt(P(ast::Stmt {
126-
id: ast::DUMMY_NODE_ID,
127-
span: item.span,
128-
kind: ast::StmtKind::Item(item),
129-
}))]
125+
vec![Annotatable::Stmt(P(cx.stmt_item(item.span, item)))]
130126
} else {
131127
vec![Annotatable::Item(item)]
132128
};
@@ -138,7 +134,11 @@ pub fn expand_test_or_bench(
138134
if (!is_bench && !has_test_signature(cx, &item))
139135
|| (is_bench && !has_bench_signature(cx, &item))
140136
{
141-
return vec![Annotatable::Item(item)];
137+
return if is_stmt {
138+
vec![Annotatable::Stmt(P(cx.stmt_item(item.span, item)))]
139+
} else {
140+
vec![Annotatable::Item(item)]
141+
};
142142
}
143143

144144
let sp = cx.with_def_site_ctxt(item.span);
@@ -550,24 +550,21 @@ fn has_test_signature(cx: &ExtCtxt<'_>, i: &ast::Item) -> bool {
550550
return false;
551551
}
552552

553-
match (has_output, has_should_panic_attr) {
554-
(true, true) => {
555-
sd.span_err(i.span, "functions using `#[should_panic]` must return `()`");
556-
false
557-
}
558-
(true, false) => {
559-
if !generics.params.is_empty() {
560-
sd.span_err(
561-
i.span,
562-
"functions used as tests must have signature fn() -> ()",
563-
);
564-
false
565-
} else {
566-
true
567-
}
568-
}
569-
(false, _) => true,
553+
if has_should_panic_attr && has_output {
554+
sd.span_err(i.span, "functions using `#[should_panic]` must return `()`");
555+
return false;
556+
}
557+
558+
if generics.params.iter().any(|param| !matches!(param.kind, GenericParamKind::Lifetime))
559+
{
560+
sd.span_err(
561+
i.span,
562+
"functions used as tests can not have any non-lifetime generic parameters",
563+
);
564+
return false;
570565
}
566+
567+
true
571568
}
572569
_ => {
573570
// should be unreachable because `is_test_fn_item` should catch all non-fn items
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// compile-flags: --test
2+
3+
#[test]
4+
fn foo() -> Result<(), ()> {
5+
Ok(())
6+
}
7+
8+
#[test]
9+
fn bar() -> i32 { //~ ERROR the trait bound `i32: Termination` is not satisfied
10+
0
11+
}
12+
13+
#[test]
14+
fn baz(val: i32) {} //~ ERROR functions used as tests can not have any arguments
15+
16+
#[test]
17+
fn lifetime_generic<'a>() -> Result<(), &'a str> {
18+
Err("coerce me to any lifetime")
19+
}
20+
21+
#[test]
22+
fn type_generic<T>() {} //~ ERROR functions used as tests can not have any non-lifetime generic parameters
23+
24+
#[test]
25+
fn const_generic<const N: usize>() {} //~ ERROR functions used as tests can not have any non-lifetime generic parameters
26+
27+
// Regression test for <https://github.com/rust-lang/rust/issues/112360>. This used to ICE.
28+
fn nested() {
29+
#[test]
30+
fn foo(arg: ()) {} //~ ERROR functions used as tests can not have any arguments
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
error: functions used as tests can not have any arguments
2+
--> $DIR/test-function-signature.rs:14:1
3+
|
4+
LL | fn baz(val: i32) {}
5+
| ^^^^^^^^^^^^^^^^^^^
6+
7+
error: functions used as tests can not have any non-lifetime generic parameters
8+
--> $DIR/test-function-signature.rs:22:1
9+
|
10+
LL | fn type_generic<T>() {}
11+
| ^^^^^^^^^^^^^^^^^^^^^^^
12+
13+
error: functions used as tests can not have any non-lifetime generic parameters
14+
--> $DIR/test-function-signature.rs:25:1
15+
|
16+
LL | fn const_generic<const N: usize>() {}
17+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
18+
19+
error: functions used as tests can not have any arguments
20+
--> $DIR/test-function-signature.rs:30:5
21+
|
22+
LL | fn foo(arg: ()) {}
23+
| ^^^^^^^^^^^^^^^^^^
24+
25+
error[E0277]: the trait bound `i32: Termination` is not satisfied
26+
--> $DIR/test-function-signature.rs:9:13
27+
|
28+
LL | #[test]
29+
| ------- in this procedural macro expansion
30+
LL | fn bar() -> i32 {
31+
| ^^^ the trait `Termination` is not implemented for `i32`
32+
|
33+
note: required by a bound in `assert_test_result`
34+
--> $SRC_DIR/test/src/lib.rs:LL:COL
35+
= note: this error originates in the attribute macro `test` (in Nightly builds, run with -Z macro-backtrace for more info)
36+
37+
error: aborting due to 5 previous errors
38+
39+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)