Skip to content

Commit adf12f3

Browse files
committed
don't gratuitously error on tests returning Result with lifetime
Sander Maijers reports that #[test] functions that return a `Result` with a named lifetime fail to compile with a "functions used as tests must have signature fn() -> ()" error message—but that can't really be right, because #[test] functions that return a `Result` with a static lifetime are accepted! It turns out that this error message dates all the way back to April 2013's 5f1a90e ("Issue 4391: rustc should not silently skip tests with erroneous signature."). But after RFC 1937, we actually do accept non-unit return types in tests. Let's edit the error message accordingly. This resolves #55228, and is of historical interest to #4391.
1 parent edeb631 commit adf12f3

File tree

4 files changed

+76
-2
lines changed

4 files changed

+76
-2
lines changed

compiler/rustc_ast/src/ast.rs

+6
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,12 @@ pub struct GenericParam {
392392
pub kind: GenericParamKind,
393393
}
394394

395+
impl GenericParam {
396+
pub fn is_lifetime(&self) -> bool {
397+
matches!(self.kind, GenericParamKind::Lifetime)
398+
}
399+
}
400+
395401
/// Represents lifetime, type and const parameters attached to a declaration of
396402
/// a function, enum, trait, etc.
397403
#[derive(Clone, Encodable, Decodable, Debug)]

compiler/rustc_builtin_macros/src/test.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -458,8 +458,8 @@ fn has_test_signature(cx: &ExtCtxt<'_>, i: &ast::Item) -> bool {
458458
false
459459
}
460460
(true, false) => {
461-
if !generics.params.is_empty() {
462-
sd.span_err(i.span, "functions used as tests must have signature fn() -> ()");
461+
if !generics.params.iter().all(|p| p.is_lifetime()) {
462+
sd.span_err(i.span, "return value of functions used as tests must either be `()` or implement `Termination`, and cannot use generics");
463463
false
464464
} else {
465465
true

src/test/ui/test-fn-with-lifetime.rs

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// compile-flags: --test
2+
3+
// Issue #55228
4+
5+
#![feature(termination_trait_lib)]
6+
7+
use std::process::Termination;
8+
9+
#[test]
10+
fn unit_1() -> Result<(), &'static str> { // this is OK
11+
Ok(())
12+
}
13+
14+
#[test]
15+
fn unit_2<'a>() -> Result<(), &'a str> { // also OK
16+
Ok(())
17+
}
18+
19+
#[test]
20+
fn unit_3<T: Termination>() -> Result<(), T> { // nope (how would this get monomorphized?)
21+
//~^ ERROR return value of functions used as tests must either be `()` or implement
22+
Ok(())
23+
}
24+
25+
26+
struct Quux;
27+
28+
trait Bar {
29+
type Baz;
30+
31+
fn rah(&self) -> Self::Baz;
32+
}
33+
34+
impl Bar for Quux {
35+
type Baz = String;
36+
37+
fn rah(&self) -> Self::Baz {
38+
"rah".to_owned()
39+
}
40+
}
41+
42+
#[test]
43+
fn unit_4<T: Bar<Baz = String>>() -> <T as Bar>::Baz { // also nope
44+
//~^ ERROR return value of functions used as tests must either be `()` or implement
45+
let q = Quux{};
46+
<Quux as Bar>::rah(&q)
47+
}
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
error: return value of functions used as tests must either be `()` or implement `Termination`, and cannot use generics
2+
--> $DIR/test-fn-with-lifetime.rs:20:1
3+
|
4+
LL | / fn unit_3<T: Termination>() -> Result<(), T> { // nope (how would this get monomorphized?)
5+
LL | |
6+
LL | | Ok(())
7+
LL | | }
8+
| |_^
9+
10+
error: return value of functions used as tests must either be `()` or implement `Termination`, and cannot use generics
11+
--> $DIR/test-fn-with-lifetime.rs:43:1
12+
|
13+
LL | / fn unit_4<T: Bar<Baz = String>>() -> <T as Bar>::Baz { // also nope
14+
LL | |
15+
LL | | let q = Quux{};
16+
LL | | <Quux as Bar>::rah(&q)
17+
LL | | }
18+
| |_^
19+
20+
error: aborting due to 2 previous errors
21+

0 commit comments

Comments
 (0)