@@ -14,6 +14,39 @@ use crate::{
14
14
MaybeItemFn , MaybeItemFnRef ,
15
15
} ;
16
16
17
+ #[ derive( Debug , Default ) ]
18
+ struct AsyncContext {
19
+ fake_return_edge : Option < proc_macro2:: TokenStream > ,
20
+ }
21
+
22
+ impl AsyncContext {
23
+ /// Adds a fake return statement that can be installed as the first thing in the
24
+ /// function body, so that we eagerly infer that the return type is what was declared
25
+ /// in the async fn signature.
26
+ fn with_fake_return_edge ( mut self , ident : & Ident , output : & ReturnType ) -> Self {
27
+ let ( return_type, return_span) = if let ReturnType :: Type ( _, return_type) = & output {
28
+ ( erase_impl_trait ( return_type) , return_type. span ( ) )
29
+ } else {
30
+ // Point at function name if we don't have an explicit return type
31
+ ( syn:: parse_quote! { ( ) } , ident. span ( ) )
32
+ } ;
33
+ // The `#[allow(..)]` is given because the return statement is
34
+ // unreachable, but does affect inference, so it needs to be written
35
+ // exactly that way for it to do its magic.
36
+ let fake_return_edge = quote_spanned ! { return_span=>
37
+ #[ allow( unreachable_code, clippy:: diverging_sub_expression, clippy:: let_unit_value) ]
38
+ if false {
39
+ let __tracing_attr_fake_return: #return_type =
40
+ unreachable!( "this is just for type inference, and is unreachable code" ) ;
41
+ return __tracing_attr_fake_return;
42
+ }
43
+ } ;
44
+
45
+ self . fake_return_edge = Some ( fake_return_edge) ;
46
+ self
47
+ }
48
+ }
49
+
17
50
/// Given an existing function, generate an instrumented version of that function
18
51
pub ( crate ) fn gen_function < ' a , B : ToTokens + ' a > (
19
52
input : MaybeItemFnRef < ' a , B > ,
@@ -51,37 +84,13 @@ pub(crate) fn gen_function<'a, B: ToTokens + 'a>(
51
84
52
85
let warnings = args. warnings ( ) ;
53
86
54
- let ( return_type, return_span) = if let ReturnType :: Type ( _, return_type) = & output {
55
- ( erase_impl_trait ( return_type) , return_type. span ( ) )
56
- } else {
57
- // Point at function name if we don't have an explicit return type
58
- ( syn:: parse_quote! { ( ) } , ident. span ( ) )
59
- } ;
60
- // Install a fake return statement as the first thing in the function
61
- // body, so that we eagerly infer that the return type is what we
62
- // declared in the async fn signature.
63
- // The `#[allow(..)]` is given because the return statement is
64
- // unreachable, but does affect inference, so it needs to be written
65
- // exactly that way for it to do its magic.
66
- let fake_return_edge = quote_spanned ! { return_span=>
67
- #[ allow( unreachable_code, clippy:: diverging_sub_expression, clippy:: let_unit_value) ]
68
- if false {
69
- let __tracing_attr_fake_return: #return_type =
70
- unreachable!( "this is just for type inference, and is unreachable code" ) ;
71
- return __tracing_attr_fake_return;
72
- }
73
- } ;
74
- let block = quote ! {
75
- {
76
- #fake_return_edge
77
- #block
78
- }
79
- } ;
87
+ let async_context =
88
+ asyncness. map ( |_| AsyncContext :: default ( ) . with_fake_return_edge ( ident, output) ) ;
80
89
81
90
let body = gen_block (
82
- & block,
91
+ block,
83
92
params,
84
- asyncness . is_some ( ) ,
93
+ async_context ,
85
94
args,
86
95
instrumented_function_name,
87
96
self_type,
@@ -103,7 +112,7 @@ pub(crate) fn gen_function<'a, B: ToTokens + 'a>(
103
112
fn gen_block < B : ToTokens > (
104
113
block : & B ,
105
114
params : & Punctuated < FnArg , Token ! [ , ] > ,
106
- async_context : bool ,
115
+ async_context : Option < AsyncContext > ,
107
116
mut args : InstrumentArgs ,
108
117
instrumented_function_name : & str ,
109
118
self_type : Option < & TypePath > ,
@@ -257,7 +266,30 @@ fn gen_block<B: ToTokens>(
257
266
// If `err` is in args, instrument any resulting `Err`s.
258
267
// If `ret` is in args, instrument any resulting `Ok`s when the function
259
268
// returns `Result`s, otherwise instrument any resulting values.
260
- if async_context {
269
+ if let Some ( context) = async_context {
270
+ let block = if let Some ( fake_return_edge) = context. fake_return_edge {
271
+ // Install the fake return edge.
272
+ quote ! {
273
+ {
274
+ // Because `quote` produces a stream of tokens _without_ whitespace,
275
+ // the `if` and the block will appear directly next to each other.
276
+ // This generates a clippy lint about suspicious `if/else` formatting.
277
+ // Therefore, suppress the lint inside the generated code...
278
+ #[ allow( clippy:: suspicious_else_formatting) ]
279
+ {
280
+ #fake_return_edge
281
+ // ...but turn the lint back on inside the function body.
282
+ #[ warn( clippy:: suspicious_else_formatting) ]
283
+ #block
284
+ }
285
+ }
286
+ }
287
+ } else {
288
+ // Re-quote the block for type matching.
289
+ quote ! {
290
+ #block
291
+ }
292
+ } ;
261
293
let mk_fut = match ( err_event, ret_event) {
262
294
( Some ( err_event) , Some ( ret_event) ) => quote_spanned ! ( block. span( ) =>
263
295
async move {
@@ -697,7 +729,7 @@ impl<'block> AsyncInfo<'block> {
697
729
let instrumented_block = gen_block (
698
730
& async_expr. block ,
699
731
& self . input . sig . inputs ,
700
- true ,
732
+ Some ( AsyncContext :: default ( ) ) ,
701
733
args,
702
734
instrumented_function_name,
703
735
None ,
0 commit comments