1
1
use proc_macro2:: { Span , TokenStream } ;
2
2
use quote:: { quote, ToTokens } ;
3
3
use syn:: {
4
- parse_quote, punctuated:: Punctuated , Block , FnArg , Lifetime , ReturnType , Signature , Type ,
5
- WhereClause ,
4
+ parse_quote, punctuated:: Punctuated , visit_mut :: VisitMut , Block , Lifetime , Receiver ,
5
+ ReturnType , Signature , TypeReference , WhereClause ,
6
6
} ;
7
7
8
8
use crate :: parse:: { AsyncItem , RecursionArgs } ;
@@ -40,6 +40,63 @@ impl ArgLifetime {
40
40
}
41
41
}
42
42
43
+ #[ derive( Default ) ]
44
+ struct ReferenceVisitor {
45
+ counter : usize ,
46
+ lifetimes : Vec < ArgLifetime > ,
47
+ self_receiver : bool ,
48
+ self_receiver_new_lifetime : bool ,
49
+ self_lifetime : Option < Lifetime > ,
50
+ }
51
+
52
+ impl VisitMut for ReferenceVisitor {
53
+ fn visit_receiver_mut ( & mut self , receiver : & mut Receiver ) {
54
+ self . self_lifetime = Some ( if let Some ( ( _, lt) ) = & mut receiver. reference {
55
+ self . self_receiver = true ;
56
+
57
+ if let Some ( lt) = lt {
58
+ lt. clone ( )
59
+ } else {
60
+ // Use 'life_self to avoid collisions with 'life<count> lifetimes.
61
+ let new_lifetime: Lifetime = parse_quote ! ( ' life_self) ;
62
+ lt. replace ( new_lifetime. clone ( ) ) ;
63
+
64
+ self . self_receiver_new_lifetime = true ;
65
+
66
+ new_lifetime
67
+ }
68
+ } else {
69
+ return ;
70
+ } ) ;
71
+ }
72
+
73
+ fn visit_type_reference_mut ( & mut self , argument : & mut TypeReference ) {
74
+ if argument. lifetime . is_none ( ) {
75
+ // If this reference doesn't have a lifetime (&T) then give it an explicit one.
76
+ let lt = Lifetime :: new ( & format ! ( "'life{}" , self . counter) , Span :: call_site ( ) ) ;
77
+ self . lifetimes . push ( ArgLifetime :: New ( parse_quote ! ( #lt) ) ) ;
78
+ argument. lifetime = Some ( lt) ;
79
+ self . counter += 1 ;
80
+ } else {
81
+ // If it does (&'life T), then keep track of it.
82
+ let lt = argument. lifetime . as_ref ( ) . cloned ( ) . unwrap ( ) ;
83
+
84
+ // Check that this lifetime isn't already in our vector
85
+ let ident_matches = |x : & ArgLifetime | {
86
+ if let ArgLifetime :: Existing ( elt) = x {
87
+ elt. ident == lt. ident
88
+ } else {
89
+ false
90
+ }
91
+ } ;
92
+
93
+ if !self . lifetimes . iter ( ) . any ( ident_matches) {
94
+ self . lifetimes . push ( ArgLifetime :: Existing ( lt) ) ;
95
+ }
96
+ }
97
+ }
98
+ }
99
+
43
100
// Input:
44
101
// async fn f<S, T>(x : S, y : &T) -> Ret;
45
102
//
@@ -55,64 +112,11 @@ fn transform_sig(sig: &mut Signature, args: &RecursionArgs) {
55
112
// Remove the asyncness of this function
56
113
sig. asyncness = None ;
57
114
58
- // Find all reference arguments
59
- let mut ref_arguments = Vec :: new ( ) ;
60
- let mut self_lifetime = None ;
61
-
62
- for arg in & mut sig. inputs {
63
- if let FnArg :: Typed ( pt) = arg {
64
- match pt. ty . as_mut ( ) {
65
- // rustc can give us a None-delimited group if this type comes from
66
- // a macro_rules macro. I don't think this can happen for code the user has written.
67
- Type :: Group ( tg) => {
68
- if let Type :: Reference ( tr) = & mut * tg. elem {
69
- ref_arguments. push ( tr) ;
70
- }
71
- }
72
- Type :: Reference ( tr) => {
73
- ref_arguments. push ( tr) ;
74
- }
75
- _ => { }
76
- }
77
- } else if let FnArg :: Receiver ( recv) = arg {
78
- if let Some ( ( _, slt) ) = & mut recv. reference {
79
- self_lifetime = Some ( slt) ;
80
- }
81
- }
82
- }
83
-
84
- let mut counter = 0 ;
85
- let mut lifetimes = Vec :: new ( ) ;
115
+ // Use a visitor to find reference expressions?
116
+ let mut v = ReferenceVisitor :: default ( ) ;
86
117
87
- if !ref_arguments. is_empty ( ) {
88
- for ra in & mut ref_arguments {
89
- // If this reference arg doesn't have a lifetime, give it an explicit one
90
- if ra. lifetime . is_none ( ) {
91
- let lt = Lifetime :: new ( & format ! ( "'life{counter}" ) , Span :: call_site ( ) ) ;
92
-
93
- lifetimes. push ( ArgLifetime :: New ( parse_quote ! ( #lt) ) ) ;
94
-
95
- ra. lifetime = Some ( lt) ;
96
- counter += 1 ;
97
- } else {
98
- let lt = ra. lifetime . as_ref ( ) . cloned ( ) . unwrap ( ) ;
99
-
100
- // Check that this lifetime isn't already in our vector
101
- let ident_matches = |x : & ArgLifetime | {
102
- if let ArgLifetime :: Existing ( elt) = x {
103
- elt. ident == lt. ident
104
- } else {
105
- false
106
- }
107
- } ;
108
-
109
- if !lifetimes. iter ( ) . any ( ident_matches) {
110
- lifetimes. push ( ArgLifetime :: Existing (
111
- ra. lifetime . as_ref ( ) . cloned ( ) . unwrap ( ) ,
112
- ) ) ;
113
- }
114
- }
115
- }
118
+ for input in & mut sig. inputs {
119
+ v. visit_fn_arg_mut ( input) ;
116
120
}
117
121
118
122
// Does this expansion require `async_recursion to be added to the output
@@ -127,13 +131,13 @@ fn transform_sig(sig: &mut Signature, args: &RecursionArgs) {
127
131
for param in sig. generics . type_params ( ) {
128
132
let ident = param. ident . clone ( ) ;
129
133
where_clause_generics. push ( ident) ;
130
-
131
134
requires_lifetime = true ;
132
135
}
133
136
134
137
// Add an 'a : 'async_recursion bound to any lifetimes 'a appearing in the function
135
- if !lifetimes. is_empty ( ) {
136
- for alt in lifetimes {
138
+ if !v. lifetimes . is_empty ( ) {
139
+ requires_lifetime = true ;
140
+ for alt in v. lifetimes {
137
141
if let ArgLifetime :: New ( lt) = & alt {
138
142
// If this is a new argument,
139
143
sig. generics . params . push ( parse_quote ! ( #lt) ) ;
@@ -143,29 +147,15 @@ fn transform_sig(sig: &mut Signature, args: &RecursionArgs) {
143
147
let lt = alt. lifetime ( ) ;
144
148
where_clause_lifetimes. push ( lt) ;
145
149
}
146
-
147
- requires_lifetime = true ;
148
150
}
149
151
150
152
// If our function accepts &self, then we modify this to the explicit lifetime &'life_self,
151
153
// and add the bound &'life_self : 'async_recursion
152
- if let Some ( slt) = self_lifetime {
153
- let lt = {
154
- if let Some ( lt) = slt. as_mut ( ) {
155
- lt. clone ( )
156
- } else {
157
- // We use `life_self here to avoid any collisions with `life0, `life1 from above
158
- let lt: Lifetime = parse_quote ! ( ' life_self) ;
159
- sig. generics . params . push ( parse_quote ! ( #lt) ) ;
160
-
161
- // add lt to the lifetime of self
162
- * slt = Some ( lt. clone ( ) ) ;
163
-
164
- lt
165
- }
166
- } ;
167
-
168
- where_clause_lifetimes. push ( lt) ;
154
+ if v. self_receiver {
155
+ if v. self_receiver_new_lifetime {
156
+ sig. generics . params . push ( parse_quote ! ( ' life_self) ) ;
157
+ }
158
+ where_clause_lifetimes. extend ( v. self_lifetime ) ;
169
159
requires_lifetime = true ;
170
160
}
171
161
0 commit comments