Skip to content

Commit 4ff8a2c

Browse files
authored
make runtime macros more IDE friendly (#391)
1 parent 5c555a9 commit 4ff8a2c

File tree

9 files changed

+109
-6
lines changed

9 files changed

+109
-6
lines changed

actix-macros/CHANGES.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
# Changes
22

33
## Unreleased - 2021-xx-xx
4+
* Improve error recovery potential when macro input is invalid. [#391]
5+
* Allow custom `System`s on test macro. [#391]
6+
7+
[#391]: https://github.com/actix/actix-net/pull/391
48

59

610
## 0.2.1 - 2021-02-02

actix-macros/src/lib.rs

Lines changed: 63 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,12 @@ use quote::quote;
2828
#[proc_macro_attribute]
2929
#[cfg(not(test))] // Work around for rust-lang/rust#62127
3030
pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
31-
let mut input = syn::parse_macro_input!(item as syn::ItemFn);
31+
let mut input = match syn::parse::<syn::ItemFn>(item.clone()) {
32+
Ok(input) => input,
33+
// on parse err, make IDEs happy; see fn docs
34+
Err(err) => return input_and_compile_error(item, err),
35+
};
36+
3237
let args = syn::parse_macro_input!(args as syn::AttributeArgs);
3338

3439
let attrs = &input.attrs;
@@ -101,8 +106,15 @@ pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
101106
/// }
102107
/// ```
103108
#[proc_macro_attribute]
104-
pub fn test(_: TokenStream, item: TokenStream) -> TokenStream {
105-
let mut input = syn::parse_macro_input!(item as syn::ItemFn);
109+
pub fn test(args: TokenStream, item: TokenStream) -> TokenStream {
110+
let mut input = match syn::parse::<syn::ItemFn>(item.clone()) {
111+
Ok(input) => input,
112+
// on parse err, make IDEs happy; see fn docs
113+
Err(err) => return input_and_compile_error(item, err),
114+
};
115+
116+
let args = syn::parse_macro_input!(args as syn::AttributeArgs);
117+
106118
let attrs = &input.attrs;
107119
let vis = &input.vis;
108120
let sig = &mut input.sig;
@@ -132,13 +144,59 @@ pub fn test(_: TokenStream, item: TokenStream) -> TokenStream {
132144
quote!(#[test])
133145
};
134146

147+
let mut system = syn::parse_str::<syn::Path>("::actix_rt::System").unwrap();
148+
149+
for arg in &args {
150+
match arg {
151+
syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue {
152+
lit: syn::Lit::Str(lit),
153+
path,
154+
..
155+
})) => match path
156+
.get_ident()
157+
.map(|i| i.to_string().to_lowercase())
158+
.as_deref()
159+
{
160+
Some("system") => match lit.parse() {
161+
Ok(path) => system = path,
162+
Err(_) => {
163+
return syn::Error::new_spanned(lit, "Expected path")
164+
.to_compile_error()
165+
.into();
166+
}
167+
},
168+
_ => {
169+
return syn::Error::new_spanned(arg, "Unknown attribute specified")
170+
.to_compile_error()
171+
.into();
172+
}
173+
},
174+
_ => {
175+
return syn::Error::new_spanned(arg, "Unknown attribute specified")
176+
.to_compile_error()
177+
.into();
178+
}
179+
}
180+
}
181+
135182
(quote! {
136183
#missing_test_attr
137184
#(#attrs)*
138185
#vis #sig {
139-
actix_rt::System::new()
140-
.block_on(async { #body })
186+
<#system>::new().block_on(async { #body })
141187
}
142188
})
143189
.into()
144190
}
191+
192+
/// Converts the error to a token stream and appends it to the original input.
193+
///
194+
/// Returning the original input in addition to the error is good for IDEs which can gracefully
195+
/// recover and show more precise errors within the macro body.
196+
///
197+
/// See <https://github.com/rust-analyzer/rust-analyzer/issues/10468> for more info.
198+
fn input_and_compile_error(mut item: TokenStream, err: syn::Error) -> TokenStream {
199+
let compile_err = TokenStream::from(err.to_compile_error());
200+
item.extend(compile_err);
201+
return item;
202+
}

actix-macros/tests/trybuild.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,7 @@ fn compile_macros() {
1111
t.pass("tests/trybuild/test-01-basic.rs");
1212
t.pass("tests/trybuild/test-02-keep-attrs.rs");
1313
t.compile_fail("tests/trybuild/test-03-only-async.rs");
14+
t.pass("tests/trybuild/test-04-system-path.rs");
15+
t.compile_fail("tests/trybuild/test-05-system-expect-path.rs");
16+
t.compile_fail("tests/trybuild/test-06-unknown-attr.rs");
1417
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
mod system {
2+
pub use actix_rt::System as MySystem;
3+
}
4+
5+
#[actix_rt::test(system = "system::MySystem")]
6+
async fn my_test() {
7+
futures_util::future::ready(()).await
8+
}
9+
10+
fn main() {}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#[actix_rt::test(system = "!@#*&")]
2+
async fn my_test() {}
3+
4+
fn main() {}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
error: Expected path
2+
--> $DIR/test-05-system-expect-path.rs:1:27
3+
|
4+
1 | #[actix_rt::test(system = "!@#*&")]
5+
| ^^^^^^^
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#[actix_rt::test(foo = "bar")]
2+
async fn my_test_1() {}
3+
4+
#[actix_rt::test(bar::baz)]
5+
async fn my_test_2() {}
6+
7+
fn main() {}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error: Unknown attribute specified
2+
--> $DIR/test-06-unknown-attr.rs:1:18
3+
|
4+
1 | #[actix_rt::test(foo = "bar")]
5+
| ^^^^^^^^^^^
6+
7+
error: Unknown attribute specified
8+
--> $DIR/test-06-unknown-attr.rs:4:18
9+
|
10+
4 | #[actix_rt::test(bar::baz)]
11+
| ^^^^^^^^

bytestring/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,5 @@ serde = { version = "1.0", optional = true }
2424

2525
[dev-dependencies]
2626
serde_json = "1.0"
27-
ahash = { version = "0.7", default-features = false }
27+
# TODO: remove when ahash MSRV is restored
28+
ahash = { version = "=0.7.4", default-features = false }

0 commit comments

Comments
 (0)