mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-08-28 00:00:11 +02:00
macros: various error message improvements (#3677)
Improves a few of the error messages for `#[tokio::main]` and `#[tokio::test]`. Also adds a note to the docs about `start_paused` requiring the `test-util` feature which wasn't mentioned previously.
This commit is contained in:
@@ -18,6 +18,24 @@ async fn test_fn_has_args(_x: u8) {}
|
|||||||
#[tokio::test(foo)]
|
#[tokio::test(foo)]
|
||||||
async fn test_attr_has_args() {}
|
async fn test_attr_has_args() {}
|
||||||
|
|
||||||
|
#[tokio::test(foo = 123)]
|
||||||
|
async fn test_unexpected_attr() {}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = 123)]
|
||||||
|
async fn test_flavor_not_string() {}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "foo")]
|
||||||
|
async fn test_unknown_flavor() {}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread", start_paused = false)]
|
||||||
|
async fn test_multi_thread_with_start_paused() {}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread", worker_threads = "foo")]
|
||||||
|
async fn test_worker_threads_not_int() {}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "current_thread", worker_threads = 4)]
|
||||||
|
async fn test_worker_threads_and_current_thread() {}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[test]
|
#[test]
|
||||||
async fn test_has_second_test_attr() {}
|
async fn test_has_second_test_attr() {}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
error: the async keyword is missing from the function declaration
|
error: the `async` keyword is missing from the function declaration
|
||||||
--> $DIR/macros_invalid_input.rs:4:1
|
--> $DIR/macros_invalid_input.rs:4:1
|
||||||
|
|
|
|
||||||
4 | fn main_is_not_async() {}
|
4 | fn main_is_not_async() {}
|
||||||
@@ -16,7 +16,7 @@ error: Must have specified ident
|
|||||||
9 | #[tokio::main(threadpool::bar)]
|
9 | #[tokio::main(threadpool::bar)]
|
||||||
| ^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: the async keyword is missing from the function declaration
|
error: the `async` keyword is missing from the function declaration
|
||||||
--> $DIR/macros_invalid_input.rs:13:1
|
--> $DIR/macros_invalid_input.rs:13:1
|
||||||
|
|
|
|
||||||
13 | fn test_is_not_async() {}
|
13 | fn test_is_not_async() {}
|
||||||
@@ -34,8 +34,44 @@ error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_th
|
|||||||
18 | #[tokio::test(foo)]
|
18 | #[tokio::test(foo)]
|
||||||
| ^^^
|
| ^^^
|
||||||
|
|
||||||
error: second test attribute is supplied
|
error: Unknown attribute foo is specified; expected one of: `flavor`, `worker_threads`, `start_paused`
|
||||||
--> $DIR/macros_invalid_input.rs:22:1
|
--> $DIR/macros_invalid_input.rs:21:15
|
||||||
|
|
|
|
||||||
22 | #[test]
|
21 | #[tokio::test(foo = 123)]
|
||||||
|
| ^^^^^^^^^
|
||||||
|
|
||||||
|
error: Failed to parse value of `flavor` as string.
|
||||||
|
--> $DIR/macros_invalid_input.rs:24:24
|
||||||
|
|
|
||||||
|
24 | #[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
|
||||||
|
|
|
||||||
|
27 | #[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
|
||||||
|
|
|
||||||
|
30 | #[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
|
||||||
|
|
|
||||||
|
33 | #[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
|
||||||
|
|
|
||||||
|
36 | #[tokio::test(flavor = "current_thread", worker_threads = 4)]
|
||||||
|
| ^
|
||||||
|
|
||||||
|
error: second test attribute is supplied
|
||||||
|
--> $DIR/macros_invalid_input.rs:40:1
|
||||||
|
|
|
||||||
|
40 | #[test]
|
||||||
| ^^^^^^^
|
| ^^^^^^^
|
||||||
|
|||||||
+42
-23
@@ -1,7 +1,6 @@
|
|||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use proc_macro2::Span;
|
use proc_macro2::Span;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::spanned::Spanned;
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
enum RuntimeFlavor {
|
enum RuntimeFlavor {
|
||||||
@@ -34,6 +33,7 @@ struct Configuration {
|
|||||||
flavor: Option<RuntimeFlavor>,
|
flavor: Option<RuntimeFlavor>,
|
||||||
worker_threads: Option<(usize, Span)>,
|
worker_threads: Option<(usize, Span)>,
|
||||||
start_paused: Option<(bool, Span)>,
|
start_paused: Option<(bool, Span)>,
|
||||||
|
is_test: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Configuration {
|
impl Configuration {
|
||||||
@@ -47,6 +47,7 @@ impl Configuration {
|
|||||||
flavor: None,
|
flavor: None,
|
||||||
worker_threads: None,
|
worker_threads: None,
|
||||||
start_paused: None,
|
start_paused: None,
|
||||||
|
is_test,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,16 +93,25 @@ impl Configuration {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn macro_name(&self) -> &'static str {
|
||||||
|
if self.is_test {
|
||||||
|
"tokio::test"
|
||||||
|
} else {
|
||||||
|
"tokio::main"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn build(&self) -> Result<FinalConfig, syn::Error> {
|
fn build(&self) -> Result<FinalConfig, syn::Error> {
|
||||||
let flavor = self.flavor.unwrap_or(self.default_flavor);
|
let flavor = self.flavor.unwrap_or(self.default_flavor);
|
||||||
use RuntimeFlavor::*;
|
use RuntimeFlavor::*;
|
||||||
|
|
||||||
let worker_threads = match (flavor, self.worker_threads) {
|
let worker_threads = match (flavor, self.worker_threads) {
|
||||||
(CurrentThread, Some((_, worker_threads_span))) => {
|
(CurrentThread, Some((_, worker_threads_span))) => {
|
||||||
return Err(syn::Error::new(
|
let msg = format!(
|
||||||
worker_threads_span,
|
"The `worker_threads` option requires the `multi_thread` runtime flavor. Use `#[{}(flavor = \"multi_thread\")]`",
|
||||||
"The `worker_threads` option requires the `multi_thread` runtime flavor.",
|
self.macro_name(),
|
||||||
))
|
);
|
||||||
|
return Err(syn::Error::new(worker_threads_span, msg));
|
||||||
}
|
}
|
||||||
(CurrentThread, None) => None,
|
(CurrentThread, None) => None,
|
||||||
(Threaded, worker_threads) if self.rt_multi_thread_available => {
|
(Threaded, worker_threads) if self.rt_multi_thread_available => {
|
||||||
@@ -119,10 +129,11 @@ impl Configuration {
|
|||||||
|
|
||||||
let start_paused = match (flavor, self.start_paused) {
|
let start_paused = match (flavor, self.start_paused) {
|
||||||
(Threaded, Some((_, start_paused_span))) => {
|
(Threaded, Some((_, start_paused_span))) => {
|
||||||
return Err(syn::Error::new(
|
let msg = format!(
|
||||||
start_paused_span,
|
"The `start_paused` option requires the `current_thread` runtime flavor. Use `#[{}(flavor = \"current_thread\")]`",
|
||||||
"The `start_paused` option requires the `current_thread` runtime flavor.",
|
self.macro_name(),
|
||||||
));
|
);
|
||||||
|
return Err(syn::Error::new(start_paused_span, msg));
|
||||||
}
|
}
|
||||||
(CurrentThread, Some((start_paused, _))) => Some(start_paused),
|
(CurrentThread, Some((start_paused, _))) => Some(start_paused),
|
||||||
(_, None) => None,
|
(_, None) => None,
|
||||||
@@ -142,12 +153,12 @@ fn parse_int(int: syn::Lit, span: Span, field: &str) -> Result<usize, syn::Error
|
|||||||
Ok(value) => Ok(value),
|
Ok(value) => Ok(value),
|
||||||
Err(e) => Err(syn::Error::new(
|
Err(e) => Err(syn::Error::new(
|
||||||
span,
|
span,
|
||||||
format!("Failed to parse {} as integer: {}", field, e),
|
format!("Failed to parse value of `{}` as integer: {}", field, e),
|
||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
_ => Err(syn::Error::new(
|
_ => Err(syn::Error::new(
|
||||||
span,
|
span,
|
||||||
format!("Failed to parse {} as integer.", field),
|
format!("Failed to parse value of `{}` as integer.", field),
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -158,7 +169,7 @@ fn parse_string(int: syn::Lit, span: Span, field: &str) -> Result<String, syn::E
|
|||||||
syn::Lit::Verbatim(s) => Ok(s.to_string()),
|
syn::Lit::Verbatim(s) => Ok(s.to_string()),
|
||||||
_ => Err(syn::Error::new(
|
_ => Err(syn::Error::new(
|
||||||
span,
|
span,
|
||||||
format!("Failed to parse {} as string.", field),
|
format!("Failed to parse value of `{}` as string.", field),
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -168,7 +179,7 @@ fn parse_bool(bool: syn::Lit, span: Span, field: &str) -> Result<bool, syn::Erro
|
|||||||
syn::Lit::Bool(b) => Ok(b.value),
|
syn::Lit::Bool(b) => Ok(b.value),
|
||||||
_ => Err(syn::Error::new(
|
_ => Err(syn::Error::new(
|
||||||
span,
|
span,
|
||||||
format!("Failed to parse {} as bool.", field),
|
format!("Failed to parse value of `{}` as bool.", field),
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -185,18 +196,14 @@ fn parse_knobs(
|
|||||||
let vis = input.vis;
|
let vis = input.vis;
|
||||||
|
|
||||||
if sig.asyncness.is_none() {
|
if sig.asyncness.is_none() {
|
||||||
let msg = "the async keyword is missing from the function declaration";
|
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(sig.fn_token, msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
sig.asyncness = None;
|
sig.asyncness = None;
|
||||||
|
|
||||||
let macro_name = if is_test {
|
|
||||||
"tokio::test"
|
|
||||||
} else {
|
|
||||||
"tokio::main"
|
|
||||||
};
|
|
||||||
let mut config = Configuration::new(is_test, rt_multi_thread);
|
let mut config = Configuration::new(is_test, rt_multi_thread);
|
||||||
|
let macro_name = config.macro_name();
|
||||||
|
|
||||||
for arg in args {
|
for arg in args {
|
||||||
match arg {
|
match arg {
|
||||||
@@ -208,20 +215,32 @@ fn parse_knobs(
|
|||||||
}
|
}
|
||||||
match ident.unwrap().to_string().to_lowercase().as_str() {
|
match ident.unwrap().to_string().to_lowercase().as_str() {
|
||||||
"worker_threads" => {
|
"worker_threads" => {
|
||||||
config.set_worker_threads(namevalue.lit.clone(), namevalue.span())?;
|
config.set_worker_threads(
|
||||||
|
namevalue.lit.clone(),
|
||||||
|
syn::spanned::Spanned::span(&namevalue.lit),
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
"flavor" => {
|
"flavor" => {
|
||||||
config.set_flavor(namevalue.lit.clone(), namevalue.span())?;
|
config.set_flavor(
|
||||||
|
namevalue.lit.clone(),
|
||||||
|
syn::spanned::Spanned::span(&namevalue.lit),
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
"start_paused" => {
|
"start_paused" => {
|
||||||
config.set_start_paused(namevalue.lit.clone(), namevalue.span())?;
|
config.set_start_paused(
|
||||||
|
namevalue.lit.clone(),
|
||||||
|
syn::spanned::Spanned::span(&namevalue.lit),
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
"core_threads" => {
|
"core_threads" => {
|
||||||
let msg = "Attribute `core_threads` is renamed to `worker_threads`";
|
let msg = "Attribute `core_threads` is renamed to `worker_threads`";
|
||||||
return Err(syn::Error::new_spanned(namevalue, msg));
|
return Err(syn::Error::new_spanned(namevalue, msg));
|
||||||
}
|
}
|
||||||
name => {
|
name => {
|
||||||
let msg = format!("Unknown attribute {} is specified; expected one of: `flavor`, `worker_threads`", name);
|
let msg = format!(
|
||||||
|
"Unknown attribute {} is specified; expected one of: `flavor`, `worker_threads`, `start_paused`",
|
||||||
|
name,
|
||||||
|
);
|
||||||
return Err(syn::Error::new_spanned(namevalue, msg));
|
return Err(syn::Error::new_spanned(namevalue, msg));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -167,6 +167,8 @@ use proc_macro::TokenStream;
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
/// Note that `start_paused` requires the `test-util` feature to be enabled.
|
||||||
|
///
|
||||||
/// ### NOTE:
|
/// ### NOTE:
|
||||||
///
|
///
|
||||||
/// If you rename the Tokio crate in your dependencies this macro will not work.
|
/// If you rename the Tokio crate in your dependencies this macro will not work.
|
||||||
@@ -257,6 +259,8 @@ pub fn main_rt(args: TokenStream, item: TokenStream) -> TokenStream {
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
/// Note that `start_paused` requires the `test-util` feature to be enabled.
|
||||||
|
///
|
||||||
/// ### NOTE:
|
/// ### NOTE:
|
||||||
///
|
///
|
||||||
/// If you rename the Tokio crate in your dependencies this macro will not work.
|
/// If you rename the Tokio crate in your dependencies this macro will not work.
|
||||||
|
|||||||
Reference in New Issue
Block a user