async_attributes/
lib.rs

1//! Experimental language-level polyfills for Async Rust.
2//!
3//! # Examples
4//!
5//! ```
6//! #[async_attributes::main]
7//! async fn main() {
8//!     println!("Hello, world!");
9//! }
10//! ```
11//!
12//! # About
13//!
14//! Async Rust is a work in progress. The language has enabled us to do some
15//! fantastic things, but not everything is figured out yet. This crate exists
16//! to polyfill language-level support for async idioms before they can be part
17//! of the language.
18//!
19//! A great example of this is `async fn main`, which we first introduced as
20//! part of the [`runtime`](https://docs.rs/runtime/0.3.0-alpha.7/runtime/) crate.
21//! Its premise is that if `async fn` is required for every `await` call, it
22//! makes sense to apply that even to `fn main`. Unfortunately this would
23//! require compiler support to enable, so we've provided an experimental
24//! polyfill for it in the mean time.
25
26#![forbid(unsafe_code, future_incompatible, rust_2018_idioms)]
27#![deny(missing_debug_implementations, nonstandard_style)]
28#![recursion_limit = "512"]
29
30use proc_macro::TokenStream;
31use quote::{quote, quote_spanned};
32use syn::spanned::Spanned;
33
34/// Enables an async main function.
35///
36/// # Examples
37///
38/// ```ignore
39/// #[async_std::main]
40/// async fn main() -> std::io::Result<()> {
41///     Ok(())
42/// }
43/// ```
44#[cfg(not(test))] // NOTE: exporting main breaks tests, we should file an issue.
45#[proc_macro_attribute]
46pub fn main(_attr: TokenStream, item: TokenStream) -> TokenStream {
47    let input = syn::parse_macro_input!(item as syn::ItemFn);
48
49    let ret = &input.sig.output;
50    let inputs = &input.sig.inputs;
51    let name = &input.sig.ident;
52    let body = &input.block;
53    let attrs = &input.attrs;
54    let vis = &input.vis;
55
56    if name != "main" {
57        return TokenStream::from(quote_spanned! { name.span() =>
58            compile_error!("only the main function can be tagged with #[async_std::main]"),
59        });
60    }
61
62    if input.sig.asyncness.is_none() {
63        return TokenStream::from(quote_spanned! { input.span() =>
64            compile_error!("the async keyword is missing from the function declaration"),
65        });
66    }
67
68    let result = quote! {
69        #vis fn main() #ret {
70            #(#attrs)*
71            async fn main(#inputs) #ret {
72                #body
73            }
74
75            async_std::task::block_on(async {
76                main().await
77            })
78        }
79
80    };
81
82    result.into()
83}
84
85/// Enables an async test function.
86///
87/// # Examples
88///
89/// ```ignore
90/// #[async_std::test]
91/// async fn my_test() -> std::io::Result<()> {
92///     assert_eq!(2 * 2, 4);
93///     Ok(())
94/// }
95/// ```
96#[proc_macro_attribute]
97pub fn test(_attr: TokenStream, item: TokenStream) -> TokenStream {
98    let input = syn::parse_macro_input!(item as syn::ItemFn);
99
100    let ret = &input.sig.output;
101    let name = &input.sig.ident;
102    let body = &input.block;
103    let attrs = &input.attrs;
104    let vis = &input.vis;
105
106    if input.sig.asyncness.is_none() {
107        return TokenStream::from(quote_spanned! { input.span() =>
108            compile_error!("the async keyword is missing from the function declaration"),
109        });
110    }
111
112    let result = quote! {
113        #[::core::prelude::v1::test]
114        #(#attrs)*
115        #vis fn #name() #ret {
116            async_std::task::block_on(async { #body })
117        }
118    };
119
120    result.into()
121}
122
123/// Enables an async benchmark function.
124///
125/// # Examples
126///
127/// ```ignore
128/// #![feature(test)]
129/// extern crate test;
130///
131/// #[async_std::bench]
132/// async fn bench_1(b: &mut test::Bencher) {
133///     b.iter(|| {
134///         println!("hello world");
135///     })
136/// }
137/// ```
138#[proc_macro_attribute]
139pub fn bench(_attr: TokenStream, item: TokenStream) -> TokenStream {
140    let input = syn::parse_macro_input!(item as syn::ItemFn);
141
142    let ret = &input.sig.output;
143    let args = &input.sig.inputs;
144    let name = &input.sig.ident;
145    let body = &input.block;
146    let attrs = &input.attrs;
147    let vis = &input.vis;
148
149    if input.sig.asyncness.is_none() {
150        return TokenStream::from(quote_spanned! { input.span() =>
151            compile_error!("the async keyword is missing from the function declaration"),
152        });
153    }
154
155    if !args.is_empty() {
156        return TokenStream::from(quote_spanned! { args.span() =>
157            compile_error!("async benchmarks don't take any arguments"),
158        });
159    }
160
161    let result = quote! {
162        #[::core::prelude::v1::bench]
163        #(#attrs)*
164        #vis fn #name(b: &mut test::Bencher) #ret {
165            task::block_on(task::spawn(async {
166                #body
167            }))
168        }
169    };
170
171    result.into()
172}