mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-01-24 23:16:48 +00:00
macros: improve error message for return type mismatch in #[tokio::main] (#7856)
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -3,3 +3,4 @@ Cargo.lock
|
||||
|
||||
.cargo/config.toml
|
||||
.cargo/config
|
||||
|
||||
|
||||
@@ -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
|
||||
|
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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,7 +532,6 @@ 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 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
|
||||
@@ -511,14 +539,35 @@ fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenSt
|
||||
syn::ReturnType::Default => quote! { () },
|
||||
syn::ReturnType::Type(_, ret_type) => quote! { #ret_type },
|
||||
};
|
||||
|
||||
let body = if is_test {
|
||||
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
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user