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
This commit is contained in:
David Pedersen
2021-04-11 20:39:05 +02:00
committed by GitHub
parent 1a72b28f53
commit 28d6879897
5 changed files with 48 additions and 53 deletions
@@ -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() {}
@@ -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]
| ^^^^^^^
+3
View File
@@ -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");
@@ -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<dyn std::error::Error>> {
Ok(())
}
+15 -27
View File
@@ -190,18 +190,11 @@ fn parse_knobs(
is_test: bool,
rt_multi_thread: bool,
) -> Result<TokenStream, syn::Error> {
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())
}