tokio-macros-1.7.0/.cargo_vcs_info.json0000644000000001520000000000100134460ustar { "git": { "sha1": "f64673580dfc649954eb744eb2734f2f118baa47" }, "path_in_vcs": "tokio-macros" }tokio-macros-1.7.0/CHANGELOG.md000064400000000000000000000060400072674642500141010ustar 00000000000000# 1.7.0 (December 15th, 2021) - macros: address remainging 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-1.7.0/Cargo.toml0000644000000020400000000000100114420ustar # 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 = "2018" rust-version = "1.46" name = "tokio-macros" version = "1.7.0" authors = ["Tokio Contributors "] description = "Tokio's proc macros.\n" homepage = "https://tokio.rs" 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.7" [dependencies.quote] version = "1" [dependencies.syn] version = "1.0.56" features = ["full"] [dev-dependencies.tokio] version = "1.0.0" features = ["full"] [features] tokio-macros-1.7.0/Cargo.toml.orig000064400000000000000000000013030072674642500151540ustar 00000000000000[package] name = "tokio-macros" # When releasing to crates.io: # - Remove path dependencies # - Update CHANGELOG.md. # - Create "tokio-macros-1.0.x" git tag. version = "1.7.0" edition = "2018" rust-version = "1.46" 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.7" quote = "1" syn = { version = "1.0.56", features = ["full"] } [dev-dependencies] tokio = { version = "1.0.0", path = "../tokio", features = ["full"] } [package.metadata.docs.rs] all-features = true tokio-macros-1.7.0/LICENSE000064400000000000000000000041360072674642500133010ustar 00000000000000Copyright (c) 2021 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. The MIT License (MIT) Copyright (c) 2019 Yoshua Wuyts 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-1.7.0/README.md000064400000000000000000000005050072674642500135470ustar 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-1.7.0/src/entry.rs000064400000000000000000000370100072674642500145670ustar 00000000000000use proc_macro::TokenStream; use proc_macro2::Span; use quote::{quote, quote_spanned, ToTokens}; use syn::parse::Parser; // 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)), } } } struct FinalConfig { flavor: RuntimeFlavor, worker_threads: Option, start_paused: 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, }; 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, } 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, } } 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 macro_name(&self) -> &'static str { if self.is_test { "tokio::test" } else { "tokio::main" } } fn build(&self) -> Result { let flavor = self.flavor.unwrap_or(self.default_flavor); use RuntimeFlavor::*; let worker_threads = match (flavor, self.worker_threads) { (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)); } (CurrentThread, None) => None, (Threaded, worker_threads) if self.rt_multi_thread_available => { worker_threads.map(|(val, _span)| val) } (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) { (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)); } (CurrentThread, Some((start_paused, _))) => Some(start_paused), (_, None) => None, }; Ok(FinalConfig { flavor, worker_threads, start_paused, }) } } 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_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: syn::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::NestedMeta::Meta(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(); match ident.as_str() { "worker_threads" => { config.set_worker_threads( namevalue.lit.clone(), syn::spanned::Spanned::span(&namevalue.lit), )?; } "flavor" => { config.set_flavor( namevalue.lit.clone(), syn::spanned::Spanned::span(&namevalue.lit), )?; } "start_paused" => { config.set_start_paused( namevalue.lit.clone(), syn::spanned::Spanned::span(&namevalue.lit), )?; } "core_threads" => { let msg = "Attribute `core_threads` is renamed to `worker_threads`"; return Err(syn::Error::new_spanned(namevalue, msg)); } 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)); } } } syn::NestedMeta::Meta(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" => { format!("The `{}` attribute requires an argument.", name) } name => { format!("Unknown attribute {} is specified; expected one of: `flavor`, `worker_threads`, `start_paused`", 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: syn::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 .block .stmts .last() .map(ToTokens::into_token_stream) .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 mut rt = match config.flavor { RuntimeFlavor::CurrentThread => quote_spanned! {last_stmt_start_span=> tokio::runtime::Builder::new_current_thread() }, RuntimeFlavor::Threaded => quote_spanned! {last_stmt_start_span=> tokio::runtime::Builder::new_multi_thread() }, }; if let Some(v) = config.worker_threads { rt = quote! { #rt.worker_threads(#v) }; } if let Some(v) = config.start_paused { rt = quote! { #rt.start_paused(#v) }; } let header = if is_test { quote! { #[::core::prelude::v1::test] } } else { quote! {} }; let body = &input.block; let brace_token = input.block.brace_token; let (tail_return, tail_semicolon) = match body.stmts.last() { Some(syn::Stmt::Semi(syn::Expr::Return(_), _)) => (quote! { return }, quote! { ; }), Some(syn::Stmt::Semi(..)) | Some(syn::Stmt::Local(..)) | None => { match &input.sig.output { syn::ReturnType::Type(_, ty) if matches!(&**ty, syn::Type::Tuple(ty) if ty.elems.is_empty()) => { (quote! {}, quote! { ; }) // unit } syn::ReturnType::Default => (quote! {}, quote! { ; }), // unit syn::ReturnType::Type(..) => (quote! {}, quote! {}), // ! or another } } _ => (quote! {}, quote! {}), }; input.block = syn::parse2(quote_spanned! {last_stmt_end_span=> { let body = async #body; #[allow(clippy::expect_used)] #tail_return #rt .enable_all() .build() .expect("Failed building the Runtime") .block_on(body)#tail_semicolon } }) .expect("Parsing failure"); input.block.brace_token = brace_token; let result = quote! { #header #input }; result.into() } fn token_stream_with_error(mut tokens: TokenStream, error: syn::Error) -> TokenStream { tokens.extend(TokenStream::from(error.into_compile_error())); tokens } #[cfg(not(test))] // Work around for rust-lang/rust#62127 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: syn::ItemFn = match syn::parse(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 .parse(args) .and_then(|args| build_config(input.clone(), 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), } } 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: syn::ItemFn = match syn::parse(item.clone()) { Ok(it) => it, Err(e) => return token_stream_with_error(item, e), }; let config = if let Some(attr) = input.attrs.iter().find(|attr| attr.path.is_ident("test")) { let msg = "second test attribute is supplied"; Err(syn::Error::new_spanned(&attr, msg)) } else { AttributeArgs::parse_terminated .parse(args) .and_then(|args| build_config(input.clone(), 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), } } tokio-macros-1.7.0/src/lib.rs000064400000000000000000000224340072674642500142000ustar 00000000000000#![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. /// /// # 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. /// /// ### NOTE: /// /// If you rename the Tokio crate in your dependencies this macro will not work. /// If you must rename the current version of Tokio because you're also using an /// older version of Tokio, you _must_ make the current version of Tokio /// available as `tokio` in the module where this macro is expanded. #[proc_macro_attribute] #[cfg(not(test))] // Work around for rust-lang/rust#62127 pub fn main(args: TokenStream, item: TokenStream) -> TokenStream { entry::main(args, item, true) } /// 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"); /// }) /// } /// ``` /// /// ### NOTE: /// /// If you rename the Tokio crate in your dependencies this macro will not work. /// If you must rename the current version of Tokio because you're also using an /// older version of Tokio, you _must_ make the current version of Tokio /// available as `tokio` in the module where this macro is expanded. #[proc_macro_attribute] #[cfg(not(test))] // Work around for rust-lang/rust#62127 pub fn main_rt(args: TokenStream, item: TokenStream) -> TokenStream { entry::main(args, item, false) } /// Marks async function to be executed by runtime, suitable to test environment /// /// ## Usage /// /// ### Multi-thread runtime /// /// ```no_run /// #[tokio::test(flavor = "multi_thread", worker_threads = 1)] /// async fn my_test() { /// assert!(true); /// } /// ``` /// /// ### Using default /// /// The default test runtime is single-threaded. /// /// ```no_run /// #[tokio::test] /// async fn my_test() { /// assert!(true); /// } /// ``` /// /// ### Configure the runtime to start with time paused /// /// ```no_run /// #[tokio::test(start_paused = true)] /// async fn my_test() { /// assert!(true); /// } /// ``` /// /// Note that `start_paused` requires the `test-util` feature to be enabled. /// /// ### NOTE: /// /// If you rename the Tokio crate in your dependencies this macro will not work. /// If you must rename the current version of Tokio because you're also using an /// older version of Tokio, you _must_ make the current version of Tokio /// available as `tokio` in the module where this macro is expanded. #[proc_macro_attribute] pub fn test(args: TokenStream, item: TokenStream) -> TokenStream { entry::test(args, item, true) } /// Marks async function to be executed by runtime, suitable to test environment /// /// ## Usage /// /// ```no_run /// #[tokio::test] /// async fn my_test() { /// assert!(true); /// } /// ``` /// /// ### NOTE: /// /// If you rename the Tokio crate in your dependencies this macro will not work. /// If you must rename the current version of Tokio because you're also using an /// older version of Tokio, you _must_ make the current version of Tokio /// available as `tokio` in the module where this macro is expanded. #[proc_macro_attribute] pub fn test_rt(args: TokenStream, item: TokenStream) -> TokenStream { entry::test(args, item, false) } /// 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-1.7.0/src/select.rs000064400000000000000000000065020072674642500147070ustar 00000000000000use proc_macro::{TokenStream, TokenTree}; use proc_macro2::Span; use quote::quote; use syn::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::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::Box(_box) => {} 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 or.cases.iter_mut() { clean_pattern(case); } } syn::Pat::Slice(slice) => { for elem in slice.elems.iter_mut() { clean_pattern(elem); } } syn::Pat::Struct(struct_pat) => { for field in struct_pat.fields.iter_mut() { clean_pattern(&mut field.pat); } } syn::Pat::Tuple(tuple) => { for elem in tuple.elems.iter_mut() { clean_pattern(elem); } } syn::Pat::TupleStruct(tuple) => { for elem in tuple.pat.elems.iter_mut() { 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); } _ => {} } }