Skip to content

Commit

Permalink
macros: forward input arguments in #[tokio::test] (#3691)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
davidpdrsn authored Apr 11, 2021
1 parent 1a72b28 commit 28d6879
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 52 deletions.
3 changes: 0 additions & 3 deletions tests-build/tests/fail/macros_invalid_input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {}

Expand Down
38 changes: 16 additions & 22 deletions tests-build/tests/fail/macros_invalid_input.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
|
16 | async fn test_fn_has_args(_x: u8) {}
| ^^^^^^

error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_threads`, `start_paused`
--> $DIR/macros_invalid_input.rs:18:15
--> $DIR/macros_invalid_input.rs:15:15
|
18 | #[tokio::test(foo)]
15 | #[tokio::test(foo)]
| ^^^

error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_threads`, `start_paused`
--> $DIR/macros_invalid_input.rs:21:15
--> $DIR/macros_invalid_input.rs:18: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 changes: 3 additions & 0 deletions tests-build/tests/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Expand Down
13 changes: 13 additions & 0 deletions tests-build/tests/pass/forward_args_and_output.rs
Original file line number Diff line number Diff line change
@@ -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(())
}
42 changes: 15 additions & 27 deletions tokio-macros/src/entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand Down Expand Up @@ -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()
Expand All @@ -322,6 +312,11 @@ fn parse_knobs(
}
};

let result = quote! {
#header
#input
};

Ok(result.into())
}

Expand Down Expand Up @@ -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())
}

0 comments on commit 28d6879

Please sign in to comment.