From 28d68798973fbfaab889d73c4da7cbc2d5a0e2f8 Mon Sep 17 00:00:00 2001 From: David Pedersen Date: Sun, 11 Apr 2021 20:39:05 +0200 Subject: [PATCH] macros: forward input arguments in `#[tokio::test]` (#3691) Fixes #2388 Previously `#[tokio::test]` would error on functions that took arguments. That meant other attribute macros couldn't do further transformations on them. This changes that so arguments are forwarded as is. Whatever else might be included on the function is forwarded as well. For example return type, generics, etc. Worth noting that this is only for compatibility with other macros. `#[test]`s that take arguments will still fail to compile. A bit odd that [trybuild] tests don't fail `#[test]` functions with arguments which is why the new tests are run with `t.pass(...)`. They do actually fail if part of a real crate. [trybuild]: https://crates.io/crates/trybuild --- .../tests/fail/macros_invalid_input.rs | 3 -- .../tests/fail/macros_invalid_input.stderr | 40 ++++++++---------- tests-build/tests/macros.rs | 3 ++ .../tests/pass/forward_args_and_output.rs | 13 ++++++ tokio-macros/src/entry.rs | 42 +++++++------------ 5 files changed, 48 insertions(+), 53 deletions(-) create mode 100644 tests-build/tests/pass/forward_args_and_output.rs diff --git a/tests-build/tests/fail/macros_invalid_input.rs b/tests-build/tests/fail/macros_invalid_input.rs index 1700c8e74..eb04eca76 100644 --- a/tests-build/tests/fail/macros_invalid_input.rs +++ b/tests-build/tests/fail/macros_invalid_input.rs @@ -12,9 +12,6 @@ async fn main_attr_has_path_args() {} #[tokio::test] fn test_is_not_async() {} -#[tokio::test] -async fn test_fn_has_args(_x: u8) {} - #[tokio::test(foo)] async fn test_attr_has_args() {} diff --git a/tests-build/tests/fail/macros_invalid_input.stderr b/tests-build/tests/fail/macros_invalid_input.stderr index 35d984869..11337a94f 100644 --- a/tests-build/tests/fail/macros_invalid_input.stderr +++ b/tests-build/tests/fail/macros_invalid_input.stderr @@ -22,56 +22,50 @@ error: the `async` keyword is missing from the function declaration 13 | fn test_is_not_async() {} | ^^ -error: the test function cannot accept arguments - --> $DIR/macros_invalid_input.rs:16:27 +error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_threads`, `start_paused` + --> $DIR/macros_invalid_input.rs:15:15 | -16 | async fn test_fn_has_args(_x: u8) {} - | ^^^^^^ +15 | #[tokio::test(foo)] + | ^^^ error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_threads`, `start_paused` --> $DIR/macros_invalid_input.rs:18:15 | -18 | #[tokio::test(foo)] - | ^^^ - -error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_threads`, `start_paused` - --> $DIR/macros_invalid_input.rs:21:15 - | -21 | #[tokio::test(foo = 123)] +18 | #[tokio::test(foo = 123)] | ^^^^^^^^^ error: Failed to parse value of `flavor` as string. - --> $DIR/macros_invalid_input.rs:24:24 + --> $DIR/macros_invalid_input.rs:21:24 | -24 | #[tokio::test(flavor = 123)] +21 | #[tokio::test(flavor = 123)] | ^^^ error: No such runtime flavor `foo`. The runtime flavors are `current_thread` and `multi_thread`. - --> $DIR/macros_invalid_input.rs:27:24 + --> $DIR/macros_invalid_input.rs:24:24 | -27 | #[tokio::test(flavor = "foo")] +24 | #[tokio::test(flavor = "foo")] | ^^^^^ error: The `start_paused` option requires the `current_thread` runtime flavor. Use `#[tokio::test(flavor = "current_thread")]` - --> $DIR/macros_invalid_input.rs:30:55 + --> $DIR/macros_invalid_input.rs:27:55 | -30 | #[tokio::test(flavor = "multi_thread", start_paused = false)] +27 | #[tokio::test(flavor = "multi_thread", start_paused = false)] | ^^^^^ error: Failed to parse value of `worker_threads` as integer. - --> $DIR/macros_invalid_input.rs:33:57 + --> $DIR/macros_invalid_input.rs:30:57 | -33 | #[tokio::test(flavor = "multi_thread", worker_threads = "foo")] +30 | #[tokio::test(flavor = "multi_thread", worker_threads = "foo")] | ^^^^^ error: The `worker_threads` option requires the `multi_thread` runtime flavor. Use `#[tokio::test(flavor = "multi_thread")]` - --> $DIR/macros_invalid_input.rs:36:59 + --> $DIR/macros_invalid_input.rs:33:59 | -36 | #[tokio::test(flavor = "current_thread", worker_threads = 4)] +33 | #[tokio::test(flavor = "current_thread", worker_threads = 4)] | ^ error: second test attribute is supplied - --> $DIR/macros_invalid_input.rs:40:1 + --> $DIR/macros_invalid_input.rs:37:1 | -40 | #[test] +37 | #[test] | ^^^^^^^ diff --git a/tests-build/tests/macros.rs b/tests-build/tests/macros.rs index 048e7f091..baed23117 100644 --- a/tests-build/tests/macros.rs +++ b/tests-build/tests/macros.rs @@ -2,6 +2,9 @@ fn compile_fail_full() { let t = trybuild::TestCases::new(); + #[cfg(feature = "full")] + t.pass("tests/pass/forward_args_and_output.rs"); + #[cfg(feature = "full")] t.compile_fail("tests/fail/macros_invalid_input.rs"); diff --git a/tests-build/tests/pass/forward_args_and_output.rs b/tests-build/tests/pass/forward_args_and_output.rs new file mode 100644 index 000000000..e6ef1f88c --- /dev/null +++ b/tests-build/tests/pass/forward_args_and_output.rs @@ -0,0 +1,13 @@ +use tests_build::tokio; + +fn main() {} + +// arguments and output type is forwarded so other macros can access them + +#[tokio::test] +async fn test_fn_has_args(_x: u8) {} + +#[tokio::test] +async fn test_has_output() -> Result<(), Box> { + Ok(()) +} diff --git a/tokio-macros/src/entry.rs b/tokio-macros/src/entry.rs index a59447b94..0967317ce 100644 --- a/tokio-macros/src/entry.rs +++ b/tokio-macros/src/entry.rs @@ -190,18 +190,11 @@ fn parse_knobs( is_test: bool, rt_multi_thread: bool, ) -> Result { - let sig = &mut input.sig; - let body = &input.block; - let attrs = &input.attrs; - let vis = input.vis; - - if sig.asyncness.is_none() { + if input.sig.asyncness.take().is_none() { let msg = "the `async` keyword is missing from the function declaration"; - return Err(syn::Error::new_spanned(sig.fn_token, msg)); + return Err(syn::Error::new_spanned(input.sig.fn_token, msg)); } - sig.asyncness = None; - let mut config = Configuration::new(is_test, rt_multi_thread); let macro_name = config.macro_name(); @@ -300,20 +293,17 @@ fn parse_knobs( rt = quote! { #rt.start_paused(#v) }; } - let header = { - if is_test { - quote! { - #[::core::prelude::v1::test] - } - } else { - quote! {} + let header = if is_test { + quote! { + #[::core::prelude::v1::test] } + } else { + quote! {} }; - let result = quote! { - #header - #(#attrs)* - #vis #sig { + let body = &input.block; + input.block = syn::parse_quote! { + { #rt .enable_all() .build() @@ -322,6 +312,11 @@ fn parse_knobs( } }; + let result = quote! { + #header + #input + }; + Ok(result.into()) } @@ -353,12 +348,5 @@ pub(crate) fn test(args: TokenStream, item: TokenStream, rt_multi_thread: bool) } } - if !input.sig.inputs.is_empty() { - let msg = "the test function cannot accept arguments"; - return syn::Error::new_spanned(&input.sig.inputs, msg) - .to_compile_error() - .into(); - } - parse_knobs(input, args, true, rt_multi_thread).unwrap_or_else(|e| e.to_compile_error().into()) }