tokio-macros-2.4.0/.cargo_vcs_info.json0000644000000001520000000000100134440ustar { "git": { "sha1": "6a1a7b15912d1a86ab4c0d09e1a8f0a221dbfeee" }, "path_in_vcs": "tokio-macros" }tokio-macros-2.4.0/CHANGELOG.md000064400000000000000000000123001046102023000140430ustar 00000000000000# 2.4.0 (July 22nd, 2024) - msrv: increase MSRV to 1.70 ([#6645]) - macros: allow `unhandled_panic` behavior for `#[tokio::main]` and `#[tokio::test]` ([#6593]) [#6593]: https://github.com/tokio-rs/tokio/pull/6593 [#6645]: https://github.com/tokio-rs/tokio/pull/6645 # 2.3.0 (May 30th, 2024) - macros: make `#[tokio::test]` append `#[test]` at the end of the attribute list ([#6497]) [#6497]: https://github.com/tokio-rs/tokio/pull/6497 # 2.2.0 (November 19th, 2023) ### Changed - use `::core` qualified imports instead of `::std` inside `tokio::test` macro ([#5973]) [#5973]: https://github.com/tokio-rs/tokio/pull/5973 # 2.1.0 (April 25th, 2023) - macros: fix typo in `#[tokio::test]` docs ([#5636]) - macros: make entrypoints more efficient ([#5621]) [#5621]: https://github.com/tokio-rs/tokio/pull/5621 [#5636]: https://github.com/tokio-rs/tokio/pull/5636 # 2.0.0 (March 24th, 2023) This major release updates the dependency on the syn crate to 2.0.0, and increases the MSRV to 1.56. As part of this release, we are adopting a policy of depending on a specific minor release of tokio-macros. This prevents Tokio from being able to pull in many different versions of tokio-macros. - macros: update `syn` ([#5572]) - macros: accept path as crate rename ([#5557]) [#5572]: https://github.com/tokio-rs/tokio/pull/5572 [#5557]: https://github.com/tokio-rs/tokio/pull/5557 # 1.8.2 (November 30th, 2022) - fix a regression introduced in 1.8.1 ([#5244]) [#5244]: https://github.com/tokio-rs/tokio/pull/5244 # 1.8.1 (November 29th, 2022) (yanked) - macros: Pin Futures in `#[tokio::test]` to stack ([#5205]) - macros: Reduce usage of last statement spans in proc-macros ([#5092]) - macros: Improve the documentation for `#[tokio::test]` ([#4761]) [#5205]: https://github.com/tokio-rs/tokio/pull/5205 [#5092]: https://github.com/tokio-rs/tokio/pull/5092 [#4761]: https://github.com/tokio-rs/tokio/pull/4761 # 1.8.0 (June 4th, 2022) - macros: always emit return statement ([#4636]) - macros: support setting a custom crate name for `#[tokio::main]` and `#[tokio::test]` ([#4613]) [#4613]: https://github.com/tokio-rs/tokio/pull/4613 [#4636]: https://github.com/tokio-rs/tokio/pull/4636 # 1.7.0 (December 15th, 2021) - macros: address remaining `clippy::semicolon_if_nothing_returned` warning ([#4252]) [#4252]: https://github.com/tokio-rs/tokio/pull/4252 # 1.6.0 (November 16th, 2021) - macros: fix mut patterns in `select!` macro ([#4211]) [#4211]: https://github.com/tokio-rs/tokio/pull/4211 # 1.5.1 (October 29th, 2021) - macros: fix type resolution error in `#[tokio::main]` ([#4176]) [#4176]: https://github.com/tokio-rs/tokio/pull/4176 # 1.5.0 (October 13th, 2021) - macros: make tokio-macros attributes more IDE friendly ([#4162]) [#4162]: https://github.com/tokio-rs/tokio/pull/4162 # 1.4.1 (September 30th, 2021) Reverted: run `current_thread` inside `LocalSet` ([#4027]) # 1.4.0 (September 29th, 2021) (yanked) ### Changed - macros: run `current_thread` inside `LocalSet` ([#4027]) - macros: explicitly relaxed clippy lint for `.expect()` in runtime entry macro ([#4030]) ### Fixed - macros: fix invalid error messages in functions wrapped with `#[main]` or `#[test]` ([#4067]) [#4027]: https://github.com/tokio-rs/tokio/pull/4027 [#4030]: https://github.com/tokio-rs/tokio/pull/4030 [#4067]: https://github.com/tokio-rs/tokio/pull/4067 # 1.3.0 (July 7, 2021) - macros: don't trigger `clippy::unwrap_used` ([#3926]) [#3926]: https://github.com/tokio-rs/tokio/pull/3926 # 1.2.0 (May 14, 2021) - macros: forward input arguments in `#[tokio::test]` ([#3691]) - macros: improve diagnostics on type mismatch ([#3766]) - macros: various error message improvements ([#3677]) [#3677]: https://github.com/tokio-rs/tokio/pull/3677 [#3691]: https://github.com/tokio-rs/tokio/pull/3691 [#3766]: https://github.com/tokio-rs/tokio/pull/3766 # 1.1.0 (February 5, 2021) - add `start_paused` option to macros ([#3492]) # 1.0.0 (December 23, 2020) - track `tokio` 1.0 release. # 0.3.1 (October 25, 2020) ### Fixed - fix incorrect docs regarding `max_threads` option ([#3038]) # 0.3.0 (October 15, 2020) - Track `tokio` 0.3 release. ### Changed - options are renamed to track `tokio` runtime builder fn names. - `#[tokio::main]` macro requires `rt-multi-thread` when no `flavor` is specified. # 0.2.5 (February 27, 2019) ### Fixed - doc improvements ([#2225]). # 0.2.4 (January 27, 2019) ### Fixed - generics on `#[tokio::main]` function ([#2177]). ### Added - support for `tokio::select!` ([#2152]). # 0.2.3 (January 7, 2019) ### Fixed - Revert breaking change. # 0.2.2 (January 7, 2019) ### Added - General refactoring and inclusion of additional runtime options ([#2022] and [#2038]) # 0.2.1 (December 18, 2019) ### Fixes - inherit visibility when wrapping async fn ([#1954]). # 0.2.0 (November 26, 2019) - Initial release [#1954]: https://github.com/tokio-rs/tokio/pull/1954 [#2022]: https://github.com/tokio-rs/tokio/pull/2022 [#2038]: https://github.com/tokio-rs/tokio/pull/2038 [#2152]: https://github.com/tokio-rs/tokio/pull/2152 [#2177]: https://github.com/tokio-rs/tokio/pull/2177 [#2225]: https://github.com/tokio-rs/tokio/pull/2225 [#3038]: https://github.com/tokio-rs/tokio/pull/3038 [#3492]: https://github.com/tokio-rs/tokio/pull/3492 tokio-macros-2.4.0/Cargo.toml0000644000000020720000000000100114450ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.70" name = "tokio-macros" version = "2.4.0" authors = ["Tokio Contributors "] description = """ Tokio's proc macros. """ homepage = "https://tokio.rs" readme = "README.md" categories = ["asynchronous"] license = "MIT" repository = "https://github.com/tokio-rs/tokio" [package.metadata.docs.rs] all-features = true [lib] proc-macro = true [dependencies.proc-macro2] version = "1.0.60" [dependencies.quote] version = "1" [dependencies.syn] version = "2.0" features = ["full"] [dev-dependencies.tokio] version = "1.0.0" features = ["full"] [features] tokio-macros-2.4.0/Cargo.toml.orig000064400000000000000000000013011046102023000151200ustar 00000000000000[package] name = "tokio-macros" # When releasing to crates.io: # - Remove path dependencies # - Update CHANGELOG.md. # - Create "tokio-macros-1.x.y" git tag. version = "2.4.0" edition = "2021" rust-version = "1.70" authors = ["Tokio Contributors "] license = "MIT" repository = "https://github.com/tokio-rs/tokio" homepage = "https://tokio.rs" description = """ Tokio's proc macros. """ categories = ["asynchronous"] [lib] proc-macro = true [features] [dependencies] proc-macro2 = "1.0.60" quote = "1" syn = { version = "2.0", features = ["full"] } [dev-dependencies] tokio = { version = "1.0.0", path = "../tokio", features = ["full"] } [package.metadata.docs.rs] all-features = true tokio-macros-2.4.0/LICENSE000064400000000000000000000021161046102023000132430ustar 00000000000000MIT License Copyright (c) 2019 Yoshua Wuyts Copyright (c) Tokio Contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. tokio-macros-2.4.0/README.md000064400000000000000000000005051046102023000135150ustar 00000000000000# Tokio Macros Procedural macros for use with Tokio ## License This project is licensed under the [MIT license](LICENSE). ### Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in Tokio by you, shall be licensed as MIT, without any additional terms or conditions. tokio-macros-2.4.0/src/entry.rs000064400000000000000000000610441046102023000145410ustar 00000000000000use proc_macro2::{Span, TokenStream, TokenTree}; use quote::{quote, quote_spanned, ToTokens}; use syn::parse::{Parse, ParseStream, Parser}; use syn::{braced, Attribute, Ident, Path, Signature, Visibility}; // syn::AttributeArgs does not implement syn::Parse type AttributeArgs = syn::punctuated::Punctuated; #[derive(Clone, Copy, PartialEq)] enum RuntimeFlavor { CurrentThread, Threaded, } impl RuntimeFlavor { fn from_str(s: &str) -> Result { match s { "current_thread" => Ok(RuntimeFlavor::CurrentThread), "multi_thread" => Ok(RuntimeFlavor::Threaded), "single_thread" => Err("The single threaded runtime flavor is called `current_thread`.".to_string()), "basic_scheduler" => Err("The `basic_scheduler` runtime flavor has been renamed to `current_thread`.".to_string()), "threaded_scheduler" => Err("The `threaded_scheduler` runtime flavor has been renamed to `multi_thread`.".to_string()), _ => Err(format!("No such runtime flavor `{}`. The runtime flavors are `current_thread` and `multi_thread`.", s)), } } } #[derive(Clone, Copy, PartialEq)] enum UnhandledPanic { Ignore, ShutdownRuntime, } impl UnhandledPanic { fn from_str(s: &str) -> Result { match s { "ignore" => Ok(UnhandledPanic::Ignore), "shutdown_runtime" => Ok(UnhandledPanic::ShutdownRuntime), _ => Err(format!("No such unhandled panic behavior `{}`. The unhandled panic behaviors are `ignore` and `shutdown_runtime`.", s)), } } fn into_tokens(self, crate_path: &TokenStream) -> TokenStream { match self { UnhandledPanic::Ignore => quote! { #crate_path::runtime::UnhandledPanic::Ignore }, UnhandledPanic::ShutdownRuntime => { quote! { #crate_path::runtime::UnhandledPanic::ShutdownRuntime } } } } } struct FinalConfig { flavor: RuntimeFlavor, worker_threads: Option, start_paused: Option, crate_name: Option, unhandled_panic: Option, } /// Config used in case of the attribute not being able to build a valid config const DEFAULT_ERROR_CONFIG: FinalConfig = FinalConfig { flavor: RuntimeFlavor::CurrentThread, worker_threads: None, start_paused: None, crate_name: None, unhandled_panic: None, }; struct Configuration { rt_multi_thread_available: bool, default_flavor: RuntimeFlavor, flavor: Option, worker_threads: Option<(usize, Span)>, start_paused: Option<(bool, Span)>, is_test: bool, crate_name: Option, unhandled_panic: Option<(UnhandledPanic, Span)>, } impl Configuration { fn new(is_test: bool, rt_multi_thread: bool) -> Self { Configuration { rt_multi_thread_available: rt_multi_thread, default_flavor: match is_test { true => RuntimeFlavor::CurrentThread, false => RuntimeFlavor::Threaded, }, flavor: None, worker_threads: None, start_paused: None, is_test, crate_name: None, unhandled_panic: None, } } fn set_flavor(&mut self, runtime: syn::Lit, span: Span) -> Result<(), syn::Error> { if self.flavor.is_some() { return Err(syn::Error::new(span, "`flavor` set multiple times.")); } let runtime_str = parse_string(runtime, span, "flavor")?; let runtime = RuntimeFlavor::from_str(&runtime_str).map_err(|err| syn::Error::new(span, err))?; self.flavor = Some(runtime); Ok(()) } fn set_worker_threads( &mut self, worker_threads: syn::Lit, span: Span, ) -> Result<(), syn::Error> { if self.worker_threads.is_some() { return Err(syn::Error::new( span, "`worker_threads` set multiple times.", )); } let worker_threads = parse_int(worker_threads, span, "worker_threads")?; if worker_threads == 0 { return Err(syn::Error::new(span, "`worker_threads` may not be 0.")); } self.worker_threads = Some((worker_threads, span)); Ok(()) } fn set_start_paused(&mut self, start_paused: syn::Lit, span: Span) -> Result<(), syn::Error> { if self.start_paused.is_some() { return Err(syn::Error::new(span, "`start_paused` set multiple times.")); } let start_paused = parse_bool(start_paused, span, "start_paused")?; self.start_paused = Some((start_paused, span)); Ok(()) } fn set_crate_name(&mut self, name: syn::Lit, span: Span) -> Result<(), syn::Error> { if self.crate_name.is_some() { return Err(syn::Error::new(span, "`crate` set multiple times.")); } let name_path = parse_path(name, span, "crate")?; self.crate_name = Some(name_path); Ok(()) } fn set_unhandled_panic( &mut self, unhandled_panic: syn::Lit, span: Span, ) -> Result<(), syn::Error> { if self.unhandled_panic.is_some() { return Err(syn::Error::new( span, "`unhandled_panic` set multiple times.", )); } let unhandled_panic = parse_string(unhandled_panic, span, "unhandled_panic")?; let unhandled_panic = UnhandledPanic::from_str(&unhandled_panic).map_err(|err| syn::Error::new(span, err))?; self.unhandled_panic = Some((unhandled_panic, span)); Ok(()) } fn macro_name(&self) -> &'static str { if self.is_test { "tokio::test" } else { "tokio::main" } } fn build(&self) -> Result { use RuntimeFlavor as F; let flavor = self.flavor.unwrap_or(self.default_flavor); let worker_threads = match (flavor, self.worker_threads) { (F::CurrentThread, Some((_, worker_threads_span))) => { let msg = format!( "The `worker_threads` option requires the `multi_thread` runtime flavor. Use `#[{}(flavor = \"multi_thread\")]`", self.macro_name(), ); return Err(syn::Error::new(worker_threads_span, msg)); } (F::CurrentThread, None) => None, (F::Threaded, worker_threads) if self.rt_multi_thread_available => { worker_threads.map(|(val, _span)| val) } (F::Threaded, _) => { let msg = if self.flavor.is_none() { "The default runtime flavor is `multi_thread`, but the `rt-multi-thread` feature is disabled." } else { "The runtime flavor `multi_thread` requires the `rt-multi-thread` feature." }; return Err(syn::Error::new(Span::call_site(), msg)); } }; let start_paused = match (flavor, self.start_paused) { (F::Threaded, Some((_, start_paused_span))) => { let msg = format!( "The `start_paused` option requires the `current_thread` runtime flavor. Use `#[{}(flavor = \"current_thread\")]`", self.macro_name(), ); return Err(syn::Error::new(start_paused_span, msg)); } (F::CurrentThread, Some((start_paused, _))) => Some(start_paused), (_, None) => None, }; let unhandled_panic = match (flavor, self.unhandled_panic) { (F::Threaded, Some((_, unhandled_panic_span))) => { let msg = format!( "The `unhandled_panic` option requires the `current_thread` runtime flavor. Use `#[{}(flavor = \"current_thread\")]`", self.macro_name(), ); return Err(syn::Error::new(unhandled_panic_span, msg)); } (F::CurrentThread, Some((unhandled_panic, _))) => Some(unhandled_panic), (_, None) => None, }; Ok(FinalConfig { crate_name: self.crate_name.clone(), flavor, worker_threads, start_paused, unhandled_panic, }) } } fn parse_int(int: syn::Lit, span: Span, field: &str) -> Result { match int { syn::Lit::Int(lit) => match lit.base10_parse::() { Ok(value) => Ok(value), Err(e) => Err(syn::Error::new( span, format!("Failed to parse value of `{}` as integer: {}", field, e), )), }, _ => Err(syn::Error::new( span, format!("Failed to parse value of `{}` as integer.", field), )), } } fn parse_string(int: syn::Lit, span: Span, field: &str) -> Result { match int { syn::Lit::Str(s) => Ok(s.value()), syn::Lit::Verbatim(s) => Ok(s.to_string()), _ => Err(syn::Error::new( span, format!("Failed to parse value of `{}` as string.", field), )), } } fn parse_path(lit: syn::Lit, span: Span, field: &str) -> Result { match lit { syn::Lit::Str(s) => { let err = syn::Error::new( span, format!( "Failed to parse value of `{}` as path: \"{}\"", field, s.value() ), ); s.parse::().map_err(|_| err.clone()) } _ => Err(syn::Error::new( span, format!("Failed to parse value of `{}` as path.", field), )), } } fn parse_bool(bool: syn::Lit, span: Span, field: &str) -> Result { match bool { syn::Lit::Bool(b) => Ok(b.value), _ => Err(syn::Error::new( span, format!("Failed to parse value of `{}` as bool.", field), )), } } fn build_config( input: &ItemFn, args: AttributeArgs, is_test: bool, rt_multi_thread: bool, ) -> Result { if input.sig.asyncness.is_none() { let msg = "the `async` keyword is missing from the function declaration"; return Err(syn::Error::new_spanned(input.sig.fn_token, msg)); } let mut config = Configuration::new(is_test, rt_multi_thread); let macro_name = config.macro_name(); for arg in args { match arg { syn::Meta::NameValue(namevalue) => { let ident = namevalue .path .get_ident() .ok_or_else(|| { syn::Error::new_spanned(&namevalue, "Must have specified ident") })? .to_string() .to_lowercase(); let lit = match &namevalue.value { syn::Expr::Lit(syn::ExprLit { lit, .. }) => lit, expr => return Err(syn::Error::new_spanned(expr, "Must be a literal")), }; match ident.as_str() { "worker_threads" => { config.set_worker_threads(lit.clone(), syn::spanned::Spanned::span(lit))?; } "flavor" => { config.set_flavor(lit.clone(), syn::spanned::Spanned::span(lit))?; } "start_paused" => { config.set_start_paused(lit.clone(), syn::spanned::Spanned::span(lit))?; } "core_threads" => { let msg = "Attribute `core_threads` is renamed to `worker_threads`"; return Err(syn::Error::new_spanned(namevalue, msg)); } "crate" => { config.set_crate_name(lit.clone(), syn::spanned::Spanned::span(lit))?; } "unhandled_panic" => { config .set_unhandled_panic(lit.clone(), syn::spanned::Spanned::span(lit))?; } name => { let msg = format!( "Unknown attribute {} is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `crate`, `unhandled_panic`", name, ); return Err(syn::Error::new_spanned(namevalue, msg)); } } } syn::Meta::Path(path) => { let name = path .get_ident() .ok_or_else(|| syn::Error::new_spanned(&path, "Must have specified ident"))? .to_string() .to_lowercase(); let msg = match name.as_str() { "threaded_scheduler" | "multi_thread" => { format!( "Set the runtime flavor with #[{}(flavor = \"multi_thread\")].", macro_name ) } "basic_scheduler" | "current_thread" | "single_threaded" => { format!( "Set the runtime flavor with #[{}(flavor = \"current_thread\")].", macro_name ) } "flavor" | "worker_threads" | "start_paused" | "crate" | "unhandled_panic" => { format!("The `{}` attribute requires an argument.", name) } name => { format!("Unknown attribute {} is specified; expected one of: `flavor`, `worker_threads`, `start_paused`, `crate`, `unhandled_panic`.", name) } }; return Err(syn::Error::new_spanned(path, msg)); } other => { return Err(syn::Error::new_spanned( other, "Unknown attribute inside the macro", )); } } } config.build() } fn parse_knobs(mut input: ItemFn, is_test: bool, config: FinalConfig) -> TokenStream { input.sig.asyncness = None; // If type mismatch occurs, the current rustc points to the last statement. let (last_stmt_start_span, last_stmt_end_span) = { let mut last_stmt = input.stmts.last().cloned().unwrap_or_default().into_iter(); // `Span` on stable Rust has a limitation that only points to the first // token, not the whole tokens. We can work around this limitation by // using the first/last span of the tokens like // `syn::Error::new_spanned` does. let start = last_stmt.next().map_or_else(Span::call_site, |t| t.span()); let end = last_stmt.last().map_or(start, |t| t.span()); (start, end) }; let crate_path = config .crate_name .map(ToTokens::into_token_stream) .unwrap_or_else(|| Ident::new("tokio", last_stmt_start_span).into_token_stream()); let mut rt = match config.flavor { RuntimeFlavor::CurrentThread => quote_spanned! {last_stmt_start_span=> #crate_path::runtime::Builder::new_current_thread() }, RuntimeFlavor::Threaded => quote_spanned! {last_stmt_start_span=> #crate_path::runtime::Builder::new_multi_thread() }, }; if let Some(v) = config.worker_threads { rt = quote_spanned! {last_stmt_start_span=> #rt.worker_threads(#v) }; } if let Some(v) = config.start_paused { rt = quote_spanned! {last_stmt_start_span=> #rt.start_paused(#v) }; } if let Some(v) = config.unhandled_panic { let unhandled_panic = v.into_tokens(&crate_path); rt = quote_spanned! {last_stmt_start_span=> #rt.unhandled_panic(#unhandled_panic) }; } let generated_attrs = if is_test { quote! { #[::core::prelude::v1::test] } } else { quote! {} }; let body_ident = quote! { body }; let last_block = quote_spanned! {last_stmt_end_span=> #[allow(clippy::expect_used, clippy::diverging_sub_expression)] { return #rt .enable_all() .build() .expect("Failed building the Runtime") .block_on(#body_ident); } }; let body = input.body(); // For test functions pin the body to the stack and use `Pin<&mut dyn // Future>` to reduce the amount of `Runtime::block_on` (and related // functions) copies we generate during compilation due to the generic // parameter `F` (the future to block on). This could have an impact on // performance, but because it's only for testing it's unlikely to be very // large. // // We don't do this for the main function as it should only be used once so // there will be no benefit. let body = if is_test { let output_type = match &input.sig.output { // For functions with no return value syn doesn't print anything, // but that doesn't work as `Output` for our boxed `Future`, so // default to `()` (the same type as the function output). syn::ReturnType::Default => quote! { () }, syn::ReturnType::Type(_, ret_type) => quote! { #ret_type }, }; quote! { let body = async #body; #crate_path::pin!(body); let body: ::core::pin::Pin<&mut dyn ::core::future::Future> = body; } } else { quote! { let body = async #body; } }; input.into_tokens(generated_attrs, body, last_block) } fn token_stream_with_error(mut tokens: TokenStream, error: syn::Error) -> TokenStream { tokens.extend(error.into_compile_error()); tokens } pub(crate) fn main(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 // related features keep working. let input: ItemFn = match syn::parse2(item.clone()) { Ok(it) => it, Err(e) => return token_stream_with_error(item, e), }; let config = if input.sig.ident == "main" && !input.sig.inputs.is_empty() { let msg = "the main function cannot accept arguments"; Err(syn::Error::new_spanned(&input.sig.ident, msg)) } else { AttributeArgs::parse_terminated .parse2(args) .and_then(|args| build_config(&input, args, false, rt_multi_thread)) }; match config { Ok(config) => parse_knobs(input, false, config), Err(e) => token_stream_with_error(parse_knobs(input, false, DEFAULT_ERROR_CONFIG), e), } } // 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 // related features keep working. let input: ItemFn = match syn::parse2(item.clone()) { Ok(it) => it, Err(e) => return token_stream_with_error(item, e), }; 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 .parse2(args) .and_then(|args| build_config(&input, args, true, rt_multi_thread)) }; match config { Ok(config) => parse_knobs(input, true, config), Err(e) => token_stream_with_error(parse_knobs(input, true, DEFAULT_ERROR_CONFIG), e), } } struct ItemFn { outer_attrs: Vec, vis: Visibility, sig: Signature, brace_token: syn::token::Brace, inner_attrs: Vec, stmts: Vec, } impl ItemFn { /// Access all attributes of the function item. fn attrs(&self) -> impl Iterator { self.outer_attrs.iter().chain(self.inner_attrs.iter()) } /// Get the body of the function item in a manner so that it can be /// conveniently used with the `quote!` macro. fn body(&self) -> Body<'_> { Body { brace_token: self.brace_token, stmts: &self.stmts, } } /// Convert our local function item into a token stream. fn into_tokens( self, generated_attrs: proc_macro2::TokenStream, body: proc_macro2::TokenStream, last_block: proc_macro2::TokenStream, ) -> TokenStream { let mut tokens = proc_macro2::TokenStream::new(); // Outer attributes are simply streamed as-is. for attr in self.outer_attrs { attr.to_tokens(&mut tokens); } // Inner attributes require extra care, since they're not supported on // blocks (which is what we're expanded into) we instead lift them // outside of the function. This matches the behavior of `syn`. for mut attr in self.inner_attrs { attr.style = syn::AttrStyle::Outer; 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); self.brace_token.surround(&mut tokens, |tokens| { body.to_tokens(tokens); last_block.to_tokens(tokens); }); tokens } } impl Parse for ItemFn { #[inline] fn parse(input: ParseStream<'_>) -> syn::Result { // This parse implementation has been largely lifted from `syn`, with // the exception of: // * We don't have access to the plumbing necessary to parse inner // attributes in-place. // * We do our own statements parsing to avoid recursively parsing // entire statements and only look for the parts we're interested in. let outer_attrs = input.call(Attribute::parse_outer)?; let vis: Visibility = input.parse()?; let sig: Signature = input.parse()?; let content; let brace_token = braced!(content in input); let inner_attrs = Attribute::parse_inner(&content)?; let mut buf = proc_macro2::TokenStream::new(); let mut stmts = Vec::new(); while !content.is_empty() { if let Some(semi) = content.parse::>()? { semi.to_tokens(&mut buf); stmts.push(buf); buf = proc_macro2::TokenStream::new(); continue; } // Parse a single token tree and extend our current buffer with it. // This avoids parsing the entire content of the sub-tree. buf.extend([content.parse::()?]); } if !buf.is_empty() { stmts.push(buf); } Ok(Self { outer_attrs, vis, sig, brace_token, inner_attrs, stmts, }) } } struct Body<'a> { brace_token: syn::token::Brace, // Statements, with terminating `;`. stmts: &'a [TokenStream], } impl ToTokens for Body<'_> { fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { self.brace_token.surround(tokens, |tokens| { for stmt in self.stmts { stmt.to_tokens(tokens); } }); } } tokio-macros-2.4.0/src/lib.rs000064400000000000000000000357631046102023000141570ustar 00000000000000#![allow(unknown_lints, unexpected_cfgs)] #![allow(clippy::needless_doctest_main)] #![warn( missing_debug_implementations, missing_docs, rust_2018_idioms, unreachable_pub )] #![doc(test( no_crate_inject, attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables)) ))] //! Macros for use with Tokio // This `extern` is required for older `rustc` versions but newer `rustc` // versions warn about the unused `extern crate`. #[allow(unused_extern_crates)] extern crate proc_macro; mod entry; mod select; use proc_macro::TokenStream; /// Marks async function to be executed by the selected runtime. This macro /// helps set up a `Runtime` without requiring the user to use /// [Runtime](../tokio/runtime/struct.Runtime.html) or /// [Builder](../tokio/runtime/struct.Builder.html) directly. /// /// Note: This macro is designed to be simplistic and targets applications that /// do not require a complex setup. If the provided functionality is not /// sufficient, you may be interested in using /// [Builder](../tokio/runtime/struct.Builder.html), which provides a more /// powerful interface. /// /// Note: This macro can be used on any function and not just the `main` /// function. Using it on a non-main function makes the function behave as if it /// was synchronous by starting a new runtime each time it is called. If the /// function is called often, it is preferable to create the runtime using the /// runtime builder so the runtime can be reused across calls. /// /// # Non-worker async function /// /// Note that the async function marked with this macro does not run as a /// worker. The expectation is that other tasks are spawned by the function here. /// Awaiting on other futures from the function provided here will not /// perform as fast as those spawned as workers. /// /// # Multi-threaded runtime /// /// To use the multi-threaded runtime, the macro can be configured using /// /// ``` /// #[tokio::main(flavor = "multi_thread", worker_threads = 10)] /// # async fn main() {} /// ``` /// /// The `worker_threads` option configures the number of worker threads, and /// defaults to the number of cpus on the system. This is the default flavor. /// /// Note: The multi-threaded runtime requires the `rt-multi-thread` feature /// flag. /// /// # Current thread runtime /// /// To use the single-threaded runtime known as the `current_thread` runtime, /// the macro can be configured using /// /// ``` /// #[tokio::main(flavor = "current_thread")] /// # async fn main() {} /// ``` /// /// ## Function arguments: /// /// Arguments are allowed for any functions aside from `main` which is special /// /// ## Usage /// /// ### Using the multi-thread runtime /// /// ```rust /// #[tokio::main] /// async fn main() { /// println!("Hello world"); /// } /// ``` /// /// Equivalent code not using `#[tokio::main]` /// /// ```rust /// fn main() { /// tokio::runtime::Builder::new_multi_thread() /// .enable_all() /// .build() /// .unwrap() /// .block_on(async { /// println!("Hello world"); /// }) /// } /// ``` /// /// ### Using current thread runtime /// /// The basic scheduler is single-threaded. /// /// ```rust /// #[tokio::main(flavor = "current_thread")] /// async fn main() { /// println!("Hello world"); /// } /// ``` /// /// Equivalent code not using `#[tokio::main]` /// /// ```rust /// fn main() { /// tokio::runtime::Builder::new_current_thread() /// .enable_all() /// .build() /// .unwrap() /// .block_on(async { /// println!("Hello world"); /// }) /// } /// ``` /// /// ### Set number of worker threads /// /// ```rust /// #[tokio::main(worker_threads = 2)] /// async fn main() { /// println!("Hello world"); /// } /// ``` /// /// Equivalent code not using `#[tokio::main]` /// /// ```rust /// fn main() { /// tokio::runtime::Builder::new_multi_thread() /// .worker_threads(2) /// .enable_all() /// .build() /// .unwrap() /// .block_on(async { /// println!("Hello world"); /// }) /// } /// ``` /// /// ### Configure the runtime to start with time paused /// /// ```rust /// #[tokio::main(flavor = "current_thread", start_paused = true)] /// async fn main() { /// println!("Hello world"); /// } /// ``` /// /// Equivalent code not using `#[tokio::main]` /// /// ```rust /// fn main() { /// tokio::runtime::Builder::new_current_thread() /// .enable_all() /// .start_paused(true) /// .build() /// .unwrap() /// .block_on(async { /// println!("Hello world"); /// }) /// } /// ``` /// /// Note that `start_paused` requires the `test-util` feature to be enabled. /// /// ### Rename package /// /// ```rust /// use tokio as tokio1; /// /// #[tokio1::main(crate = "tokio1")] /// async fn main() { /// println!("Hello world"); /// } /// ``` /// /// Equivalent code not using `#[tokio::main]` /// /// ```rust /// use tokio as tokio1; /// /// fn main() { /// tokio1::runtime::Builder::new_multi_thread() /// .enable_all() /// .build() /// .unwrap() /// .block_on(async { /// println!("Hello world"); /// }) /// } /// ``` /// /// ### Configure unhandled panic behavior /// /// Available options are `shutdown_runtime` and `ignore`. For more details, see /// [`Builder::unhandled_panic`]. /// /// This option is only compatible with the `current_thread` runtime. /// /// ```no_run /// #[cfg(tokio_unstable)] /// #[tokio::main(flavor = "current_thread", unhandled_panic = "shutdown_runtime")] /// async fn main() { /// let _ = tokio::spawn(async { /// panic!("This panic will shutdown the runtime."); /// }).await; /// } /// # #[cfg(not(tokio_unstable))] /// # fn main() { } /// ``` /// /// Equivalent code not using `#[tokio::main]` /// /// ```no_run /// #[cfg(tokio_unstable)] /// fn main() { /// tokio::runtime::Builder::new_current_thread() /// .enable_all() /// .unhandled_panic(UnhandledPanic::ShutdownRuntime) /// .build() /// .unwrap() /// .block_on(async { /// let _ = tokio::spawn(async { /// panic!("This panic will shutdown the runtime."); /// }).await; /// }) /// } /// # #[cfg(not(tokio_unstable))] /// # fn main() { } /// ``` /// /// **Note**: This option depends on Tokio's [unstable API][unstable]. See [the /// documentation on unstable features][unstable] for details on how to enable /// Tokio's unstable features. /// /// [`Builder::unhandled_panic`]: ../tokio/runtime/struct.Builder.html#method.unhandled_panic /// [unstable]: ../tokio/index.html#unstable-features #[proc_macro_attribute] pub fn main(args: TokenStream, item: TokenStream) -> TokenStream { entry::main(args.into(), item.into(), true).into() } /// Marks async function to be executed by selected runtime. This macro helps set up a `Runtime` /// without requiring the user to use [Runtime](../tokio/runtime/struct.Runtime.html) or /// [Builder](../tokio/runtime/struct.Builder.html) directly. /// /// ## Function arguments: /// /// Arguments are allowed for any functions aside from `main` which is special /// /// ## Usage /// /// ### Using default /// /// ```rust /// #[tokio::main(flavor = "current_thread")] /// async fn main() { /// println!("Hello world"); /// } /// ``` /// /// Equivalent code not using `#[tokio::main]` /// /// ```rust /// fn main() { /// tokio::runtime::Builder::new_current_thread() /// .enable_all() /// .build() /// .unwrap() /// .block_on(async { /// println!("Hello world"); /// }) /// } /// ``` /// /// ### Rename package /// /// ```rust /// use tokio as tokio1; /// /// #[tokio1::main(crate = "tokio1")] /// async fn main() { /// println!("Hello world"); /// } /// ``` /// /// Equivalent code not using `#[tokio::main]` /// /// ```rust /// use tokio as tokio1; /// /// fn main() { /// tokio1::runtime::Builder::new_multi_thread() /// .enable_all() /// .build() /// .unwrap() /// .block_on(async { /// println!("Hello world"); /// }) /// } /// ``` #[proc_macro_attribute] pub fn main_rt(args: TokenStream, item: TokenStream) -> TokenStream { entry::main(args.into(), item.into(), false).into() } /// Marks async function to be executed by runtime, suitable to test environment. /// This macro helps set up a `Runtime` without requiring the user to use /// [Runtime](../tokio/runtime/struct.Runtime.html) or /// [Builder](../tokio/runtime/struct.Builder.html) directly. /// /// Note: This macro is designed to be simplistic and targets applications that /// do not require a complex setup. If the provided functionality is not /// sufficient, you may be interested in using /// [Builder](../tokio/runtime/struct.Builder.html), which provides a more /// powerful interface. /// /// # Multi-threaded runtime /// /// To use the multi-threaded runtime, the macro can be configured using /// /// ```no_run /// #[tokio::test(flavor = "multi_thread", worker_threads = 1)] /// async fn my_test() { /// assert!(true); /// } /// ``` /// /// The `worker_threads` option configures the number of worker threads, and /// defaults to the number of cpus on the system. /// /// Note: The multi-threaded runtime requires the `rt-multi-thread` feature /// flag. /// /// # Current thread runtime /// /// The default test runtime is single-threaded. Each test gets a /// separate current-thread runtime. /// /// ```no_run /// #[tokio::test] /// async fn my_test() { /// assert!(true); /// } /// ``` /// /// ## Usage /// /// ### Using the multi-thread runtime /// /// ```no_run /// #[tokio::test(flavor = "multi_thread")] /// async fn my_test() { /// assert!(true); /// } /// ``` /// /// Equivalent code not using `#[tokio::test]` /// /// ```no_run /// #[test] /// fn my_test() { /// tokio::runtime::Builder::new_multi_thread() /// .enable_all() /// .build() /// .unwrap() /// .block_on(async { /// assert!(true); /// }) /// } /// ``` /// /// ### Using current thread runtime /// /// ```no_run /// #[tokio::test] /// async fn my_test() { /// assert!(true); /// } /// ``` /// /// Equivalent code not using `#[tokio::test]` /// /// ```no_run /// #[test] /// fn my_test() { /// tokio::runtime::Builder::new_current_thread() /// .enable_all() /// .build() /// .unwrap() /// .block_on(async { /// assert!(true); /// }) /// } /// ``` /// /// ### Set number of worker threads /// /// ```no_run /// #[tokio::test(flavor = "multi_thread", worker_threads = 2)] /// async fn my_test() { /// assert!(true); /// } /// ``` /// /// Equivalent code not using `#[tokio::test]` /// /// ```no_run /// #[test] /// fn my_test() { /// tokio::runtime::Builder::new_multi_thread() /// .worker_threads(2) /// .enable_all() /// .build() /// .unwrap() /// .block_on(async { /// assert!(true); /// }) /// } /// ``` /// /// ### Configure the runtime to start with time paused /// /// ```no_run /// #[tokio::test(start_paused = true)] /// async fn my_test() { /// assert!(true); /// } /// ``` /// /// Equivalent code not using `#[tokio::test]` /// /// ```no_run /// #[test] /// fn my_test() { /// tokio::runtime::Builder::new_current_thread() /// .enable_all() /// .start_paused(true) /// .build() /// .unwrap() /// .block_on(async { /// assert!(true); /// }) /// } /// ``` /// /// Note that `start_paused` requires the `test-util` feature to be enabled. /// /// ### Rename package /// /// ```rust /// use tokio as tokio1; /// /// #[tokio1::test(crate = "tokio1")] /// async fn my_test() { /// println!("Hello world"); /// } /// ``` /// /// ### Configure unhandled panic behavior /// /// Available options are `shutdown_runtime` and `ignore`. For more details, see /// [`Builder::unhandled_panic`]. /// /// This option is only compatible with the `current_thread` runtime. /// /// ```no_run /// #[cfg(tokio_unstable)] /// #[tokio::test(flavor = "current_thread", unhandled_panic = "shutdown_runtime")] /// async fn my_test() { /// let _ = tokio::spawn(async { /// panic!("This panic will shutdown the runtime."); /// }).await; /// } /// # #[cfg(not(tokio_unstable))] /// # fn main() { } /// ``` /// /// Equivalent code not using `#[tokio::test]` /// /// ```no_run /// #[cfg(tokio_unstable)] /// #[test] /// fn my_test() { /// tokio::runtime::Builder::new_current_thread() /// .enable_all() /// .unhandled_panic(UnhandledPanic::ShutdownRuntime) /// .build() /// .unwrap() /// .block_on(async { /// let _ = tokio::spawn(async { /// panic!("This panic will shutdown the runtime."); /// }).await; /// }) /// } /// # #[cfg(not(tokio_unstable))] /// # fn main() { } /// ``` /// /// **Note**: This option depends on Tokio's [unstable API][unstable]. See [the /// documentation on unstable features][unstable] for details on how to enable /// Tokio's unstable features. /// /// [`Builder::unhandled_panic`]: ../tokio/runtime/struct.Builder.html#method.unhandled_panic /// [unstable]: ../tokio/index.html#unstable-features #[proc_macro_attribute] pub fn test(args: TokenStream, item: TokenStream) -> TokenStream { entry::test(args.into(), item.into(), true).into() } /// Marks async function to be executed by runtime, suitable to test environment /// /// ## Usage /// /// ```no_run /// #[tokio::test] /// async fn my_test() { /// assert!(true); /// } /// ``` #[proc_macro_attribute] pub fn test_rt(args: TokenStream, item: TokenStream) -> TokenStream { entry::test(args.into(), item.into(), false).into() } /// Always fails with the error message below. /// ```text /// The #[tokio::main] macro requires rt or rt-multi-thread. /// ``` #[proc_macro_attribute] pub fn main_fail(_args: TokenStream, _item: TokenStream) -> TokenStream { syn::Error::new( proc_macro2::Span::call_site(), "The #[tokio::main] macro requires rt or rt-multi-thread.", ) .to_compile_error() .into() } /// Always fails with the error message below. /// ```text /// The #[tokio::test] macro requires rt or rt-multi-thread. /// ``` #[proc_macro_attribute] pub fn test_fail(_args: TokenStream, _item: TokenStream) -> TokenStream { syn::Error::new( proc_macro2::Span::call_site(), "The #[tokio::test] macro requires rt or rt-multi-thread.", ) .to_compile_error() .into() } /// Implementation detail of the `select!` macro. This macro is **not** intended /// to be used as part of the public API and is permitted to change. #[proc_macro] #[doc(hidden)] pub fn select_priv_declare_output_enum(input: TokenStream) -> TokenStream { select::declare_output_enum(input) } /// Implementation detail of the `select!` macro. This macro is **not** intended /// to be used as part of the public API and is permitted to change. #[proc_macro] #[doc(hidden)] pub fn select_priv_clean_pattern(input: TokenStream) -> TokenStream { select::clean_pattern_macro(input) } tokio-macros-2.4.0/src/select.rs000064400000000000000000000064371046102023000146640ustar 00000000000000use proc_macro::{TokenStream, TokenTree}; use proc_macro2::Span; use quote::quote; use syn::{parse::Parser, Ident}; pub(crate) fn declare_output_enum(input: TokenStream) -> TokenStream { // passed in is: `(_ _ _)` with one `_` per branch let branches = match input.into_iter().next() { Some(TokenTree::Group(group)) => group.stream().into_iter().count(), _ => panic!("unexpected macro input"), }; let variants = (0..branches) .map(|num| Ident::new(&format!("_{}", num), Span::call_site())) .collect::>(); // Use a bitfield to track which futures completed let mask = Ident::new( if branches <= 8 { "u8" } else if branches <= 16 { "u16" } else if branches <= 32 { "u32" } else if branches <= 64 { "u64" } else { panic!("up to 64 branches supported"); }, Span::call_site(), ); TokenStream::from(quote! { pub(super) enum Out<#( #variants ),*> { #( #variants(#variants), )* // Include a `Disabled` variant signifying that all select branches // failed to resolve. Disabled, } pub(super) type Mask = #mask; }) } pub(crate) fn clean_pattern_macro(input: TokenStream) -> TokenStream { // If this isn't a pattern, we return the token stream as-is. The select! // macro is using it in a location requiring a pattern, so an error will be // emitted there. let mut input: syn::Pat = match syn::Pat::parse_single.parse(input.clone()) { Ok(it) => it, Err(_) => return input, }; clean_pattern(&mut input); quote::ToTokens::into_token_stream(input).into() } // Removes any occurrences of ref or mut in the provided pattern. fn clean_pattern(pat: &mut syn::Pat) { match pat { syn::Pat::Lit(_literal) => {} syn::Pat::Macro(_macro) => {} syn::Pat::Path(_path) => {} syn::Pat::Range(_range) => {} syn::Pat::Rest(_rest) => {} syn::Pat::Verbatim(_tokens) => {} syn::Pat::Wild(_underscore) => {} syn::Pat::Ident(ident) => { ident.by_ref = None; ident.mutability = None; if let Some((_at, pat)) = &mut ident.subpat { clean_pattern(&mut *pat); } } syn::Pat::Or(or) => { for case in &mut or.cases { clean_pattern(case); } } syn::Pat::Slice(slice) => { for elem in &mut slice.elems { clean_pattern(elem); } } syn::Pat::Struct(struct_pat) => { for field in &mut struct_pat.fields { clean_pattern(&mut field.pat); } } syn::Pat::Tuple(tuple) => { for elem in &mut tuple.elems { clean_pattern(elem); } } syn::Pat::TupleStruct(tuple) => { for elem in &mut tuple.elems { clean_pattern(elem); } } syn::Pat::Reference(reference) => { reference.mutability = None; clean_pattern(&mut reference.pat); } syn::Pat::Type(type_pat) => { clean_pattern(&mut type_pat.pat); } _ => {} } }