From d5f5c74f4126fa26b51ec6c040a3a9ae60f6088b Mon Sep 17 00:00:00 2001 From: Nur Date: Wed, 13 Nov 2024 01:30:03 +0600 Subject: [PATCH 01/15] macros: Improve IDE support --- tokio-macros/src/entry.rs | 161 ++++++++------------------------------ 1 file changed, 33 insertions(+), 128 deletions(-) diff --git a/tokio-macros/src/entry.rs b/tokio-macros/src/entry.rs index 184718784e7..0b0319d1251 100644 --- a/tokio-macros/src/entry.rs +++ b/tokio-macros/src/entry.rs @@ -1,7 +1,8 @@ -use proc_macro2::{Span, TokenStream, TokenTree}; -use quote::{quote, quote_spanned, ToTokens}; +use proc_macro2::{Span, TokenStream}; +use quote::{quote, ToTokens}; use syn::parse::{Parse, ParseStream, Parser}; -use syn::{braced, Attribute, Ident, Path, Signature, Visibility}; +use syn::token::Brace; +use syn::{Attribute, Ident, Path, Signature, Visibility}; // syn::AttributeArgs does not implement syn::Parse type AttributeArgs = syn::punctuated::Punctuated; @@ -392,41 +393,28 @@ fn build_config( fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenStream { input.sig.asyncness = None; - // If type mismatch occurs, the current rustc points to the last statement. - let (last_stmt_start_span, last_stmt_end_span) = { - let mut last_stmt = input.stmts.last().cloned().unwrap_or_default().into_iter(); - - // `Span` on stable Rust has a limitation that only points to the first - // token, not the whole tokens. We can work around this limitation by - // using the first/last span of the tokens like - // `syn::Error::new_spanned` does. - let start = last_stmt.next().map_or_else(Span::call_site, |t| t.span()); - let end = last_stmt.last().map_or(start, |t| t.span()); - (start, end) - }; - let crate_path = config .crate_name .map(ToTokens::into_token_stream) - .unwrap_or_else(|| Ident::new("tokio", last_stmt_start_span).into_token_stream()); + .unwrap_or_else(|| Ident::new("tokio", Span::call_site()).into_token_stream()); let mut rt = match config.flavor { - RuntimeFlavor::CurrentThread => quote_spanned! {last_stmt_start_span=> + RuntimeFlavor::CurrentThread => quote! { #crate_path::runtime::Builder::new_current_thread() }, - RuntimeFlavor::Threaded => quote_spanned! {last_stmt_start_span=> + RuntimeFlavor::Threaded => quote! { #crate_path::runtime::Builder::new_multi_thread() }, }; if let Some(v) = config.worker_threads { - rt = quote_spanned! {last_stmt_start_span=> #rt.worker_threads(#v) }; + rt = quote! { #rt.worker_threads(#v) }; } if let Some(v) = config.start_paused { - rt = quote_spanned! {last_stmt_start_span=> #rt.start_paused(#v) }; + rt = quote! { #rt.start_paused(#v) }; } if let Some(v) = config.unhandled_panic { let unhandled_panic = v.into_tokens(&crate_path); - rt = quote_spanned! {last_stmt_start_span=> #rt.unhandled_panic(#unhandled_panic) }; + rt = quote! { #rt.unhandled_panic(#unhandled_panic) }; } let generated_attrs = if is_test { @@ -439,7 +427,7 @@ fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenSt let body_ident = quote! { body }; // This explicit `return` is intentional. See tokio-rs/tokio#4636 - let last_block = quote_spanned! {last_stmt_end_span=> + let last_block = quote! { #[allow(clippy::expect_used, clippy::diverging_sub_expression, clippy::needless_return)] { return #rt @@ -450,7 +438,7 @@ fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenSt } }; - let body = input.body(); + let body = input.body; // For test functions pin the body to the stack and use `Pin<&mut dyn // Future>` to reduce the amount of `Runtime::block_on` (and related @@ -461,7 +449,7 @@ 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 { + input.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 @@ -479,8 +467,7 @@ fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenSt let body = async #body; } }; - - input.into_tokens(generated_attrs, body, last_block) + input.into_tokens(generated_attrs, last_block) } fn token_stream_with_error(mut tokens: TokenStream, error: syn::Error) -> TokenStream { @@ -549,7 +536,8 @@ pub(crate) fn test(args: TokenStream, item: TokenStream, rt_multi_thread: bool) Ok(it) => it, Err(e) => return token_stream_with_error(item, e), }; - let config = if let Some(attr) = input.attrs().find(|attr| is_test_attribute(attr)) { + + let config = if let Some(attr) = input.attrs.iter().find(|attr| is_test_attribute(attr)) { let msg = "second test attribute is supplied, consider removing or changing the order of your test attributes"; Err(syn::Error::new_spanned(attr, msg)) } else { @@ -565,126 +553,43 @@ pub(crate) fn test(args: TokenStream, item: TokenStream, rt_multi_thread: bool) } struct ItemFn { - outer_attrs: Vec, + attrs: Vec, vis: Visibility, sig: Signature, - brace_token: syn::token::Brace, - inner_attrs: Vec, - stmts: Vec, + body: proc_macro2::TokenStream, } -impl ItemFn { - /// Access all attributes of the function item. - fn attrs(&self) -> impl Iterator { - self.outer_attrs.iter().chain(self.inner_attrs.iter()) - } - - /// Get the body of the function item in a manner so that it can be - /// conveniently used with the `quote!` macro. - fn body(&self) -> Body<'_> { - Body { - brace_token: self.brace_token, - stmts: &self.stmts, - } +impl Parse for ItemFn { + fn parse(input: ParseStream<'_>) -> syn::Result { + Ok(Self { + attrs: input.call(Attribute::parse_outer)?, + vis: input.parse()?, + sig: input.parse()?, + body: input.parse()?, + }) } +} +impl ItemFn { /// Convert our local function item into a token stream. fn into_tokens( self, generated_attrs: proc_macro2::TokenStream, - body: proc_macro2::TokenStream, last_block: proc_macro2::TokenStream, ) -> TokenStream { - let mut tokens = proc_macro2::TokenStream::new(); + let mut tokens = generated_attrs; // Outer attributes are simply streamed as-is. - for attr in self.outer_attrs { - attr.to_tokens(&mut tokens); - } - - // Inner attributes require extra care, since they're not supported on - // blocks (which is what we're expanded into) we instead lift them - // outside of the function. This matches the behavior of `syn`. - for mut attr in self.inner_attrs { - attr.style = syn::AttrStyle::Outer; + for attr in self.attrs { attr.to_tokens(&mut tokens); } - - // Add generated macros at the end, so macros processed later are aware of them. - generated_attrs.to_tokens(&mut tokens); - self.vis.to_tokens(&mut tokens); self.sig.to_tokens(&mut tokens); - self.brace_token.surround(&mut tokens, |tokens| { - body.to_tokens(tokens); + Brace::default().surround(&mut tokens, |tokens| { + // Note: To add `TokenStream`, we need to use `Some(self.body)` instead of `self.body`. + tokens.extend(Some(self.body)); last_block.to_tokens(tokens); }); - tokens } } - -impl Parse for ItemFn { - #[inline] - fn parse(input: ParseStream<'_>) -> syn::Result { - // This parse implementation has been largely lifted from `syn`, with - // the exception of: - // * We don't have access to the plumbing necessary to parse inner - // attributes in-place. - // * We do our own statements parsing to avoid recursively parsing - // entire statements and only look for the parts we're interested in. - - let outer_attrs = input.call(Attribute::parse_outer)?; - let vis: Visibility = input.parse()?; - let sig: Signature = input.parse()?; - - let content; - let brace_token = braced!(content in input); - let inner_attrs = Attribute::parse_inner(&content)?; - - let mut buf = proc_macro2::TokenStream::new(); - let mut stmts = Vec::new(); - - while !content.is_empty() { - if let Some(semi) = content.parse::>()? { - semi.to_tokens(&mut buf); - stmts.push(buf); - buf = proc_macro2::TokenStream::new(); - continue; - } - - // Parse a single token tree and extend our current buffer with it. - // This avoids parsing the entire content of the sub-tree. - buf.extend([content.parse::()?]); - } - - if !buf.is_empty() { - stmts.push(buf); - } - - Ok(Self { - outer_attrs, - vis, - sig, - brace_token, - inner_attrs, - stmts, - }) - } -} - -struct Body<'a> { - brace_token: syn::token::Brace, - // Statements, with terminating `;`. - stmts: &'a [TokenStream], -} - -impl ToTokens for Body<'_> { - fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { - self.brace_token.surround(tokens, |tokens| { - for stmt in self.stmts { - stmt.to_tokens(tokens); - } - }); - } -} From e7518d063d9c614e8eeb5f20dee1f35b7a02d155 Mon Sep 17 00:00:00 2001 From: Nur Date: Wed, 13 Nov 2024 18:24:30 +0600 Subject: [PATCH 02/15] macros: Improve IDE support --- tests-build/tests/macros_clippy.rs | 7 ------- tokio-macros/src/entry.rs | 10 +++------- 2 files changed, 3 insertions(+), 14 deletions(-) delete mode 100644 tests-build/tests/macros_clippy.rs diff --git a/tests-build/tests/macros_clippy.rs b/tests-build/tests/macros_clippy.rs deleted file mode 100644 index 0f3f4bb0b8b..00000000000 --- a/tests-build/tests/macros_clippy.rs +++ /dev/null @@ -1,7 +0,0 @@ -#[cfg(feature = "full")] -#[tokio::test] -async fn test_with_semicolon_without_return_type() { - #![deny(clippy::semicolon_if_nothing_returned)] - - dbg!(0); -} diff --git a/tokio-macros/src/entry.rs b/tokio-macros/src/entry.rs index 0b0319d1251..2d4f6fd8b43 100644 --- a/tokio-macros/src/entry.rs +++ b/tokio-macros/src/entry.rs @@ -556,7 +556,7 @@ struct ItemFn { attrs: Vec, vis: Visibility, sig: Signature, - body: proc_macro2::TokenStream, + body: TokenStream, } impl Parse for ItemFn { @@ -572,11 +572,7 @@ impl Parse for ItemFn { impl ItemFn { /// Convert our local function item into a token stream. - fn into_tokens( - self, - generated_attrs: proc_macro2::TokenStream, - last_block: proc_macro2::TokenStream, - ) -> TokenStream { + fn into_tokens(self, generated_attrs: TokenStream, last_block: TokenStream) -> TokenStream { let mut tokens = generated_attrs; // Outer attributes are simply streamed as-is. for attr in self.attrs { @@ -588,7 +584,7 @@ impl ItemFn { Brace::default().surround(&mut tokens, |tokens| { // Note: To add `TokenStream`, we need to use `Some(self.body)` instead of `self.body`. tokens.extend(Some(self.body)); - last_block.to_tokens(tokens); + tokens.extend(Some(last_block)); }); tokens } From dae1ff7ef69093ba1404902540c5608922536ee0 Mon Sep 17 00:00:00 2001 From: Nur Date: Wed, 13 Nov 2024 21:16:45 +0600 Subject: [PATCH 03/15] fix: Revert span usage to restore type suggestions --- .../tests/fail/macros_invalid_input.stderr | 14 +++-- .../tests/fail/macros_type_mismatch.rs | 8 --- .../tests/fail/macros_type_mismatch.stderr | 14 ++--- tests-build/tests/macros.rs | 4 +- tokio-macros/src/entry.rs | 52 +++++++++++++++---- 5 files changed, 61 insertions(+), 31 deletions(-) diff --git a/tests-build/tests/fail/macros_invalid_input.stderr b/tests-build/tests/fail/macros_invalid_input.stderr index e6e80e8114a..31ba797c5e4 100644 --- a/tests-build/tests/fail/macros_invalid_input.stderr +++ b/tests-build/tests/fail/macros_invalid_input.stderr @@ -112,10 +112,14 @@ error: second test attribute is supplied, consider removing or changing the orde 64 | #[std::prelude::rust_2021::test] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: second test attribute is supplied, consider removing or changing the order of your test attributes - --> tests/fail/macros_invalid_input.rs:67:1 +error: duplicated attribute + --> tests/fail/macros_invalid_input.rs:48:1 + | +48 | #[test] + | ^^^^^^^ | -67 | #[tokio::test] - | ^^^^^^^^^^^^^^ +note: the lint level is defined here + --> tests/fail/macros_invalid_input.rs:1:9 | - = note: this error originates in the attribute macro `tokio::test` (in Nightly builds, run with -Z macro-backtrace for more info) +1 | #![deny(duplicate_macro_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests-build/tests/fail/macros_type_mismatch.rs b/tests-build/tests/fail/macros_type_mismatch.rs index c292ee68f66..15d70770983 100644 --- a/tests-build/tests/fail/macros_type_mismatch.rs +++ b/tests-build/tests/fail/macros_type_mismatch.rs @@ -12,14 +12,6 @@ async fn missing_return_type() { #[tokio::main] async fn extra_semicolon() -> Result<(), ()> { - /* TODO(taiki-e): help message still wrong - help: try using a variant of the expected enum - | - 23 | Ok(Ok(());) - | - 23 | Err(Ok(());) - | - */ Ok(()); } diff --git a/tests-build/tests/fail/macros_type_mismatch.stderr b/tests-build/tests/fail/macros_type_mismatch.stderr index 579c241559b..2105136347c 100644 --- a/tests-build/tests/fail/macros_type_mismatch.stderr +++ b/tests-build/tests/fail/macros_type_mismatch.stderr @@ -43,17 +43,17 @@ error[E0308]: mismatched types | = note: expected enum `Result<(), ()>` found unit type `()` -help: try adding an expression at the end of the block - | -23 ~ Ok(());; -24 + Ok(()) +help: try wrapping the expression in a variant of `Result` | +23 | Ok(Ok(());) + | +++ + +23 | Err(Ok(());) + | ++++ + error[E0308]: mismatched types - --> tests/fail/macros_type_mismatch.rs:32:5 + --> tests/fail/macros_type_mismatch.rs:31:5 | 30 | async fn issue_4635() { | - help: try adding a return type: `-> i32` 31 | return 1; -32 | ; - | ^ expected `()`, found integer + | ^^^^^^^^^ expected `()`, found integer diff --git a/tests-build/tests/macros.rs b/tests-build/tests/macros.rs index 8de08125d30..b1cd774ea53 100644 --- a/tests-build/tests/macros.rs +++ b/tests-build/tests/macros.rs @@ -13,10 +13,10 @@ fn compile_fail_full() { t.pass("tests/pass/macros_main_loop.rs"); #[cfg(feature = "full")] - t.compile_fail("tests/fail/macros_invalid_input.rs"); + t.pass("tests/fail/macros_dead_code.rs"); #[cfg(feature = "full")] - t.compile_fail("tests/fail/macros_dead_code.rs"); + t.compile_fail("tests/fail/macros_invalid_input.rs"); #[cfg(feature = "full")] t.compile_fail("tests/fail/macros_type_mismatch.rs"); diff --git a/tokio-macros/src/entry.rs b/tokio-macros/src/entry.rs index 2d4f6fd8b43..b0946e8066c 100644 --- a/tokio-macros/src/entry.rs +++ b/tokio-macros/src/entry.rs @@ -1,5 +1,5 @@ -use proc_macro2::{Span, TokenStream}; -use quote::{quote, ToTokens}; +use proc_macro2::{Span, TokenStream, TokenTree}; +use quote::{quote, quote_spanned, ToTokens}; use syn::parse::{Parse, ParseStream, Parser}; use syn::token::Brace; use syn::{Attribute, Ident, Path, Signature, Visibility}; @@ -393,28 +393,62 @@ fn build_config( fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenStream { input.sig.asyncness = None; + // If type mismatch occurs, the current rustc points to the last statement. + let (last_stmt_start_span, last_stmt_end_span) = { + // `Span` on stable Rust has a limitation that only points to the first + // token, not the whole tokens. We can work around this limitation by + // using the first/last span of the tokens like + // `syn::Error::new_spanned` does. + let mut start = Span::call_site(); + let mut end = Span::call_site(); + + let TokenTree::Group(group) = input.body.clone().into_iter().next().unwrap() else { + unreachable!() + }; + let mut stream = group.stream().into_iter(); + while let Some(tt) = stream.next() { + if matches!(&tt, TokenTree::Punct(p) if p.as_char() == ';') { + continue; + } + + start = tt.span(); + end = tt.span(); + + while let Some(tt) = stream.next() { + match tt { + TokenTree::Punct(p) if p.as_char() == ';' => { + end = p.span(); + break; + } + tt => end = tt.span(), + } + } + } + (start, end) + }; + let crate_path = config .crate_name .map(ToTokens::into_token_stream) - .unwrap_or_else(|| Ident::new("tokio", Span::call_site()).into_token_stream()); + .unwrap_or_else(|| Ident::new("tokio", last_stmt_start_span).into_token_stream()); let mut rt = match config.flavor { - RuntimeFlavor::CurrentThread => quote! { + RuntimeFlavor::CurrentThread => quote_spanned! {last_stmt_start_span=> #crate_path::runtime::Builder::new_current_thread() }, - RuntimeFlavor::Threaded => quote! { + RuntimeFlavor::Threaded => quote_spanned! {last_stmt_start_span=> #crate_path::runtime::Builder::new_multi_thread() }, }; if let Some(v) = config.worker_threads { - rt = quote! { #rt.worker_threads(#v) }; + rt = quote_spanned! {last_stmt_start_span=> #rt.worker_threads(#v) }; } if let Some(v) = config.start_paused { - rt = quote! { #rt.start_paused(#v) }; + rt = quote_spanned! {last_stmt_start_span=> #rt.start_paused(#v) }; } if let Some(v) = config.unhandled_panic { let unhandled_panic = v.into_tokens(&crate_path); - rt = quote! { #rt.unhandled_panic(#unhandled_panic) }; + rt = quote_spanned! {last_stmt_start_span=> #rt.unhandled_panic(#unhandled_panic) }; } let generated_attrs = if is_test { @@ -427,7 +461,7 @@ fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenSt let body_ident = quote! { body }; // This explicit `return` is intentional. See tokio-rs/tokio#4636 - let last_block = quote! { + let last_block = quote_spanned! {last_stmt_end_span=> #[allow(clippy::expect_used, clippy::diverging_sub_expression, clippy::needless_return)] { return #rt From 642b01fafdccb3b69574718ab71db1821938438f Mon Sep 17 00:00:00 2001 From: Nur Date: Wed, 13 Nov 2024 21:23:48 +0600 Subject: [PATCH 04/15] clippy fix --- .../tests/fail/macros_type_mismatch.stderr | 15 +++++++-------- tokio-macros/src/entry.rs | 2 +- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/tests-build/tests/fail/macros_type_mismatch.stderr b/tests-build/tests/fail/macros_type_mismatch.stderr index 2105136347c..46ba4e01cd6 100644 --- a/tests-build/tests/fail/macros_type_mismatch.stderr +++ b/tests-build/tests/fail/macros_type_mismatch.stderr @@ -33,27 +33,26 @@ help: consider using `Result::expect` to unwrap the `Result<(), _>` value, panic | +++++++++++++++++ error[E0308]: mismatched types - --> tests/fail/macros_type_mismatch.rs:23:5 + --> tests/fail/macros_type_mismatch.rs:15:5 | 14 | async fn extra_semicolon() -> Result<(), ()> { | -------------- expected `Result<(), ()>` because of return type -... -23 | Ok(()); +15 | Ok(()); | ^^^^^^^ expected `Result<(), ()>`, found `()` | = note: expected enum `Result<(), ()>` found unit type `()` help: try wrapping the expression in a variant of `Result` | -23 | Ok(Ok(());) +15 | Ok(Ok(());) | +++ + -23 | Err(Ok(());) +15 | Err(Ok(());) | ++++ + error[E0308]: mismatched types - --> tests/fail/macros_type_mismatch.rs:31:5 + --> tests/fail/macros_type_mismatch.rs:23:5 | -30 | async fn issue_4635() { +22 | async fn issue_4635() { | - help: try adding a return type: `-> i32` -31 | return 1; +23 | return 1; | ^^^^^^^^^ expected `()`, found integer diff --git a/tokio-macros/src/entry.rs b/tokio-macros/src/entry.rs index b0946e8066c..ded46b0ee13 100644 --- a/tokio-macros/src/entry.rs +++ b/tokio-macros/src/entry.rs @@ -414,7 +414,7 @@ fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenSt start = tt.span(); end = tt.span(); - while let Some(tt) = stream.next() { + for tt in stream.by_ref() { match tt { TokenTree::Punct(p) if p.as_char() == ';' => { end = p.span(); From 9b02d37c8d6d5a8214a302a1d9691693be42cea4 Mon Sep 17 00:00:00 2001 From: Nur Date: Wed, 13 Nov 2024 22:19:05 +0600 Subject: [PATCH 05/15] macro: ignore `;` span --- .../tests/fail/macros_type_mismatch.rs | 5 +++ .../tests/fail/macros_type_mismatch.stderr | 39 +++++++++++------ tokio-macros/src/entry.rs | 42 +++++++++++-------- 3 files changed, 56 insertions(+), 30 deletions(-) diff --git a/tests-build/tests/fail/macros_type_mismatch.rs b/tests-build/tests/fail/macros_type_mismatch.rs index 15d70770983..21fb2d979be 100644 --- a/tests-build/tests/fail/macros_type_mismatch.rs +++ b/tests-build/tests/fail/macros_type_mismatch.rs @@ -15,6 +15,11 @@ async fn extra_semicolon() -> Result<(), ()> { Ok(()); } +#[tokio::main] +async fn invalid_return_type() -> Option<()> { + () +} + // https://github.com/tokio-rs/tokio/issues/4635 #[allow(redundant_semicolons)] #[rustfmt::skip] diff --git a/tests-build/tests/fail/macros_type_mismatch.stderr b/tests-build/tests/fail/macros_type_mismatch.stderr index 46ba4e01cd6..9f2a0254f5a 100644 --- a/tests-build/tests/fail/macros_type_mismatch.stderr +++ b/tests-build/tests/fail/macros_type_mismatch.stderr @@ -19,7 +19,7 @@ error[E0308]: mismatched types --> tests/fail/macros_type_mismatch.rs:10:5 | 10 | return Ok(()); - | ^^^^^^^^^^^^^^ expected `()`, found `Result<(), _>` + | ^^^^^^^^^^^^^ expected `()`, found `Result<(), _>` | = note: expected unit type `()` found enum `Result<(), _>` @@ -29,8 +29,8 @@ help: a return type might be missing here | ++++ help: consider using `Result::expect` to unwrap the `Result<(), _>` value, panicking if the value is a `Result::Err` | -10 | return Ok(());.expect("REASON") - | +++++++++++++++++ +10 | return Ok(()).expect("REASON"); + | +++++++++++++++++ error[E0308]: mismatched types --> tests/fail/macros_type_mismatch.rs:15:5 @@ -38,21 +38,36 @@ error[E0308]: mismatched types 14 | async fn extra_semicolon() -> Result<(), ()> { | -------------- expected `Result<(), ()>` because of return type 15 | Ok(()); - | ^^^^^^^ expected `Result<(), ()>`, found `()` + | ^^^^^^ expected `Result<(), ()>`, found `()` | = note: expected enum `Result<(), ()>` found unit type `()` help: try wrapping the expression in a variant of `Result` | -15 | Ok(Ok(());) - | +++ + -15 | Err(Ok(());) - | ++++ + +15 | Ok(Ok(())); + | +++ + +15 | Err(Ok(())); + | ++++ + error[E0308]: mismatched types - --> tests/fail/macros_type_mismatch.rs:23:5 + --> tests/fail/macros_type_mismatch.rs:20:5 | -22 | async fn issue_4635() { +19 | async fn invalid_return_type() -> Option<()> { + | ---------- expected `Option<()>` because of return type +20 | () + | ^^ expected `Option<()>`, found `()` + | + = note: expected enum `Option<()>` + found unit type `()` +help: try wrapping the expression in `Some` + | +20 | Some(()) + | +++++ + + +error[E0308]: mismatched types + --> tests/fail/macros_type_mismatch.rs:28:5 + | +27 | async fn issue_4635() { | - help: try adding a return type: `-> i32` -23 | return 1; - | ^^^^^^^^^ expected `()`, found integer +28 | return 1; + | ^^^^^^^^ expected `()`, found integer diff --git a/tokio-macros/src/entry.rs b/tokio-macros/src/entry.rs index ded46b0ee13..b83a2271f0c 100644 --- a/tokio-macros/src/entry.rs +++ b/tokio-macros/src/entry.rs @@ -402,25 +402,31 @@ fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenSt let mut start = Span::call_site(); let mut end = Span::call_site(); - let TokenTree::Group(group) = input.body.clone().into_iter().next().unwrap() else { - unreachable!() - }; - let mut stream = group.stream().into_iter(); - while let Some(tt) = stream.next() { - if matches!(&tt, TokenTree::Punct(p) if p.as_char() == ';') { - continue; - } - - start = tt.span(); - end = tt.span(); - - for tt in stream.by_ref() { - match tt { - TokenTree::Punct(p) if p.as_char() == ';' => { - end = p.span(); - break; + if let Some(tt) = input.body.clone().into_iter().last() { + match tt { + TokenTree::Group(group) => { + let mut stream = group.stream().into_iter(); + while let Some(tt) = stream.next() { + if matches!(&tt, TokenTree::Punct(p) if p.as_char() == ';') { + continue; + } + start = tt.span(); + end = tt.span(); + + for tt in stream.by_ref() { + match tt { + TokenTree::Punct(p) if p.as_char() == ';' => { + // end = p.span(); + break; + } + tt => end = tt.span(), + } + } } - tt => end = tt.span(), + } + _ => { + start = tt.span(); + end = tt.span(); } } } From 550986001a702f806985be8f86b7384e8ac16f56 Mon Sep 17 00:00:00 2001 From: Nur Date: Fri, 15 Nov 2024 05:26:57 +0600 Subject: [PATCH 06/15] macros: improve type diagnostics --- .../tests/fail/macros_invalid_input.stderr | 14 +++ .../tests/fail/macros_type_mismatch.rs | 5 -- .../tests/fail/macros_type_mismatch.stderr | 61 +++---------- tokio-macros/src/entry.rs | 89 +++++++++++++++++-- 4 files changed, 107 insertions(+), 62 deletions(-) diff --git a/tests-build/tests/fail/macros_invalid_input.stderr b/tests-build/tests/fail/macros_invalid_input.stderr index 31ba797c5e4..b7a02d3f75b 100644 --- a/tests-build/tests/fail/macros_invalid_input.stderr +++ b/tests-build/tests/fail/macros_invalid_input.stderr @@ -123,3 +123,17 @@ note: the lint level is defined here | 1 | #![deny(duplicate_macro_attributes)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error[E0277]: `()` is not a future + --> tests/fail/macros_invalid_input.rs:5:1 + | +5 | #[tokio::main] + | ^^^^^^^^^^^^^^ `()` is not a future + | + = help: the trait `Future` is not implemented for `()` +note: required by a bound in `tests_build::tokio::runtime::Runtime::block_on` + --> $WORKSPACE/tokio/src/runtime/runtime.rs + | + | pub fn block_on(&self, future: F) -> F::Output { + | ^^^^^^ required by this bound in `Runtime::block_on` + = note: this error originates in the attribute macro `tokio::main` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests-build/tests/fail/macros_type_mismatch.rs b/tests-build/tests/fail/macros_type_mismatch.rs index 21fb2d979be..15d70770983 100644 --- a/tests-build/tests/fail/macros_type_mismatch.rs +++ b/tests-build/tests/fail/macros_type_mismatch.rs @@ -15,11 +15,6 @@ async fn extra_semicolon() -> Result<(), ()> { Ok(()); } -#[tokio::main] -async fn invalid_return_type() -> Option<()> { - () -} - // https://github.com/tokio-rs/tokio/issues/4635 #[allow(redundant_semicolons)] #[rustfmt::skip] diff --git a/tests-build/tests/fail/macros_type_mismatch.stderr b/tests-build/tests/fail/macros_type_mismatch.stderr index 9f2a0254f5a..2d5af0dd65c 100644 --- a/tests-build/tests/fail/macros_type_mismatch.stderr +++ b/tests-build/tests/fail/macros_type_mismatch.stderr @@ -6,68 +6,33 @@ error[E0308]: mismatched types | = note: expected unit type `()` found enum `Result<(), _>` -help: a return type might be missing here - | -4 | async fn missing_semicolon_or_return_type() -> _ { - | ++++ -help: consider using `Result::expect` to unwrap the `Result<(), _>` value, panicking if the value is a `Result::Err` - | -5 | Ok(()).expect("REASON") - | +++++++++++++++++ error[E0308]: mismatched types - --> tests/fail/macros_type_mismatch.rs:10:5 + --> tests/fail/macros_type_mismatch.rs:10:12 | 10 | return Ok(()); - | ^^^^^^^^^^^^^ expected `()`, found `Result<(), _>` + | ^^^^^^ expected `()`, found `Result<(), _>` | = note: expected unit type `()` found enum `Result<(), _>` -help: a return type might be missing here - | -9 | async fn missing_return_type() -> _ { - | ++++ -help: consider using `Result::expect` to unwrap the `Result<(), _>` value, panicking if the value is a `Result::Err` - | -10 | return Ok(()).expect("REASON"); - | +++++++++++++++++ error[E0308]: mismatched types - --> tests/fail/macros_type_mismatch.rs:15:5 + --> tests/fail/macros_type_mismatch.rs:14:46 | -14 | async fn extra_semicolon() -> Result<(), ()> { - | -------------- expected `Result<(), ()>` because of return type -15 | Ok(()); - | ^^^^^^ expected `Result<(), ()>`, found `()` +14 | async fn extra_semicolon() -> Result<(), ()> { + | ______________________________________________^ +15 | | Ok(()); + | | - help: remove this semicolon to return this value +16 | | } + | |_^ expected `Result<(), ()>`, found `()` | = note: expected enum `Result<(), ()>` found unit type `()` -help: try wrapping the expression in a variant of `Result` - | -15 | Ok(Ok(())); - | +++ + -15 | Err(Ok(())); - | ++++ + - -error[E0308]: mismatched types - --> tests/fail/macros_type_mismatch.rs:20:5 - | -19 | async fn invalid_return_type() -> Option<()> { - | ---------- expected `Option<()>` because of return type -20 | () - | ^^ expected `Option<()>`, found `()` - | - = note: expected enum `Option<()>` - found unit type `()` -help: try wrapping the expression in `Some` - | -20 | Some(()) - | +++++ + error[E0308]: mismatched types - --> tests/fail/macros_type_mismatch.rs:28:5 + --> tests/fail/macros_type_mismatch.rs:23:12 | -27 | async fn issue_4635() { +22 | async fn issue_4635() { | - help: try adding a return type: `-> i32` -28 | return 1; - | ^^^^^^^^ expected `()`, found integer +23 | return 1; + | ^ expected `()`, found integer diff --git a/tokio-macros/src/entry.rs b/tokio-macros/src/entry.rs index b83a2271f0c..dd8bc44fb49 100644 --- a/tokio-macros/src/entry.rs +++ b/tokio-macros/src/entry.rs @@ -390,8 +390,83 @@ fn build_config( config.build() } +fn fn_without_args(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenStream { + let async_keyword = input.sig.asyncness.take(); + let fn_sig = &input.sig; + let fn_name = &input.sig.ident; + + let crate_path = config + .crate_name + .map(ToTokens::into_token_stream) + .unwrap_or_else(|| Ident::new("tokio", Span::call_site()).into_token_stream()); + + let mut rt = match config.flavor { + RuntimeFlavor::CurrentThread => quote! { + #crate_path::runtime::Builder::new_current_thread() + }, + RuntimeFlavor::Threaded => quote! { + #crate_path::runtime::Builder::new_multi_thread() + }, + }; + if let Some(v) = config.worker_threads { + rt = quote! { #rt.worker_threads(#v) }; + } + if let Some(v) = config.start_paused { + rt = quote! { #rt.start_paused(#v) }; + } + if let Some(v) = config.unhandled_panic { + let unhandled_panic = v.into_tokens(&crate_path); + rt = quote! { #rt.unhandled_panic(#unhandled_panic) }; + } + + let generated_attrs = if is_test { + quote! { #[::core::prelude::v1::test] } + } else { + quote! {} + }; + + // This explicit `return` is intentional. See tokio-rs/tokio#4636 + let last_block = quote! { + #[allow(clippy::expect_used, clippy::diverging_sub_expression, clippy::needless_return)] + { + return #rt + .enable_all() + .build() + .expect("Failed building the Runtime") + .block_on(body); + } + }; + + let body = input.body; + + input.body = if is_test { + let output_type = match &input.sig.output { + syn::ReturnType::Default => quote! { () }, + syn::ReturnType::Type(_, ret_type) => quote! { #ret_type }, + }; + quote! { + #async_keyword #fn_sig #body + let body = #fn_name(); + + #crate_path::pin!(body); + let body: ::core::pin::Pin<&mut dyn ::core::future::Future> = body; + } + } else { + quote! { + #async_keyword #fn_sig #body + let body = #fn_name(); + } + }; + + input.into_tokens(generated_attrs, last_block) +} + fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenStream { - input.sig.asyncness = None; + if input.sig.inputs.is_empty() { + return fn_without_args(input, is_test, config); + } + + let async_keyword = input.sig.asyncness.take(); // If type mismatch occurs, the current rustc points to the last statement. let (last_stmt_start_span, last_stmt_end_span) = { @@ -415,10 +490,7 @@ fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenSt for tt in stream.by_ref() { match tt { - TokenTree::Punct(p) if p.as_char() == ';' => { - // end = p.span(); - break; - } + TokenTree::Punct(p) if p.as_char() == ';' => break, tt => end = tt.span(), } } @@ -465,7 +537,6 @@ fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenSt quote! {} }; - let body_ident = quote! { body }; // This explicit `return` is intentional. See tokio-rs/tokio#4636 let last_block = quote_spanned! {last_stmt_end_span=> #[allow(clippy::expect_used, clippy::diverging_sub_expression, clippy::needless_return)] @@ -474,7 +545,7 @@ fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenSt .enable_all() .build() .expect("Failed building the Runtime") - .block_on(#body_ident); + .block_on(body); } }; @@ -498,13 +569,13 @@ fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenSt syn::ReturnType::Type(_, ret_type) => quote! { #ret_type }, }; quote! { - let body = async #body; + let body = #async_keyword #body; #crate_path::pin!(body); let body: ::core::pin::Pin<&mut dyn ::core::future::Future> = body; } } else { quote! { - let body = async #body; + let body = #async_keyword #body; } }; input.into_tokens(generated_attrs, last_block) From 42dce852a2370c87dbfb5d4e884f5d54ec1c1260 Mon Sep 17 00:00:00 2001 From: Nur Date: Fri, 15 Nov 2024 05:33:38 +0600 Subject: [PATCH 07/15] clippy fix --- tokio/tests/macros_test.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tokio/tests/macros_test.rs b/tokio/tests/macros_test.rs index bed443cf293..f25661c007d 100644 --- a/tokio/tests/macros_test.rs +++ b/tokio/tests/macros_test.rs @@ -42,6 +42,7 @@ fn trait_method() { // https://github.com/tokio-rs/tokio/issues/4175 #[tokio::main] +#[allow(unreachable_code)] pub async fn issue_4175_main_1() -> ! { panic!(); } From a9b59efa181354bb2c6f504a85d9e2e28421f7a1 Mon Sep 17 00:00:00 2001 From: Nur Date: Thu, 21 Nov 2024 06:13:32 +0600 Subject: [PATCH 08/15] macros: fix span of body variable --- tokio-macros/src/entry.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tokio-macros/src/entry.rs b/tokio-macros/src/entry.rs index dd8bc44fb49..9c978b4bf0e 100644 --- a/tokio-macros/src/entry.rs +++ b/tokio-macros/src/entry.rs @@ -537,6 +537,7 @@ fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenSt quote! {} }; + let body_var = Ident::new("body", Span::call_site()); // This explicit `return` is intentional. See tokio-rs/tokio#4636 let last_block = quote_spanned! {last_stmt_end_span=> #[allow(clippy::expect_used, clippy::diverging_sub_expression, clippy::needless_return)] @@ -545,7 +546,7 @@ fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenSt .enable_all() .build() .expect("Failed building the Runtime") - .block_on(body); + .block_on(#body_var); } }; @@ -693,7 +694,8 @@ impl ItemFn { self.sig.to_tokens(&mut tokens); Brace::default().surround(&mut tokens, |tokens| { - // Note: To add `TokenStream`, we need to use `Some(self.body)` instead of `self.body`. + // Note: we used `Some(..)` for optimization. Because, + // extend has specialized implementation for `FromIterator` tokens.extend(Some(self.body)); tokens.extend(Some(last_block)); }); From 0824f9a5a7e8b3a96315bf7d82d9acbf62bf1885 Mon Sep 17 00:00:00 2001 From: Nur Date: Thu, 21 Nov 2024 08:14:17 +0600 Subject: [PATCH 09/15] macro: fixed needless lint --- tokio-macros/src/entry.rs | 10 ++-------- tokio/tests/macros_test.rs | 1 - 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/tokio-macros/src/entry.rs b/tokio-macros/src/entry.rs index 9c978b4bf0e..6631a76ad0c 100644 --- a/tokio-macros/src/entry.rs +++ b/tokio-macros/src/entry.rs @@ -427,14 +427,8 @@ fn fn_without_args(mut input: ItemFn, is_test: bool, config: FinalConfig) -> Tok // This explicit `return` is intentional. See tokio-rs/tokio#4636 let last_block = quote! { - #[allow(clippy::expect_used, clippy::diverging_sub_expression, clippy::needless_return)] - { - return #rt - .enable_all() - .build() - .expect("Failed building the Runtime") - .block_on(body); - } + #[allow(clippy::expect_used, clippy::diverging_sub_expression)] + #rt.enable_all().build().expect("Failed building the Runtime").block_on(body) }; let body = input.body; diff --git a/tokio/tests/macros_test.rs b/tokio/tests/macros_test.rs index f25661c007d..bed443cf293 100644 --- a/tokio/tests/macros_test.rs +++ b/tokio/tests/macros_test.rs @@ -42,7 +42,6 @@ fn trait_method() { // https://github.com/tokio-rs/tokio/issues/4175 #[tokio::main] -#[allow(unreachable_code)] pub async fn issue_4175_main_1() -> ! { panic!(); } From 309d6530a291d0cc2ec89380985218d5c5162894 Mon Sep 17 00:00:00 2001 From: Nur Date: Thu, 21 Nov 2024 08:58:54 +0600 Subject: [PATCH 10/15] macros: fallback to `async` block when method has `Self` keyword --- tokio-macros/src/entry.rs | 12 ++++++++++-- tokio/tests/macros_test.rs | 8 ++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/tokio-macros/src/entry.rs b/tokio-macros/src/entry.rs index 6631a76ad0c..65fd352b611 100644 --- a/tokio-macros/src/entry.rs +++ b/tokio-macros/src/entry.rs @@ -1,4 +1,4 @@ -use proc_macro2::{Span, TokenStream, TokenTree}; +use proc_macro2::{token_stream, Span, TokenStream, TokenTree}; use quote::{quote, quote_spanned, ToTokens}; use syn::parse::{Parse, ParseStream, Parser}; use syn::token::Brace; @@ -455,8 +455,16 @@ fn fn_without_args(mut input: ItemFn, is_test: bool, config: FinalConfig) -> Tok input.into_tokens(generated_attrs, last_block) } +fn has_self_keyword(mut iter: token_stream::IntoIter) -> bool { + iter.any(|tt| match tt { + TokenTree::Ident(ident) => ident == "Self", + TokenTree::Group(group) => has_self_keyword(group.stream().into_iter()), + _ => false, + }) +} + fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenStream { - if input.sig.inputs.is_empty() { + if !has_self_keyword(input.body.clone().into_iter()) && input.sig.inputs.is_empty() { return fn_without_args(input, is_test, config); } diff --git a/tokio/tests/macros_test.rs b/tokio/tests/macros_test.rs index bed443cf293..2270c421e09 100644 --- a/tokio/tests/macros_test.rs +++ b/tokio/tests/macros_test.rs @@ -25,11 +25,19 @@ async fn unused_braces_test() { assert_eq!(1 + 1, 2) } #[std::prelude::v1::test] fn trait_method() { trait A { + fn _new() -> Self; fn f(self); fn g(self); } + impl A for () { + #[tokio::main] + async fn _new() { + let v: Self = (); + v + } + #[tokio::main] async fn f(self) { self.g() From 7da2deb1e0ea9fd5bb271fba8b2eca887db12b56 Mon Sep 17 00:00:00 2001 From: Nur Date: Thu, 21 Nov 2024 09:41:34 +0600 Subject: [PATCH 11/15] macros: short-circuit to `async fn` when invoked from `#[tokio::test]` --- tokio-macros/src/entry.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tokio-macros/src/entry.rs b/tokio-macros/src/entry.rs index 65fd352b611..ceb3f6f4069 100644 --- a/tokio-macros/src/entry.rs +++ b/tokio-macros/src/entry.rs @@ -464,7 +464,7 @@ fn has_self_keyword(mut iter: token_stream::IntoIter) -> bool { } fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenStream { - if !has_self_keyword(input.body.clone().into_iter()) && input.sig.inputs.is_empty() { + if is_test || input.sig.inputs.is_empty() && !has_self_keyword(input.body.clone().into_iter()) { return fn_without_args(input, is_test, config); } From 05a2df77ecfd881ef5ca9a0afff117ff7ebcf7cc Mon Sep 17 00:00:00 2001 From: Nur Date: Thu, 21 Nov 2024 18:23:48 +0600 Subject: [PATCH 12/15] macros: fallback to async block when fn signature contain `Self` keyword --- tokio-macros/src/entry.rs | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/tokio-macros/src/entry.rs b/tokio-macros/src/entry.rs index ceb3f6f4069..5b4cd32f2de 100644 --- a/tokio-macros/src/entry.rs +++ b/tokio-macros/src/entry.rs @@ -391,7 +391,6 @@ fn build_config( } fn fn_without_args(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenStream { - let async_keyword = input.sig.asyncness.take(); let fn_sig = &input.sig; let fn_name = &input.sig.ident; @@ -439,7 +438,7 @@ fn fn_without_args(mut input: ItemFn, is_test: bool, config: FinalConfig) -> Tok syn::ReturnType::Type(_, ret_type) => quote! { #ret_type }, }; quote! { - #async_keyword #fn_sig #body + #fn_sig #body let body = #fn_name(); #crate_path::pin!(body); @@ -447,24 +446,17 @@ fn fn_without_args(mut input: ItemFn, is_test: bool, config: FinalConfig) -> Tok } } else { quote! { - #async_keyword #fn_sig #body + #fn_sig #body let body = #fn_name(); } }; + input.sig.asyncness.take(); input.into_tokens(generated_attrs, last_block) } -fn has_self_keyword(mut iter: token_stream::IntoIter) -> bool { - iter.any(|tt| match tt { - TokenTree::Ident(ident) => ident == "Self", - TokenTree::Group(group) => has_self_keyword(group.stream().into_iter()), - _ => false, - }) -} - fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenStream { - if is_test || input.sig.inputs.is_empty() && !has_self_keyword(input.body.clone().into_iter()) { + if is_test || input.sig.inputs.is_empty() && !input.is_trait_method() { return fn_without_args(input, is_test, config); } @@ -684,7 +676,27 @@ impl Parse for ItemFn { } } +fn has_self_keyword(mut iter: token_stream::IntoIter) -> bool { + iter.any(|tt| match tt { + TokenTree::Ident(ident) => ident == "Self", + TokenTree::Group(group) => has_self_keyword(group.stream().into_iter()), + _ => false, + }) +} + impl ItemFn { + fn is_trait_method(&self) -> bool { + self.sig + .generics + .where_clause + .as_ref() + .is_some_and(|clause| { + has_self_keyword(clause.to_token_stream().to_token_stream().into_iter()) + }) + || matches!(&self.sig.output, syn::ReturnType::Type(_, ty) if has_self_keyword(ty.to_token_stream().into_iter())) + || has_self_keyword(self.body.clone().into_iter()) + } + /// Convert our local function item into a token stream. fn into_tokens(self, generated_attrs: TokenStream, last_block: TokenStream) -> TokenStream { let mut tokens = generated_attrs; From a4b4c17a92031adaafbc9a04c3b0fe0d12b154fa Mon Sep 17 00:00:00 2001 From: Nur Date: Thu, 30 Jan 2025 01:57:26 +0600 Subject: [PATCH 13/15] move `macros_dead_code.rs` to `tests/pass` --- tests-build/tests/fail/macros_dead_code.stderr | 11 ----------- tests-build/tests/macros.rs | 2 +- tests-build/tests/{fail => pass}/macros_dead_code.rs | 0 3 files changed, 1 insertion(+), 12 deletions(-) delete mode 100644 tests-build/tests/fail/macros_dead_code.stderr rename tests-build/tests/{fail => pass}/macros_dead_code.rs (100%) diff --git a/tests-build/tests/fail/macros_dead_code.stderr b/tests-build/tests/fail/macros_dead_code.stderr deleted file mode 100644 index a46538a8318..00000000000 --- a/tests-build/tests/fail/macros_dead_code.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error: function `f` is never used - --> $DIR/macros_dead_code.rs:6:10 - | -6 | async fn f() {} - | ^ - | -note: the lint level is defined here - --> $DIR/macros_dead_code.rs:1:9 - | -1 | #![deny(dead_code)] - | ^^^^^^^^^ diff --git a/tests-build/tests/macros.rs b/tests-build/tests/macros.rs index b1cd774ea53..83acf96cca3 100644 --- a/tests-build/tests/macros.rs +++ b/tests-build/tests/macros.rs @@ -13,7 +13,7 @@ fn compile_fail_full() { t.pass("tests/pass/macros_main_loop.rs"); #[cfg(feature = "full")] - t.pass("tests/fail/macros_dead_code.rs"); + t.pass("tests/pass/macros_dead_code.rs"); #[cfg(feature = "full")] t.compile_fail("tests/fail/macros_invalid_input.rs"); diff --git a/tests-build/tests/fail/macros_dead_code.rs b/tests-build/tests/pass/macros_dead_code.rs similarity index 100% rename from tests-build/tests/fail/macros_dead_code.rs rename to tests-build/tests/pass/macros_dead_code.rs From 9f6f4bbcb41c69e7c3bde1b6f26ca267be176013 Mon Sep 17 00:00:00 2001 From: Nur Date: Thu, 30 Jan 2025 02:42:29 +0600 Subject: [PATCH 14/15] update `fail/macros_type_mismatch.rs` from 'master' branch --- .../tests/fail/macros_invalid_input.stderr | 2 +- .../tests/fail/macros_type_mismatch.rs | 34 +++++++++++++++++++ .../tests/fail/macros_type_mismatch.stderr | 31 +++++++++++++++-- 3 files changed, 63 insertions(+), 4 deletions(-) diff --git a/tests-build/tests/fail/macros_invalid_input.stderr b/tests-build/tests/fail/macros_invalid_input.stderr index b7a02d3f75b..daaccb2e217 100644 --- a/tests-build/tests/fail/macros_invalid_input.stderr +++ b/tests-build/tests/fail/macros_invalid_input.stderr @@ -131,7 +131,7 @@ error[E0277]: `()` is not a future | ^^^^^^^^^^^^^^ `()` is not a future | = help: the trait `Future` is not implemented for `()` -note: required by a bound in `tests_build::tokio::runtime::Runtime::block_on` +note: required by a bound in `Runtime::block_on` --> $WORKSPACE/tokio/src/runtime/runtime.rs | | pub fn block_on(&self, future: F) -> F::Output { diff --git a/tests-build/tests/fail/macros_type_mismatch.rs b/tests-build/tests/fail/macros_type_mismatch.rs index 15d70770983..16dbd2d6ac7 100644 --- a/tests-build/tests/fail/macros_type_mismatch.rs +++ b/tests-build/tests/fail/macros_type_mismatch.rs @@ -15,6 +15,40 @@ async fn extra_semicolon() -> Result<(), ()> { Ok(()); } +/// This test is a characterization test for the `?` operator. +/// +/// See for more details. +/// +/// It should fail with a single error message about the return type of the function, but instead +/// if fails with an extra error message due to the `?` operator being used within the async block +/// rather than the original function. +/// +/// ```text +/// 28 | None?; +/// | ^ cannot use the `?` operator in an async block that returns `()` +/// ``` +#[tokio::main] +async fn question_mark_operator_with_invalid_option() -> Option<()> { + None?; +} + +/// This test is a characterization test for the `?` operator. +/// +/// See for more details. +/// +/// It should fail with a single error message about the return type of the function, but instead +/// if fails with an extra error message due to the `?` operator being used within the async block +/// rather than the original function. +/// +/// ```text +/// 33 | Ok(())?; +/// | ^ cannot use the `?` operator in an async block that returns `()` +/// ``` +#[tokio::main] +async fn question_mark_operator_with_invalid_result() -> Result<(), ()> { + Ok(())?; +} + // https://github.com/tokio-rs/tokio/issues/4635 #[allow(redundant_semicolons)] #[rustfmt::skip] diff --git a/tests-build/tests/fail/macros_type_mismatch.stderr b/tests-build/tests/fail/macros_type_mismatch.stderr index 2d5af0dd65c..51f29261105 100644 --- a/tests-build/tests/fail/macros_type_mismatch.stderr +++ b/tests-build/tests/fail/macros_type_mismatch.stderr @@ -30,9 +30,34 @@ error[E0308]: mismatched types found unit type `()` error[E0308]: mismatched types - --> tests/fail/macros_type_mismatch.rs:23:12 + --> tests/fail/macros_type_mismatch.rs:31:69 | -22 | async fn issue_4635() { +31 | async fn question_mark_operator_with_invalid_option() -> Option<()> { + | _____________________________________________________________________^ +32 | | None?; + | | - help: remove this semicolon to return this value +33 | | } + | |_^ expected `Option<()>`, found `()` + | + = note: expected enum `Option<()>` + found unit type `()` + +error[E0308]: mismatched types + --> tests/fail/macros_type_mismatch.rs:48:73 + | +48 | async fn question_mark_operator_with_invalid_result() -> Result<(), ()> { + | _________________________________________________________________________^ +49 | | Ok(())?; +50 | | } + | |_^ expected `Result<(), ()>`, found `()` + | + = note: expected enum `Result<(), ()>` + found unit type `()` + +error[E0308]: mismatched types + --> tests/fail/macros_type_mismatch.rs:57:12 + | +56 | async fn issue_4635() { | - help: try adding a return type: `-> i32` -23 | return 1; +57 | return 1; | ^ expected `()`, found integer From 648cdcd264bba871b89e997774e4533576ed568d Mon Sep 17 00:00:00 2001 From: Nur Date: Thu, 30 Jan 2025 06:28:16 +0600 Subject: [PATCH 15/15] update test `macros_type_mismatch.stderr` output --- .../tests/fail/macros_type_mismatch.stderr | 57 ------------------- 1 file changed, 57 deletions(-) diff --git a/tests-build/tests/fail/macros_type_mismatch.stderr b/tests-build/tests/fail/macros_type_mismatch.stderr index 0251644c1cf..51f29261105 100644 --- a/tests-build/tests/fail/macros_type_mismatch.stderr +++ b/tests-build/tests/fail/macros_type_mismatch.stderr @@ -29,63 +29,6 @@ error[E0308]: mismatched types = note: expected enum `Result<(), ()>` found unit type `()` -error[E0277]: the `?` operator can only be used in an async block that returns `Result` or `Option` (or another type that implements `FromResidual`) - --> tests/fail/macros_type_mismatch.rs:40:9 - | -38 | #[tokio::main] - | -------------- this function should return `Result` or `Option` to accept `?` -39 | async fn question_mark_operator_with_invalid_option() -> Option<()> { -40 | None?; - | ^ cannot use the `?` operator in an async block that returns `()` - | - = help: the trait `FromResidual>` is not implemented for `()` - -error[E0308]: mismatched types - --> tests/fail/macros_type_mismatch.rs:40:5 - | -39 | async fn question_mark_operator_with_invalid_option() -> Option<()> { - | ---------- expected `Option<()>` because of return type -40 | None?; - | ^^^^^^ expected `Option<()>`, found `()` - | - = note: expected enum `Option<()>` - found unit type `()` -help: try adding an expression at the end of the block - | -40 ~ None?;; -41 + None - | -40 ~ None?;; -41 + Some(()) - | - -error[E0277]: the `?` operator can only be used in an async block that returns `Result` or `Option` (or another type that implements `FromResidual`) - --> tests/fail/macros_type_mismatch.rs:57:11 - | -55 | #[tokio::main] - | -------------- this function should return `Result` or `Option` to accept `?` -56 | async fn question_mark_operator_with_invalid_result() -> Result<(), ()> { -57 | Ok(())?; - | ^ cannot use the `?` operator in an async block that returns `()` - | - = help: the trait `FromResidual>` is not implemented for `()` - -error[E0308]: mismatched types - --> tests/fail/macros_type_mismatch.rs:57:5 - | -56 | async fn question_mark_operator_with_invalid_result() -> Result<(), ()> { - | -------------- expected `Result<(), ()>` because of return type -57 | Ok(())?; - | ^^^^^^^^ expected `Result<(), ()>`, found `()` - | - = note: expected enum `Result<(), ()>` - found unit type `()` -help: try adding an expression at the end of the block - | -57 ~ Ok(())?;; -58 + Ok(()) - | - error[E0308]: mismatched types --> tests/fail/macros_type_mismatch.rs:31:69 |