macros: improve error message for return type mismatch in #[tokio::main] (#7856)

This commit is contained in:
Chinmoy Das
2026-01-24 21:39:05 +05:30
committed by GitHub
parent 63abec0525
commit 8f6d0864c3
4 changed files with 127 additions and 7 deletions

1
.gitignore vendored
View File

@@ -3,3 +3,4 @@ Cargo.lock
.cargo/config.toml
.cargo/config

View File

@@ -1,3 +1,14 @@
error[E0271]: expected `{async block@$DIR/tests/fail/macros_type_mismatch.rs:3:1: 3:15}` to be a future that resolves to `()`, but it resolves to `Result<(), _>`
--> tests/fail/macros_type_mismatch.rs:3:1
|
3 | #[tokio::main]
| ^^^^^^^^^^^^^^ expected `()`, found `Result<(), _>`
|
= note: expected unit type `()`
found enum `Result<(), _>`
= note: required for the cast from `&{async block@$DIR/tests/fail/macros_type_mismatch.rs:3:1: 3:15}` to `&dyn Future<Output = ()>`
= note: this error originates in the attribute macro `tokio::main` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0308]: mismatched types
--> tests/fail/macros_type_mismatch.rs:5:5
|
@@ -15,6 +26,17 @@ help: consider using `Result::expect` to unwrap the `Result<(), _>` value, panic
5 | Ok(()).expect("REASON")
| +++++++++++++++++
error[E0271]: expected `{async block@$DIR/tests/fail/macros_type_mismatch.rs:8:1: 8:15}` to be a future that resolves to `()`, but it resolves to `Result<(), _>`
--> tests/fail/macros_type_mismatch.rs:8:1
|
8 | #[tokio::main]
| ^^^^^^^^^^^^^^ expected `()`, found `Result<(), _>`
|
= note: expected unit type `()`
found enum `Result<(), _>`
= note: required for the cast from `&{async block@$DIR/tests/fail/macros_type_mismatch.rs:8:1: 8:15}` to `&dyn Future<Output = ()>`
= note: this error originates in the attribute macro `tokio::main` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0308]: mismatched types
--> tests/fail/macros_type_mismatch.rs:10:5
|
@@ -32,6 +54,17 @@ help: consider using `Result::expect` to unwrap the `Result<(), _>` value, panic
10 | return Ok(());.expect("REASON")
| +++++++++++++++++
error[E0271]: expected `{async block@$DIR/tests/fail/macros_type_mismatch.rs:13:1: 13:15}` to be a future that resolves to `Result<(), ()>`, but it resolves to `()`
--> tests/fail/macros_type_mismatch.rs:13:1
|
13 | #[tokio::main]
| ^^^^^^^^^^^^^^ expected `Result<(), ()>`, found `()`
|
= note: expected enum `Result<(), ()>`
found unit type `()`
= note: required for the cast from `&{async block@$DIR/tests/fail/macros_type_mismatch.rs:13:1: 13:15}` to `&dyn Future<Output = Result<(), ()>>`
= note: this error originates in the attribute macro `tokio::main` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0308]: mismatched types
--> tests/fail/macros_type_mismatch.rs:23:5
|
@@ -58,6 +91,17 @@ error[E0277]: the `?` operator can only be used in an async block that returns `
40 | None?;
| ^ cannot use the `?` operator in an async block that returns `()`
error[E0271]: expected `{async block@$DIR/tests/fail/macros_type_mismatch.rs:38:1: 38:15}` to be a future that resolves to `Option<()>`, but it resolves to `()`
--> tests/fail/macros_type_mismatch.rs:38:1
|
38 | #[tokio::main]
| ^^^^^^^^^^^^^^ expected `Option<()>`, found `()`
|
= note: expected enum `Option<()>`
found unit type `()`
= note: required for the cast from `&{async block@$DIR/tests/fail/macros_type_mismatch.rs:38:1: 38:15}` to `&dyn Future<Output = Option<()>>`
= note: this error originates in the attribute macro `tokio::main` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0308]: mismatched types
--> tests/fail/macros_type_mismatch.rs:40:5
|
@@ -86,6 +130,17 @@ error[E0277]: the `?` operator can only be used in an async block that returns `
57 | Ok(())?;
| ^ cannot use the `?` operator in an async block that returns `()`
error[E0271]: expected `{async block@$DIR/tests/fail/macros_type_mismatch.rs:55:1: 55:15}` to be a future that resolves to `Result<(), ()>`, but it resolves to `()`
--> tests/fail/macros_type_mismatch.rs:55:1
|
55 | #[tokio::main]
| ^^^^^^^^^^^^^^ expected `Result<(), ()>`, found `()`
|
= note: expected enum `Result<(), ()>`
found unit type `()`
= note: required for the cast from `&{async block@$DIR/tests/fail/macros_type_mismatch.rs:55:1: 55:15}` to `&dyn Future<Output = Result<(), ()>>`
= note: this error originates in the attribute macro `tokio::main` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0308]: mismatched types
--> tests/fail/macros_type_mismatch.rs:57:5
|
@@ -102,6 +157,15 @@ help: try adding an expression at the end of the block
58 + Ok(())
|
error[E0271]: expected `{async block@$DIR/tests/fail/macros_type_mismatch.rs:63:1: 63:15}` to be a future that resolves to `()`, but it resolves to `{integer}`
--> tests/fail/macros_type_mismatch.rs:63:1
|
63 | #[tokio::main]
| ^^^^^^^^^^^^^^ expected `()`, found integer
|
= note: required for the cast from `&{async block@$DIR/tests/fail/macros_type_mismatch.rs:63:1: 63:15}` to `&dyn Future<Output = ()>`
= note: this error originates in the attribute macro `tokio::main` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0308]: mismatched types
--> tests/fail/macros_type_mismatch.rs:66:5
|

View File

@@ -10,8 +10,14 @@ async fn impl_trait() -> impl Iterator<Item = impl core::fmt::Debug> {
[()].into_iter()
}
#[tokio::main]
async fn impl_trait2() -> Result<(), impl core::fmt::Debug> {
Err(())
}
fn main() {
if impl_trait().count() == 10 {
never();
}
let _ = impl_trait2();
}

View File

@@ -293,6 +293,35 @@ fn parse_bool(bool: syn::Lit, span: Span, field: &str) -> Result<bool, syn::Erro
}
}
fn contains_impl_trait(ty: &syn::Type) -> bool {
match ty {
syn::Type::ImplTrait(_) => true,
syn::Type::Array(t) => contains_impl_trait(&t.elem),
syn::Type::Ptr(t) => contains_impl_trait(&t.elem),
syn::Type::Reference(t) => contains_impl_trait(&t.elem),
syn::Type::Slice(t) => contains_impl_trait(&t.elem),
syn::Type::Tuple(t) => t.elems.iter().any(contains_impl_trait),
syn::Type::Paren(t) => contains_impl_trait(&t.elem),
syn::Type::Group(t) => contains_impl_trait(&t.elem),
syn::Type::Path(t) => match t.path.segments.last() {
Some(segment) => match &segment.arguments {
syn::PathArguments::AngleBracketed(args) => args.args.iter().any(|arg| match arg {
syn::GenericArgument::Type(t) => contains_impl_trait(t),
syn::GenericArgument::AssocType(t) => contains_impl_trait(&t.ty),
_ => false,
}),
syn::PathArguments::Parenthesized(args) => {
args.inputs.iter().any(contains_impl_trait)
|| matches!(&args.output, syn::ReturnType::Type(_, t) if contains_impl_trait(t))
}
syn::PathArguments::None => false,
},
None => false,
},
_ => false,
}
}
fn build_config(
input: &ItemFn,
args: AttributeArgs,
@@ -503,22 +532,42 @@ fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenSt
//
// We don't do this for the main function as it should only be used once so
// there will be no benefit.
let output_type = match &input.sig.output {
// For functions with no return value syn doesn't print anything,
// but that doesn't work as `Output` for our boxed `Future`, so
// default to `()` (the same type as the function output).
syn::ReturnType::Default => quote! { () },
syn::ReturnType::Type(_, ret_type) => quote! { #ret_type },
};
let body = if is_test {
let output_type = match &input.sig.output {
// For functions with no return value syn doesn't print anything,
// but that doesn't work as `Output` for our boxed `Future`, so
// default to `()` (the same type as the function output).
syn::ReturnType::Default => quote! { () },
syn::ReturnType::Type(_, ret_type) => quote! { #ret_type },
};
quote! {
let body = async #body;
#crate_path::pin!(body);
let body: ::core::pin::Pin<&mut dyn ::core::future::Future<Output = #output_type>> = body;
}
} else {
// force typecheck without runtime overhead
let check_block = match &input.sig.output {
syn::ReturnType::Type(_, t)
if matches!(**t, syn::Type::Never(_)) || contains_impl_trait(t) =>
{
quote! {}
}
_ => quote! {
if false {
let _: &dyn ::core::future::Future<Output = #output_type> = &body;
}
},
};
quote! {
let body = async #body;
// Compile-time assertion that the future's output matches the return type.
let body = {
#check_block
body
};
}
};