@@ -18,25 +18,23 @@ extern crate proc_macro;
18
18
#[ macro_use]
19
19
extern crate quote;
20
20
extern crate syn;
21
+ #[ macro_use]
22
+ extern crate synom;
21
23
22
24
use proc_macro2:: Span ;
23
25
use proc_macro:: { TokenStream , TokenTree , Delimiter , TokenNode } ;
24
26
use quote:: { Tokens , ToTokens } ;
25
27
use syn:: * ;
28
+ use syn:: delimited:: Delimited ;
26
29
use syn:: fold:: Folder ;
27
30
28
- #[ proc_macro_attribute]
29
- pub fn async ( attribute : TokenStream , function : TokenStream ) -> TokenStream {
30
- // Handle arguments to the #[async] attribute, if any
31
- let attribute = attribute. to_string ( ) ;
32
- let boxed = if attribute == "( boxed )" {
33
- true
34
- } else if attribute == "" {
35
- false
36
- } else {
37
- panic ! ( "the #[async] attribute currently only takes `boxed` as an arg" ) ;
38
- } ;
39
-
31
+ fn async_inner < F > (
32
+ boxed : bool ,
33
+ function : TokenStream ,
34
+ gen_function : Tokens ,
35
+ return_ty : F )
36
+ -> TokenStream
37
+ where F : FnOnce ( & Ty ) -> proc_macro2:: TokenStream {
40
38
// Parse our item, expecting a function. This function may be an actual
41
39
// top-level function or it could be a method (typically dictated by the
42
40
// arguments). We then extract everything we'd like to use.
@@ -164,30 +162,7 @@ pub fn async(attribute: TokenStream, function: TokenStream) -> TokenStream {
164
162
// Basically just take all those expression and expand them.
165
163
let block = ExpandAsyncFor . fold_block ( * block) ;
166
164
167
- // TODO: can we lift the restriction that `futures` must be at the root of
168
- // the crate?
169
-
170
- let output_span = first_last ( & output) ;
171
- let return_ty = if boxed {
172
- quote ! {
173
- Box <:: futures:: Future <
174
- Item = <! as :: futures:: __rt:: IsResult >:: Ok ,
175
- Error = <! as :: futures:: __rt:: IsResult >:: Err ,
176
- >>
177
- }
178
- } else {
179
- // Dunno why this is buggy, hits weird typecheck errors in tests
180
- //
181
- // quote! {
182
- // impl ::futures::Future<
183
- // Item = <#output as ::futures::__rt::MyTry>::MyOk,
184
- // Error = <#output as ::futures::__rt::MyTry>::MyError,
185
- // >
186
- // }
187
- quote ! { impl :: futures:: __rt:: MyFuture <!> + ' static }
188
- } ;
189
- let return_ty = respan ( return_ty. into ( ) , & output_span) ;
190
- let return_ty = replace_bang ( return_ty, & output) ;
165
+ let return_ty = return_ty ( & output) ;
191
166
192
167
let block_inner = quote ! {
193
168
#( let #patterns = #temp_bindings; ) *
@@ -207,7 +182,7 @@ pub fn async(attribute: TokenStream, function: TokenStream) -> TokenStream {
207
182
#[ allow( unreachable_code) ]
208
183
{
209
184
return __e;
210
- loop { yield }
185
+ loop { yield :: futures :: Async :: NotReady }
211
186
}
212
187
} ;
213
188
let mut gen_body = Tokens :: new ( ) ;
@@ -218,7 +193,7 @@ pub fn async(attribute: TokenStream, function: TokenStream) -> TokenStream {
218
193
// Give the invocation of the `gen` function the same span as the output
219
194
// as currently errors related to it being a result are targeted here. Not
220
195
// sure if more errors will highlight this function call...
221
- let gen_function = quote ! { :: futures :: __rt :: gen } ;
196
+ let output_span = first_last ( & output ) ;
222
197
let gen_function = respan ( gen_function. into ( ) , & output_span) ;
223
198
let body_inner = quote ! {
224
199
#gen_function ( move || #gen_body)
@@ -247,6 +222,99 @@ pub fn async(attribute: TokenStream, function: TokenStream) -> TokenStream {
247
222
output. into ( )
248
223
}
249
224
225
+ #[ proc_macro_attribute]
226
+ pub fn async ( attribute : TokenStream , function : TokenStream ) -> TokenStream {
227
+ // Handle arguments to the #[async] attribute, if any
228
+ let attribute = attribute. to_string ( ) ;
229
+ let boxed = if attribute == "( boxed )" {
230
+ true
231
+ } else if attribute == "" {
232
+ false
233
+ } else {
234
+ panic ! ( "the #[async] attribute currently only takes `boxed` as an arg" ) ;
235
+ } ;
236
+
237
+ async_inner ( boxed, function, quote ! { :: futures:: __rt:: gen } , |output| {
238
+ // TODO: can we lift the restriction that `futures` must be at the root of
239
+ // the crate?
240
+ let output_span = first_last ( & output) ;
241
+ let return_ty = if boxed {
242
+ quote ! {
243
+ Box <:: futures:: Future <
244
+ Item = <! as :: futures:: __rt:: IsResult >:: Ok ,
245
+ Error = <! as :: futures:: __rt:: IsResult >:: Err ,
246
+ >>
247
+ }
248
+ } else {
249
+ // Dunno why this is buggy, hits weird typecheck errors in tests
250
+ //
251
+ // quote! {
252
+ // impl ::futures::Future<
253
+ // Item = <#output as ::futures::__rt::MyTry>::MyOk,
254
+ // Error = <#output as ::futures::__rt::MyTry>::MyError,
255
+ // >
256
+ // }
257
+ quote ! { impl :: futures:: __rt:: MyFuture <!> + ' static }
258
+ } ;
259
+ let return_ty = respan ( return_ty. into ( ) , & output_span) ;
260
+ replace_bang ( return_ty, & output)
261
+ } )
262
+ }
263
+
264
+ #[ proc_macro_attribute]
265
+ pub fn async_stream ( attribute : TokenStream , function : TokenStream ) -> TokenStream {
266
+ // Handle arguments to the #[async_stream] attribute, if any
267
+ let args = syn:: parse :: < AsyncStreamArgs > ( attribute)
268
+ . expect ( "failed to parse attribute arguments" ) ;
269
+
270
+ let mut boxed = false ;
271
+ let mut item_ty = None ;
272
+
273
+ for arg in args. 0 {
274
+ match arg {
275
+ AsyncStreamArg ( term, None ) => {
276
+ if term == "boxed" {
277
+ if boxed {
278
+ panic ! ( "duplicate 'boxed' argument to #[async_stream]" ) ;
279
+ }
280
+ boxed = true ;
281
+ } else {
282
+ panic ! ( "unexpected #[async_stream] argument '{}'" , term) ;
283
+ }
284
+ }
285
+ AsyncStreamArg ( term, Some ( ty) ) => {
286
+ if term == "item" {
287
+ if item_ty. is_some ( ) {
288
+ panic ! ( "duplicate 'item' argument to #[async_stream]" ) ;
289
+ }
290
+ item_ty = Some ( ty) ;
291
+ } else {
292
+ panic ! ( "unexpected #[async_stream] argument '{}'" , quote!( #term = #ty) ) ;
293
+ }
294
+ }
295
+ }
296
+ }
297
+
298
+ let boxed = boxed;
299
+ let item_ty = item_ty. expect ( "#[async_stream] requires item type to be specified" ) ;
300
+
301
+ async_inner ( boxed, function, quote ! { :: futures:: __rt:: gen_stream } , |output| {
302
+ let output_span = first_last ( & output) ;
303
+ let return_ty = if boxed {
304
+ quote ! {
305
+ Box <:: futures:: Stream <
306
+ Item = !,
307
+ Error = <! as :: futures:: __rt:: IsResult >:: Err ,
308
+ >>
309
+ }
310
+ } else {
311
+ quote ! { impl :: futures:: __rt:: MyStream <!, !> + ' static }
312
+ } ;
313
+ let return_ty = respan ( return_ty. into ( ) , & output_span) ;
314
+ replace_bangs ( return_ty, & [ & item_ty, & output] )
315
+ } )
316
+ }
317
+
250
318
#[ proc_macro]
251
319
pub fn async_block ( input : TokenStream ) -> TokenStream {
252
320
let input = TokenStream :: from ( TokenTree {
@@ -268,7 +336,40 @@ pub fn async_block(input: TokenStream) -> TokenStream {
268
336
syn:: tokens:: Move ( span) . to_tokens ( tokens) ;
269
337
syn:: tokens:: OrOr ( [ span, span] ) . to_tokens ( tokens) ;
270
338
syn:: tokens:: Brace ( span) . surround ( tokens, |tokens| {
271
- ( quote ! { if false { yield } } ) . to_tokens ( tokens) ;
339
+ ( quote ! {
340
+ if false { yield :: futures:: Async :: NotReady }
341
+ } ) . to_tokens ( tokens) ;
342
+ expr. to_tokens ( tokens) ;
343
+ } ) ;
344
+ } ) ;
345
+
346
+ tokens. into ( )
347
+ }
348
+
349
+ #[ proc_macro]
350
+ pub fn async_stream_block ( input : TokenStream ) -> TokenStream {
351
+ let input = TokenStream :: from ( TokenTree {
352
+ kind : TokenNode :: Group ( Delimiter :: Brace , input) ,
353
+ span : Default :: default ( ) ,
354
+ } ) ;
355
+ let expr = syn:: parse ( input)
356
+ . expect ( "failed to parse tokens as an expression" ) ;
357
+ let expr = ExpandAsyncFor . fold_expr ( expr) ;
358
+
359
+ let mut tokens = quote ! {
360
+ :: futures:: __rt:: gen_stream
361
+ } ;
362
+
363
+ // Use some manual token construction here instead of `quote!` to ensure
364
+ // that we get the `call_site` span instead of the default span.
365
+ let span = syn:: Span ( Span :: call_site ( ) ) ;
366
+ syn:: tokens:: Paren ( span) . surround ( & mut tokens, |tokens| {
367
+ syn:: tokens:: Move ( span) . to_tokens ( tokens) ;
368
+ syn:: tokens:: OrOr ( [ span, span] ) . to_tokens ( tokens) ;
369
+ syn:: tokens:: Brace ( span) . surround ( tokens, |tokens| {
370
+ ( quote ! {
371
+ if false { yield :: futures:: Async :: NotReady }
372
+ } ) . to_tokens ( tokens) ;
272
373
expr. to_tokens ( tokens) ;
273
374
} ) ;
274
375
} ) ;
@@ -311,7 +412,7 @@ impl Folder for ExpandAsyncFor {
311
412
}
312
413
}
313
414
futures_await:: Async :: NotReady => {
314
- yield;
415
+ yield futures_await :: Async :: NotReady ;
315
416
continue
316
417
}
317
418
}
@@ -362,3 +463,40 @@ fn replace_bang(input: proc_macro2::TokenStream, tokens: &ToTokens)
362
463
}
363
464
new_tokens. into ( )
364
465
}
466
+
467
+ fn replace_bangs ( input : proc_macro2:: TokenStream , replacements : & [ & ToTokens ] )
468
+ -> proc_macro2:: TokenStream
469
+ {
470
+ let mut replacements = replacements. iter ( ) . cycle ( ) ;
471
+ let mut new_tokens = Tokens :: new ( ) ;
472
+ for token in input. into_iter ( ) {
473
+ match token. kind {
474
+ proc_macro2:: TokenNode :: Op ( '!' , _) => {
475
+ replacements. next ( ) . unwrap ( ) . to_tokens ( & mut new_tokens) ;
476
+ }
477
+ _ => token. to_tokens ( & mut new_tokens) ,
478
+ }
479
+ }
480
+ new_tokens. into ( )
481
+ }
482
+
483
+ struct AsyncStreamArg ( syn:: Ident , Option < syn:: Ty > ) ;
484
+
485
+ impl synom:: Synom for AsyncStreamArg {
486
+ named ! ( parse -> Self , do_parse!(
487
+ i: syn!( syn:: Ident ) >>
488
+ p: option!( do_parse!(
489
+ syn!( syn:: tokens:: Eq ) >>
490
+ p: syn!( syn:: Ty ) >>
491
+ ( p) ) ) >>
492
+ ( AsyncStreamArg ( i, p) ) ) ) ;
493
+ }
494
+
495
+ struct AsyncStreamArgs ( Vec < AsyncStreamArg > ) ;
496
+
497
+ impl synom:: Synom for AsyncStreamArgs {
498
+ named ! ( parse -> Self , map!(
499
+ option!( parens!( call!( Delimited :: <AsyncStreamArg , syn:: tokens:: Comma >:: parse_separated_nonempty) ) ) ,
500
+ |p| AsyncStreamArgs ( p. map( |d| d. 0 . into_vec( ) ) . unwrap_or_default( ) )
501
+ ) ) ;
502
+ }
0 commit comments