@@ -28,7 +28,12 @@ use quote::quote;
2828#[ proc_macro_attribute]
2929#[ cfg( not( test) ) ] // Work around for rust-lang/rust#62127
3030pub 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+ }
0 commit comments