diff --git a/futures-macro/Cargo.toml b/futures-macro/Cargo.toml index 26ae6bed22..8f331e9520 100644 --- a/futures-macro/Cargo.toml +++ b/futures-macro/Cargo.toml @@ -23,4 +23,4 @@ autocfg = "1" proc-macro2 = "1.0" proc-macro-hack = "0.5.19" quote = "1.0" -syn = { version = "1.0", features = ["full"] } +syn = { version = "1.0.56", features = ["full"] } diff --git a/futures-macro/src/executor.rs b/futures-macro/src/executor.rs index 1efb48c7c7..40a091f94c 100644 --- a/futures-macro/src/executor.rs +++ b/futures-macro/src/executor.rs @@ -1,5 +1,6 @@ use proc_macro::TokenStream; -use quote::quote; +use proc_macro2::Span; +use quote::{quote, quote_spanned, ToTokens}; pub(crate) fn test(args: TokenStream, item: TokenStream) -> TokenStream { if !args.is_empty() { @@ -9,23 +10,45 @@ pub(crate) fn test(args: TokenStream, item: TokenStream) -> TokenStream { } let mut input = syn::parse_macro_input!(item as syn::ItemFn); - let attrs = &input.attrs; - let vis = &input.vis; - let sig = &mut input.sig; - let body = &input.block; - if sig.asyncness.take().is_none() { - return syn::Error::new_spanned(sig.fn_token, "Only async functions are supported") + if input.sig.asyncness.take().is_none() { + return syn::Error::new_spanned(input.sig.fn_token, "Only async functions are supported") .to_compile_error() .into(); } + // 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 + .block + .stmts + .last() + .map(ToTokens::into_token_stream) + .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 path = quote_spanned! {last_stmt_start_span=> + ::futures_test::__private + }; + let body = &input.block; + input.block.stmts = vec![syn::Stmt::Expr( + syn::parse2(quote_spanned! {last_stmt_end_span=> + #path::block_on(async #body) + }) + .unwrap(), + )]; + let gen = quote! { #[::core::prelude::v1::test] - #(#attrs)* - #vis #sig { - ::futures_test::__private::block_on(async move #body) - } + #input }; gen.into() diff --git a/futures/tests/test_macro.rs b/futures/tests/test_macro.rs index 2f391997ea..6adf51d8bb 100644 --- a/futures/tests/test_macro.rs +++ b/futures/tests/test_macro.rs @@ -13,3 +13,8 @@ async fn it_is_being_run() { let fut = async { false }; assert!(fut.await); } + +#[futures_test::test] +async fn return_ty() -> Result<(), ()> { + Ok(()) +}