diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index ff89982a4efd8..dd57d7acc9601 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -392,6 +392,12 @@ pub struct GenericParam { pub kind: GenericParamKind, } +impl GenericParam { + pub fn is_lifetime(&self) -> bool { + matches!(self.kind, GenericParamKind::Lifetime) + } +} + /// Represents lifetime, type and const parameters attached to a declaration of /// a function, enum, trait, etc. #[derive(Clone, Encodable, Decodable, Debug)] diff --git a/compiler/rustc_builtin_macros/src/test.rs b/compiler/rustc_builtin_macros/src/test.rs index 25d3f46da6cdc..7b1614e637651 100644 --- a/compiler/rustc_builtin_macros/src/test.rs +++ b/compiler/rustc_builtin_macros/src/test.rs @@ -458,8 +458,8 @@ fn has_test_signature(cx: &ExtCtxt<'_>, i: &ast::Item) -> bool { false } (true, false) => { - if !generics.params.is_empty() { - sd.span_err(i.span, "functions used as tests must have signature fn() -> ()"); + if !generics.params.iter().all(|p| p.is_lifetime()) { + sd.span_err(i.span, "return value of functions used as tests must either be `()` or implement `Termination`, and cannot use generics"); false } else { true diff --git a/src/test/ui/test-fn-with-lifetime.rs b/src/test/ui/test-fn-with-lifetime.rs new file mode 100644 index 0000000000000..f03ea0ed16018 --- /dev/null +++ b/src/test/ui/test-fn-with-lifetime.rs @@ -0,0 +1,47 @@ +// compile-flags: --test + +// Issue #55228 + +#![feature(termination_trait_lib)] + +use std::process::Termination; + +#[test] +fn unit_1() -> Result<(), &'static str> { // this is OK + Ok(()) +} + +#[test] +fn unit_2<'a>() -> Result<(), &'a str> { // also OK + Ok(()) +} + +#[test] +fn unit_3() -> Result<(), T> { // nope (how would this get monomorphized?) + //~^ ERROR return value of functions used as tests must either be `()` or implement + Ok(()) +} + + +struct Quux; + +trait Bar { + type Baz; + + fn rah(&self) -> Self::Baz; +} + +impl Bar for Quux { + type Baz = String; + + fn rah(&self) -> Self::Baz { + "rah".to_owned() + } +} + +#[test] +fn unit_4>() -> ::Baz { // also nope + //~^ ERROR return value of functions used as tests must either be `()` or implement + let q = Quux{}; + ::rah(&q) +} diff --git a/src/test/ui/test-fn-with-lifetime.stderr b/src/test/ui/test-fn-with-lifetime.stderr new file mode 100644 index 0000000000000..1e19b6b0a96c1 --- /dev/null +++ b/src/test/ui/test-fn-with-lifetime.stderr @@ -0,0 +1,21 @@ +error: return value of functions used as tests must either be `()` or implement `Termination`, and cannot use generics + --> $DIR/test-fn-with-lifetime.rs:20:1 + | +LL | / fn unit_3() -> Result<(), T> { // nope (how would this get monomorphized?) +LL | | +LL | | Ok(()) +LL | | } + | |_^ + +error: return value of functions used as tests must either be `()` or implement `Termination`, and cannot use generics + --> $DIR/test-fn-with-lifetime.rs:43:1 + | +LL | / fn unit_4>() -> ::Baz { // also nope +LL | | +LL | | let q = Quux{}; +LL | | ::rah(&q) +LL | | } + | |_^ + +error: aborting due to 2 previous errors +