Skip to content

Commit 08b79de

Browse files
committed
macros: custom package name for #[tokio::main] and #[tokio::test]
This also enables `#[crate::test(package = "crate")]` in unit tests. Sees rust-lang/cargo#5653. Fixes tokio-rs#2312.
1 parent 252b0fa commit 08b79de

5 files changed

Lines changed: 141 additions & 47 deletions

File tree

tests-build/tests/fail/macros_invalid_input.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,15 @@ async fn test_worker_threads_not_int() {}
3333
#[tokio::test(flavor = "current_thread", worker_threads = 4)]
3434
async fn test_worker_threads_and_current_thread() {}
3535

36+
#[tokio::test(package = 456)]
37+
async fn test_package_not_ident_int() {}
38+
39+
#[tokio::test(package = "456")]
40+
async fn test_package_not_ident_string() {}
41+
42+
#[tokio::test(package = "abc::edf")]
43+
async fn test_package_not_ident_path() {}
44+
3645
#[tokio::test]
3746
#[test]
3847
async fn test_has_second_test_attr() {}

tests-build/tests/fail/macros_invalid_input.stderr

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ error: the `async` keyword is missing from the function declaration
44
4 | fn main_is_not_async() {}
55
| ^^
66

7-
error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_threads`, `start_paused`
7+
error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `package`
88
--> $DIR/macros_invalid_input.rs:6:15
99
|
1010
6 | #[tokio::main(foo)]
@@ -22,13 +22,13 @@ error: the `async` keyword is missing from the function declaration
2222
13 | fn test_is_not_async() {}
2323
| ^^
2424

25-
error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_threads`, `start_paused`
25+
error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `package`
2626
--> $DIR/macros_invalid_input.rs:15:15
2727
|
2828
15 | #[tokio::test(foo)]
2929
| ^^^
3030

31-
error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_threads`, `start_paused`
31+
error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `package`
3232
--> $DIR/macros_invalid_input.rs:18:15
3333
|
3434
18 | #[tokio::test(foo = 123)]
@@ -64,16 +64,34 @@ error: The `worker_threads` option requires the `multi_thread` runtime flavor. U
6464
33 | #[tokio::test(flavor = "current_thread", worker_threads = 4)]
6565
| ^
6666

67+
error: Failed to parse value of `package` as ident.
68+
--> $DIR/macros_invalid_input.rs:36:25
69+
|
70+
36 | #[tokio::test(package = 456)]
71+
| ^^^
72+
73+
error: Failed to parse value of `package` as ident: "456"
74+
--> $DIR/macros_invalid_input.rs:39:25
75+
|
76+
39 | #[tokio::test(package = "456")]
77+
| ^^^^^
78+
79+
error: Failed to parse value of `package` as ident: "abc::edf"
80+
--> $DIR/macros_invalid_input.rs:42:25
81+
|
82+
42 | #[tokio::test(package = "abc::edf")]
83+
| ^^^^^^^^^^
84+
6785
error: second test attribute is supplied
68-
--> $DIR/macros_invalid_input.rs:37:1
86+
--> $DIR/macros_invalid_input.rs:46:1
6987
|
70-
37 | #[test]
88+
46 | #[test]
7189
| ^^^^^^^
7290

7391
error: duplicated attribute
74-
--> $DIR/macros_invalid_input.rs:37:1
92+
--> $DIR/macros_invalid_input.rs:46:1
7593
|
76-
37 | #[test]
94+
46 | #[test]
7795
| ^^^^^^^
7896
|
7997
= note: `-D duplicate-macro-attributes` implied by `-D warnings`

tokio-macros/src/entry.rs

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use proc_macro::TokenStream;
2-
use proc_macro2::Span;
2+
use proc_macro2::{Ident, Span};
33
use quote::{quote, quote_spanned, ToTokens};
44
use syn::parse::Parser;
55

@@ -29,13 +29,15 @@ struct FinalConfig {
2929
flavor: RuntimeFlavor,
3030
worker_threads: Option<usize>,
3131
start_paused: Option<bool>,
32+
package_name: Option<String>,
3233
}
3334

3435
/// Config used in case of the attribute not being able to build a valid config
3536
const DEFAULT_ERROR_CONFIG: FinalConfig = FinalConfig {
3637
flavor: RuntimeFlavor::CurrentThread,
3738
worker_threads: None,
3839
start_paused: None,
40+
package_name: None,
3941
};
4042

4143
struct Configuration {
@@ -45,6 +47,7 @@ struct Configuration {
4547
worker_threads: Option<(usize, Span)>,
4648
start_paused: Option<(bool, Span)>,
4749
is_test: bool,
50+
package_name: Option<String>,
4851
}
4952

5053
impl Configuration {
@@ -59,6 +62,7 @@ impl Configuration {
5962
worker_threads: None,
6063
start_paused: None,
6164
is_test,
65+
package_name: None,
6266
}
6367
}
6468

@@ -104,6 +108,15 @@ impl Configuration {
104108
Ok(())
105109
}
106110

111+
fn set_package_name(&mut self, name: syn::Lit, span: Span) -> Result<(), syn::Error> {
112+
if self.package_name.is_some() {
113+
return Err(syn::Error::new(span, "`package` set multiple times."));
114+
}
115+
let name_ident = parse_ident(name, span, "package")?;
116+
self.package_name = Some(name_ident.to_string());
117+
Ok(())
118+
}
119+
107120
fn macro_name(&self) -> &'static str {
108121
if self.is_test {
109122
"tokio::test"
@@ -151,6 +164,7 @@ impl Configuration {
151164
};
152165

153166
Ok(FinalConfig {
167+
package_name: self.package_name.clone(),
154168
flavor,
155169
worker_threads,
156170
start_paused,
@@ -185,6 +199,27 @@ fn parse_string(int: syn::Lit, span: Span, field: &str) -> Result<String, syn::E
185199
}
186200
}
187201

202+
fn parse_ident(lit: syn::Lit, span: Span, field: &str) -> Result<Ident, syn::Error> {
203+
match lit {
204+
syn::Lit::Str(s) => {
205+
let err = syn::Error::new(
206+
span,
207+
format!(
208+
"Failed to parse value of `{}` as ident: \"{}\"",
209+
field,
210+
s.value()
211+
),
212+
);
213+
let path = s.parse::<syn::Path>().map_err(|_| err.clone())?;
214+
path.get_ident().cloned().ok_or_else(|| err)
215+
}
216+
_ => Err(syn::Error::new(
217+
span,
218+
format!("Failed to parse value of `{}` as ident.", field),
219+
)),
220+
}
221+
}
222+
188223
fn parse_bool(bool: syn::Lit, span: Span, field: &str) -> Result<bool, syn::Error> {
189224
match bool {
190225
syn::Lit::Bool(b) => Ok(b.value),
@@ -243,9 +278,15 @@ fn build_config(
243278
let msg = "Attribute `core_threads` is renamed to `worker_threads`";
244279
return Err(syn::Error::new_spanned(namevalue, msg));
245280
}
281+
"package" => {
282+
config.set_package_name(
283+
namevalue.lit.clone(),
284+
syn::spanned::Spanned::span(&namevalue.lit),
285+
)?;
286+
}
246287
name => {
247288
let msg = format!(
248-
"Unknown attribute {} is specified; expected one of: `flavor`, `worker_threads`, `start_paused`",
289+
"Unknown attribute {} is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `package`",
249290
name,
250291
);
251292
return Err(syn::Error::new_spanned(namevalue, msg));
@@ -275,7 +316,7 @@ fn build_config(
275316
format!("The `{}` attribute requires an argument.", name)
276317
}
277318
name => {
278-
format!("Unknown attribute {} is specified; expected one of: `flavor`, `worker_threads`, `start_paused`", name)
319+
format!("Unknown attribute {} is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `package`", name)
279320
}
280321
};
281322
return Err(syn::Error::new_spanned(path, msg));
@@ -313,12 +354,17 @@ fn parse_knobs(mut input: syn::ItemFn, is_test: bool, config: FinalConfig) -> To
313354
(start, end)
314355
};
315356

357+
let package_name = config
358+
.package_name
359+
.map(|name| Ident::new(&name, last_stmt_start_span))
360+
.unwrap_or_else(|| Ident::new("tokio", last_stmt_start_span));
361+
316362
let mut rt = match config.flavor {
317363
RuntimeFlavor::CurrentThread => quote_spanned! {last_stmt_start_span=>
318-
tokio::runtime::Builder::new_current_thread()
364+
#package_name::runtime::Builder::new_current_thread()
319365
},
320366
RuntimeFlavor::Threaded => quote_spanned! {last_stmt_start_span=>
321-
tokio::runtime::Builder::new_multi_thread()
367+
#package_name::runtime::Builder::new_multi_thread()
322368
},
323369
};
324370
if let Some(v) = config.worker_threads {

tokio-macros/src/lib.rs

Lines changed: 49 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -168,12 +168,28 @@ use proc_macro::TokenStream;
168168
///
169169
/// Note that `start_paused` requires the `test-util` feature to be enabled.
170170
///
171-
/// ### NOTE:
171+
/// ### Rename package
172172
///
173-
/// If you rename the Tokio crate in your dependencies this macro will not work.
174-
/// If you must rename the current version of Tokio because you're also using an
175-
/// older version of Tokio, you _must_ make the current version of Tokio
176-
/// available as `tokio` in the module where this macro is expanded.
173+
/// ```rust
174+
/// #[tokio1::main(package = "tokio1")]
175+
/// async fn main() {
176+
/// println!("Hello world");
177+
/// }
178+
/// ```
179+
///
180+
/// Equivalent code not using `#[tokio::main]`
181+
///
182+
/// ```rust
183+
/// fn main() {
184+
/// tokio1::runtime::Builder::new_multi_thread()
185+
/// .enable_all()
186+
/// .build()
187+
/// .unwrap()
188+
/// .block_on(async {
189+
/// println!("Hello world");
190+
/// })
191+
/// }
192+
/// ```
177193
#[proc_macro_attribute]
178194
#[cfg(not(test))] // Work around for rust-lang/rust#62127
179195
pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
@@ -213,12 +229,28 @@ pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
213229
/// }
214230
/// ```
215231
///
216-
/// ### NOTE:
232+
/// ### Rename package
217233
///
218-
/// If you rename the Tokio crate in your dependencies this macro will not work.
219-
/// If you must rename the current version of Tokio because you're also using an
220-
/// older version of Tokio, you _must_ make the current version of Tokio
221-
/// available as `tokio` in the module where this macro is expanded.
234+
/// ```rust
235+
/// #[tokio1::main(package = "tokio1")]
236+
/// async fn main() {
237+
/// println!("Hello world");
238+
/// }
239+
/// ```
240+
///
241+
/// Equivalent code not using `#[tokio::main]`
242+
///
243+
/// ```rust
244+
/// fn main() {
245+
/// tokio1::runtime::Builder::new_multi_thread()
246+
/// .enable_all()
247+
/// .build()
248+
/// .unwrap()
249+
/// .block_on(async {
250+
/// println!("Hello world");
251+
/// })
252+
/// }
253+
/// ```
222254
#[proc_macro_attribute]
223255
#[cfg(not(test))] // Work around for rust-lang/rust#62127
224256
pub fn main_rt(args: TokenStream, item: TokenStream) -> TokenStream {
@@ -260,12 +292,14 @@ pub fn main_rt(args: TokenStream, item: TokenStream) -> TokenStream {
260292
///
261293
/// Note that `start_paused` requires the `test-util` feature to be enabled.
262294
///
263-
/// ### NOTE:
295+
/// ### Rename package
264296
///
265-
/// If you rename the Tokio crate in your dependencies this macro will not work.
266-
/// If you must rename the current version of Tokio because you're also using an
267-
/// older version of Tokio, you _must_ make the current version of Tokio
268-
/// available as `tokio` in the module where this macro is expanded.
297+
/// ```rust
298+
/// #[tokio1::test(package = "tokio1")]
299+
/// async fn my_test() {
300+
/// println!("Hello world");
301+
/// }
302+
/// ```
269303
#[proc_macro_attribute]
270304
pub fn test(args: TokenStream, item: TokenStream) -> TokenStream {
271305
entry::test(args, item, true)
@@ -281,13 +315,6 @@ pub fn test(args: TokenStream, item: TokenStream) -> TokenStream {
281315
/// assert!(true);
282316
/// }
283317
/// ```
284-
///
285-
/// ### NOTE:
286-
///
287-
/// If you rename the Tokio crate in your dependencies this macro will not work.
288-
/// If you must rename the current version of Tokio because you're also using an
289-
/// older version of Tokio, you _must_ make the current version of Tokio
290-
/// available as `tokio` in the module where this macro is expanded.
291318
#[proc_macro_attribute]
292319
pub fn test_rt(args: TokenStream, item: TokenStream) -> TokenStream {
293320
entry::test(args, item, false)

tokio/src/sync/tests/notify.rs

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -62,20 +62,14 @@ fn notify_simple() {
6262
assert!(fut2.poll().is_ready());
6363
}
6464

65-
#[test]
65+
#[crate::test(package = "crate", flavor = "current_thread")]
6666
#[cfg(not(target_arch = "wasm32"))]
67-
fn watch_test() {
68-
let rt = crate::runtime::Builder::new_current_thread()
69-
.build()
70-
.unwrap();
71-
72-
rt.block_on(async {
73-
let (tx, mut rx) = crate::sync::watch::channel(());
67+
async fn watch_test() {
68+
let (tx, mut rx) = crate::sync::watch::channel(());
7469

75-
crate::spawn(async move {
76-
let _ = tx.send(());
77-
});
78-
79-
let _ = rx.changed().await;
70+
crate::spawn(async move {
71+
let _ = tx.send(());
8072
});
73+
74+
let _ = rx.changed().await;
8175
}

0 commit comments

Comments
 (0)