Skip to content

Commit

Permalink
Rollup merge of rust-lang#112366 - lukas-code:test, r=Nilstrieb
Browse files Browse the repository at this point in the history
`#[test]` function signature verification improvements

This PR contains two improvements to the expansion of the `#[test]` macro.

The first one fixes rust-lang#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.
  • Loading branch information
jyn514 committed Jun 19, 2023
2 parents 29ddde6 + 5049743 commit 0413f32
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 24 deletions.
45 changes: 21 additions & 24 deletions compiler/rustc_builtin_macros/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::errors;
/// Ideally, this code would be in libtest but for efficiency and error messages it lives here.
use crate::util::{check_builtin_macro_attribute, warn_on_duplicate_attribute};
use rustc_ast::ptr::P;
use rustc_ast::{self as ast, attr};
use rustc_ast::{self as ast, attr, GenericParamKind};
use rustc_ast_pretty::pprust;
use rustc_errors::Applicability;
use rustc_expand::base::*;
Expand Down Expand Up @@ -122,11 +122,7 @@ pub fn expand_test_or_bench(
let ast::ItemKind::Fn(fn_) = &item.kind else {
not_testable_error(cx, attr_sp, Some(&item));
return if is_stmt {
vec![Annotatable::Stmt(P(ast::Stmt {
id: ast::DUMMY_NODE_ID,
span: item.span,
kind: ast::StmtKind::Item(item),
}))]
vec![Annotatable::Stmt(P(cx.stmt_item(item.span, item)))]
} else {
vec![Annotatable::Item(item)]
};
Expand All @@ -138,7 +134,11 @@ pub fn expand_test_or_bench(
if (!is_bench && !has_test_signature(cx, &item))
|| (is_bench && !has_bench_signature(cx, &item))
{
return vec![Annotatable::Item(item)];
return if is_stmt {
vec![Annotatable::Stmt(P(cx.stmt_item(item.span, item)))]
} else {
vec![Annotatable::Item(item)]
};
}

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

match (has_output, has_should_panic_attr) {
(true, true) => {
sd.span_err(i.span, "functions using `#[should_panic]` must return `()`");
false
}
(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,
if has_should_panic_attr && has_output {
sd.span_err(i.span, "functions using `#[should_panic]` must return `()`");
return false;
}

if generics.params.iter().any(|param| !matches!(param.kind, GenericParamKind::Lifetime))
{
sd.span_err(
i.span,
"functions used as tests can not have any non-lifetime generic parameters",
);
return false;
}

true
}
_ => {
// should be unreachable because `is_test_fn_item` should catch all non-fn items
Expand Down
31 changes: 31 additions & 0 deletions tests/ui/test-attrs/test-function-signature.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// compile-flags: --test

#[test]
fn foo() -> Result<(), ()> {
Ok(())
}

#[test]
fn bar() -> i32 { //~ ERROR the trait bound `i32: Termination` is not satisfied
0
}

#[test]
fn baz(val: i32) {} //~ ERROR functions used as tests can not have any arguments

#[test]
fn lifetime_generic<'a>() -> Result<(), &'a str> {
Err("coerce me to any lifetime")
}

#[test]
fn type_generic<T>() {} //~ ERROR functions used as tests can not have any non-lifetime generic parameters

#[test]
fn const_generic<const N: usize>() {} //~ ERROR functions used as tests can not have any non-lifetime generic parameters

// Regression test for <https://github.com/rust-lang/rust/issues/112360>. This used to ICE.
fn nested() {
#[test]
fn foo(arg: ()) {} //~ ERROR functions used as tests can not have any arguments
}
39 changes: 39 additions & 0 deletions tests/ui/test-attrs/test-function-signature.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
error: functions used as tests can not have any arguments
--> $DIR/test-function-signature.rs:14:1
|
LL | fn baz(val: i32) {}
| ^^^^^^^^^^^^^^^^^^^

error: functions used as tests can not have any non-lifetime generic parameters
--> $DIR/test-function-signature.rs:22:1
|
LL | fn type_generic<T>() {}
| ^^^^^^^^^^^^^^^^^^^^^^^

error: functions used as tests can not have any non-lifetime generic parameters
--> $DIR/test-function-signature.rs:25:1
|
LL | fn const_generic<const N: usize>() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: functions used as tests can not have any arguments
--> $DIR/test-function-signature.rs:30:5
|
LL | fn foo(arg: ()) {}
| ^^^^^^^^^^^^^^^^^^

error[E0277]: the trait bound `i32: Termination` is not satisfied
--> $DIR/test-function-signature.rs:9:13
|
LL | #[test]
| ------- in this procedural macro expansion
LL | fn bar() -> i32 {
| ^^^ the trait `Termination` is not implemented for `i32`
|
note: required by a bound in `assert_test_result`
--> $SRC_DIR/test/src/lib.rs:LL:COL
= note: this error originates in the attribute macro `test` (in Nightly builds, run with -Z macro-backtrace for more info)

error: aborting due to 5 previous errors

For more information about this error, try `rustc --explain E0277`.

0 comments on commit 0413f32

Please sign in to comment.