@@ -88,9 +88,9 @@ use quote::{quote, quote_spanned, ToTokens};
88
88
use syn:: ext:: IdentExt as _;
89
89
use syn:: parse:: { Parse , ParseStream } ;
90
90
use syn:: {
91
- punctuated:: Punctuated , spanned:: Spanned , Block , Expr , ExprAsync , ExprCall , FieldPat , FnArg ,
92
- Ident , Item , ItemFn , LitInt , LitStr , Pat , PatIdent , PatReference , PatStruct , PatTuple ,
93
- PatTupleStruct , PatType , Path , Signature , Stmt , Token , TypePath ,
91
+ punctuated:: Punctuated , spanned:: Spanned , Attribute , Block , Expr , ExprAsync , ExprCall ,
92
+ FieldPat , FnArg , Ident , Item , ItemFn , LitInt , LitStr , Pat , PatIdent , PatReference , PatStruct ,
93
+ PatTuple , PatTupleStruct , PatType , Path , Signature , Stmt , Token , TypePath , Visibility ,
94
94
} ;
95
95
/// Instruments a function to create and enter a `tracing` [span] every time
96
96
/// the function is called.
@@ -274,14 +274,45 @@ pub fn instrument(
274
274
args : proc_macro:: TokenStream ,
275
275
item : proc_macro:: TokenStream ,
276
276
) -> proc_macro:: TokenStream {
277
- let input = syn:: parse_macro_input!( item as ItemFn ) ;
278
277
let args = syn:: parse_macro_input!( args as InstrumentArgs ) ;
279
278
279
+ instrument_precise ( args. clone ( ) , item. clone ( ) ) . unwrap_or_else ( |_err| {
280
+ // FIXME: Ideally, we'd like to emit a warning but we can't,
281
+ // so we ignore the parsing error for now.
282
+ instrument_speculative ( args, item)
283
+ } )
284
+ }
285
+
286
+ /// Instrument the function, without parsing the function body (instead using the raw tokens).
287
+ fn instrument_speculative (
288
+ args : InstrumentArgs ,
289
+ item : proc_macro:: TokenStream ,
290
+ ) -> proc_macro:: TokenStream {
291
+ let input = syn:: parse_macro_input!( item as MaybeItemFn <TokenStream >) ;
292
+ let instrumented_function_name = input. sig . ident . to_string ( ) ;
293
+ gen_function (
294
+ & input. as_ref ( ) ,
295
+ args,
296
+ instrumented_function_name. as_str ( ) ,
297
+ None ,
298
+ )
299
+ . into ( )
300
+ }
301
+
302
+ /// Instrument the function, by fully parsing the function body,
303
+ /// which allows us to rewrite some statements related to async_trait-like patterns.
304
+ fn instrument_precise (
305
+ args : InstrumentArgs ,
306
+ item : proc_macro:: TokenStream ,
307
+ ) -> Result < proc_macro:: TokenStream , syn:: Error > {
308
+ let input = syn:: parse :: < ItemFn > ( item) ?;
280
309
let instrumented_function_name = input. sig . ident . to_string ( ) ;
281
310
282
311
// check for async_trait-like patterns in the block, and instrument
283
312
// the future instead of the wrapper
284
- if let Some ( internal_fun) = get_async_trait_info ( & input. block , input. sig . asyncness . is_some ( ) ) {
313
+ let res = if let Some ( internal_fun) =
314
+ get_async_trait_info ( & input. block , input. sig . asyncness . is_some ( ) )
315
+ {
285
316
// let's rewrite some statements!
286
317
let mut out_stmts: Vec < TokenStream > = input
287
318
. block
@@ -301,7 +332,7 @@ pub fn instrument(
301
332
out_stmts[ iter] = match internal_fun. kind {
302
333
// async-trait <= 0.1.43
303
334
AsyncTraitKind :: Function ( fun) => gen_function (
304
- fun,
335
+ & MaybeItemFnRef :: from ( fun) ,
305
336
args,
306
337
instrumented_function_name. as_str ( ) ,
307
338
internal_fun. self_type . as_ref ( ) ,
@@ -335,26 +366,33 @@ pub fn instrument(
335
366
)
336
367
. into ( )
337
368
} else {
338
- gen_function ( & input, args, instrumented_function_name. as_str ( ) , None ) . into ( )
339
- }
369
+ gen_function (
370
+ & ( & input) . into ( ) ,
371
+ args,
372
+ instrumented_function_name. as_str ( ) ,
373
+ None ,
374
+ )
375
+ . into ( )
376
+ } ;
377
+
378
+ Ok ( res)
340
379
}
341
380
342
381
/// Given an existing function, generate an instrumented version of that function
343
- fn gen_function (
344
- input : & ItemFn ,
382
+ fn gen_function < ' a , B : ToTokens + ' a > (
383
+ input : & MaybeItemFnRef < ' a , B > ,
345
384
args : InstrumentArgs ,
346
385
instrumented_function_name : & str ,
347
386
self_type : Option < & syn:: TypePath > ,
348
387
) -> proc_macro2:: TokenStream {
349
388
// these are needed ahead of time, as ItemFn contains the function body _and_
350
389
// isn't representable inside a quote!/quote_spanned! macro
351
390
// (Syn's ToTokens isn't implemented for ItemFn)
352
- let ItemFn {
391
+ let MaybeItemFnRef {
353
392
attrs,
354
393
vis,
355
- block,
356
394
sig,
357
- ..
395
+ block ,
358
396
} = input;
359
397
360
398
let Signature {
@@ -397,8 +435,8 @@ fn gen_function(
397
435
}
398
436
399
437
/// Instrument a block
400
- fn gen_block (
401
- block : & Block ,
438
+ fn gen_block < B : ToTokens > (
439
+ block : & B ,
402
440
params : & Punctuated < FnArg , Token ! [ , ] > ,
403
441
async_context : bool ,
404
442
mut args : InstrumentArgs ,
@@ -611,7 +649,68 @@ fn gen_block(
611
649
)
612
650
}
613
651
614
- #[ derive( Default , Debug ) ]
652
+ /// This is a more flexible/imprecise `ItemFn` type,
653
+ /// which's block may be anything that implements `ToTokens`.
654
+ #[ derive( Debug , Clone ) ]
655
+ struct MaybeItemFn < B : ToTokens > {
656
+ attrs : Vec < Attribute > ,
657
+ vis : Visibility ,
658
+ sig : Signature ,
659
+ block : B ,
660
+ }
661
+
662
+ impl < B : ToTokens > MaybeItemFn < B > {
663
+ fn new ( attrs : Vec < Attribute > , vis : Visibility , sig : Signature , block : B ) -> Self {
664
+ Self {
665
+ attrs,
666
+ vis,
667
+ sig,
668
+ block,
669
+ }
670
+ }
671
+
672
+ fn as_ref ( & self ) -> MaybeItemFnRef < ' _ , B > {
673
+ MaybeItemFnRef {
674
+ attrs : & self . attrs ,
675
+ vis : & self . vis ,
676
+ sig : & self . sig ,
677
+ block : & self . block ,
678
+ }
679
+ }
680
+ }
681
+
682
+ /// This parses a TokenStream as ItemFn/MaybeItemFn but skips the body.
683
+ impl Parse for MaybeItemFn < TokenStream > {
684
+ fn parse ( input : ParseStream < ' _ > ) -> syn:: Result < Self > {
685
+ let attrs = input. call ( syn:: Attribute :: parse_outer) ?;
686
+ let vis: Visibility = input. parse ( ) ?;
687
+ let sig: Signature = input. parse ( ) ?;
688
+ let block: TokenStream = input. parse ( ) ?;
689
+ Ok ( Self :: new ( attrs, vis, sig, block) )
690
+ }
691
+ }
692
+
693
+ /// A generic reference type for `MaybeItemFn`.
694
+ #[ derive( Debug , Clone ) ]
695
+ struct MaybeItemFnRef < ' a , B : ToTokens > {
696
+ attrs : & ' a Vec < Attribute > ,
697
+ vis : & ' a Visibility ,
698
+ sig : & ' a Signature ,
699
+ block : & ' a B ,
700
+ }
701
+
702
+ impl < ' a > From < & ' a ItemFn > for MaybeItemFnRef < ' a , Box < Block > > {
703
+ fn from ( val : & ' a ItemFn ) -> Self {
704
+ MaybeItemFnRef {
705
+ attrs : & val. attrs ,
706
+ vis : & val. vis ,
707
+ sig : & val. sig ,
708
+ block : & val. block ,
709
+ }
710
+ }
711
+ }
712
+
713
+ #[ derive( Clone , Default , Debug ) ]
615
714
struct InstrumentArgs {
616
715
level : Option < Level > ,
617
716
name : Option < LitStr > ,
@@ -803,7 +902,7 @@ impl Parse for Skips {
803
902
}
804
903
}
805
904
806
- #[ derive( Debug , Hash , PartialEq , Eq ) ]
905
+ #[ derive( Clone , Debug , Hash , PartialEq , Eq ) ]
807
906
enum ErrorMode {
808
907
Display ,
809
908
Debug ,
@@ -836,17 +935,17 @@ impl Parse for ErrorMode {
836
935
}
837
936
}
838
937
839
- #[ derive( Debug ) ]
938
+ #[ derive( Clone , Debug ) ]
840
939
struct Fields ( Punctuated < Field , Token ! [ , ] > ) ;
841
940
842
- #[ derive( Debug ) ]
941
+ #[ derive( Clone , Debug ) ]
843
942
struct Field {
844
943
name : Punctuated < Ident , Token ! [ . ] > ,
845
944
value : Option < Expr > ,
846
945
kind : FieldKind ,
847
946
}
848
947
849
- #[ derive( Debug , Eq , PartialEq ) ]
948
+ #[ derive( Clone , Debug , Eq , PartialEq ) ]
850
949
enum FieldKind {
851
950
Debug ,
852
951
Display ,
@@ -930,7 +1029,7 @@ impl ToTokens for FieldKind {
930
1029
}
931
1030
}
932
1031
933
- #[ derive( Debug ) ]
1032
+ #[ derive( Clone , Debug ) ]
934
1033
enum Level {
935
1034
Str ( LitStr ) ,
936
1035
Int ( LitInt ) ,
0 commit comments