56 lines
1.8 KiB
Rust
56 lines
1.8 KiB
Rust
use proc_macro::TokenStream;
|
|
use proc_macro2::Span;
|
|
use quote::{quote, quote_spanned, ToTokens};
|
|
|
|
pub(crate) fn test(args: TokenStream, item: TokenStream) -> TokenStream {
|
|
if !args.is_empty() {
|
|
return syn::Error::new_spanned(proc_macro2::TokenStream::from(args), "invalid argument")
|
|
.to_compile_error()
|
|
.into();
|
|
}
|
|
|
|
let mut input = syn::parse_macro_input!(item as syn::ItemFn);
|
|
|
|
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]
|
|
#input
|
|
};
|
|
|
|
gen.into()
|
|
}
|