diff --git a/tests-build/tests/fail/macros_invalid_input.rs b/tests-build/tests/fail/macros_invalid_input.rs index 99daf00b4f9..85b4924f225 100644 --- a/tests-build/tests/fail/macros_invalid_input.rs +++ b/tests-build/tests/fail/macros_invalid_input.rs @@ -45,4 +45,24 @@ async fn test_crate_not_path_invalid() {} #[test] async fn test_has_second_test_attr() {} +#[tokio::test] +#[::core::prelude::v1::test] +async fn test_has_second_test_attr_v1() {} + +#[tokio::test] +#[core::prelude::rust_2015::test] +async fn test_has_second_test_attr_rust_2015() {} + +#[tokio::test] +#[::std::prelude::rust_2018::test] +async fn test_has_second_test_attr_rust_2018() {} + +#[tokio::test] +#[std::prelude::rust_2021::test] +async fn test_has_second_test_attr_rust_2021() {} + +#[tokio::test] +#[tokio::test] +async fn test_has_generated_second_test_attr() {} + fn main() {} diff --git a/tests-build/tests/fail/macros_invalid_input.stderr b/tests-build/tests/fail/macros_invalid_input.stderr index 0c8d65fc159..11f95315cdf 100644 --- a/tests-build/tests/fail/macros_invalid_input.stderr +++ b/tests-build/tests/fail/macros_invalid_input.stderr @@ -76,20 +76,40 @@ error: Failed to parse value of `crate` as path: "456" 41 | #[tokio::test(crate = "456")] | ^^^^^ -error: second test attribute is supplied +error: second test attribute is supplied, consider removing or changing the order of your test attributes --> $DIR/macros_invalid_input.rs:45:1 | 45 | #[test] | ^^^^^^^ -error: duplicated attribute - --> $DIR/macros_invalid_input.rs:45:1 +error: second test attribute is supplied, consider removing or changing the order of your test attributes + --> $DIR/macros_invalid_input.rs:49:1 | -45 | #[test] - | ^^^^^^^ +49 | #[::core::prelude::v1::test] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: second test attribute is supplied, consider removing or changing the order of your test attributes + --> $DIR/macros_invalid_input.rs:53:1 + | +53 | #[core::prelude::rust_2015::test] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: second test attribute is supplied, consider removing or changing the order of your test attributes + --> $DIR/macros_invalid_input.rs:57:1 + | +57 | #[::std::prelude::rust_2018::test] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: second test attribute is supplied, consider removing or changing the order of your test attributes + --> $DIR/macros_invalid_input.rs:61:1 + | +61 | #[std::prelude::rust_2021::test] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: second test attribute is supplied, consider removing or changing the order of your test attributes + --> $DIR/macros_invalid_input.rs:64:1 | -note: the lint level is defined here - --> $DIR/macros_invalid_input.rs:1:9 +64 | #[tokio::test] + | ^^^^^^^^^^^^^^ | -1 | #![deny(duplicate_macro_attributes)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in the attribute macro `tokio::test` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tokio-macros/src/entry.rs b/tokio-macros/src/entry.rs index 20cbdb1c92a..8858c8a1674 100644 --- a/tokio-macros/src/entry.rs +++ b/tokio-macros/src/entry.rs @@ -360,7 +360,7 @@ fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenSt rt = quote_spanned! {last_stmt_start_span=> #rt.start_paused(#v) }; } - let header = if is_test { + let generated_attrs = if is_test { quote! { #[::core::prelude::v1::test] } @@ -410,7 +410,7 @@ fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenSt } }; - input.into_tokens(header, body, last_block) + input.into_tokens(generated_attrs, body, last_block) } fn token_stream_with_error(mut tokens: TokenStream, error: syn::Error) -> TokenStream { @@ -442,6 +442,35 @@ pub(crate) fn main(args: TokenStream, item: TokenStream, rt_multi_thread: bool) } } +// Check whether given attribute is a test attribute of forms: +// * `#[test]` +// * `#[core::prelude::*::test]` or `#[::core::prelude::*::test]` +// * `#[std::prelude::*::test]` or `#[::std::prelude::*::test]` +fn is_test_attribute(attr: &Attribute) -> bool { + let path = match &attr.meta { + syn::Meta::Path(path) => path, + _ => return false, + }; + let candidates = [ + ["core", "prelude", "*", "test"], + ["std", "prelude", "*", "test"], + ]; + if path.leading_colon.is_none() + && path.segments.len() == 1 + && path.segments[0].arguments.is_none() + && path.segments[0].ident == "test" + { + return true; + } else if path.segments.len() != candidates[0].len() { + return false; + } + candidates.into_iter().any(|segments| { + path.segments.iter().zip(segments).all(|(segment, path)| { + segment.arguments.is_none() && (path == "*" || segment.ident == path) + }) + }) +} + pub(crate) fn test(args: TokenStream, item: TokenStream, rt_multi_thread: bool) -> TokenStream { // If any of the steps for this macro fail, we still want to expand to an item that is as close // to the expected output as possible. This helps out IDEs such that completions and other @@ -450,8 +479,8 @@ pub(crate) fn test(args: TokenStream, item: TokenStream, rt_multi_thread: bool) Ok(it) => it, Err(e) => return token_stream_with_error(item, e), }; - let config = if let Some(attr) = input.attrs().find(|attr| attr.meta.path().is_ident("test")) { - let msg = "second test attribute is supplied"; + let config = if let Some(attr) = input.attrs().find(|attr| is_test_attribute(attr)) { + let msg = "second test attribute is supplied, consider removing or changing the order of your test attributes"; Err(syn::Error::new_spanned(attr, msg)) } else { AttributeArgs::parse_terminated @@ -492,13 +521,11 @@ impl ItemFn { /// Convert our local function item into a token stream. fn into_tokens( self, - header: proc_macro2::TokenStream, + generated_attrs: proc_macro2::TokenStream, body: proc_macro2::TokenStream, last_block: proc_macro2::TokenStream, ) -> TokenStream { let mut tokens = proc_macro2::TokenStream::new(); - header.to_tokens(&mut tokens); - // Outer attributes are simply streamed as-is. for attr in self.outer_attrs { attr.to_tokens(&mut tokens); @@ -512,6 +539,9 @@ impl ItemFn { attr.to_tokens(&mut tokens); } + // Add generated macros at the end, so macros processed later are aware of them. + generated_attrs.to_tokens(&mut tokens); + self.vis.to_tokens(&mut tokens); self.sig.to_tokens(&mut tokens);