@@ -3,14 +3,16 @@ use clippy_utils::diagnostics::span_lint_and_then;
3
3
use clippy_utils:: source:: snippet;
4
4
use clippy_utils:: { is_from_proc_macro, is_self} ;
5
5
use if_chain:: if_chain;
6
+ use rustc_data_structures:: fx:: FxHashSet ;
6
7
use rustc_errors:: Applicability ;
7
8
use rustc_hir:: intravisit:: FnKind ;
8
9
use rustc_hir:: { Body , FnDecl , HirId , HirIdMap , HirIdSet , Impl , ItemKind , Mutability , Node , PatKind } ;
9
10
use rustc_hir_typeck:: expr_use_visitor as euv;
10
11
use rustc_infer:: infer:: TyCtxtInferExt ;
11
12
use rustc_lint:: { LateContext , LateLintPass } ;
13
+ use rustc_middle:: hir:: map:: associated_body;
12
14
use rustc_middle:: mir:: FakeReadCause ;
13
- use rustc_middle:: ty:: { self , Ty } ;
15
+ use rustc_middle:: ty:: { self , Ty , UpvarId , UpvarPath } ;
14
16
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
15
17
use rustc_span:: def_id:: LocalDefId ;
16
18
use rustc_span:: symbol:: kw;
@@ -102,17 +104,17 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut {
102
104
}
103
105
104
106
let hir_id = cx. tcx . hir ( ) . local_def_id_to_hir_id ( fn_def_id) ;
105
-
106
- match kind {
107
+ let is_async = match kind {
107
108
FnKind :: ItemFn ( .., header) => {
108
109
let attrs = cx. tcx . hir ( ) . attrs ( hir_id) ;
109
110
if header. abi != Abi :: Rust || requires_exact_signature ( attrs) {
110
111
return ;
111
112
}
113
+ header. is_async ( )
112
114
} ,
113
- FnKind :: Method ( ..) => ( ) ,
115
+ FnKind :: Method ( .., sig ) => sig . header . is_async ( ) ,
114
116
FnKind :: Closure => return ,
115
- }
117
+ } ;
116
118
117
119
// Exclude non-inherent impls
118
120
if let Some ( Node :: Item ( item) ) = cx. tcx . hir ( ) . find_parent ( hir_id) {
@@ -128,13 +130,14 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut {
128
130
let fn_sig = cx. tcx . liberate_late_bound_regions ( fn_def_id. to_def_id ( ) , fn_sig) ;
129
131
130
132
// If there are no `&mut` argument, no need to go any further.
131
- if ! decl
133
+ let mut it = decl
132
134
. inputs
133
135
. iter ( )
134
136
. zip ( fn_sig. inputs ( ) )
135
137
. zip ( body. params )
136
- . any ( |( ( & input, & ty) , arg) | !should_skip ( cx, input, ty, arg) )
137
- {
138
+ . filter ( |( ( & input, & ty) , arg) | !should_skip ( cx, input, ty, arg) )
139
+ . peekable ( ) ;
140
+ if it. peek ( ) . is_none ( ) {
138
141
return ;
139
142
}
140
143
@@ -144,19 +147,25 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut {
144
147
let mut ctx = MutablyUsedVariablesCtxt :: default ( ) ;
145
148
let infcx = cx. tcx . infer_ctxt ( ) . build ( ) ;
146
149
euv:: ExprUseVisitor :: new ( & mut ctx, & infcx, fn_def_id, cx. param_env , cx. typeck_results ( ) ) . consume_body ( body) ;
150
+ if is_async {
151
+ let closures = ctx. async_closures . clone ( ) ;
152
+ let hir = cx. tcx . hir ( ) ;
153
+ for closure in closures {
154
+ ctx. prev_bind = None ;
155
+ ctx. prev_move_to_closure . clear ( ) ;
156
+ if let Some ( body) = hir
157
+ . find_by_def_id ( closure)
158
+ . and_then ( associated_body)
159
+ . map ( |( _, body_id) | hir. body ( body_id) )
160
+ {
161
+ euv:: ExprUseVisitor :: new ( & mut ctx, & infcx, closure, cx. param_env , cx. typeck_results ( ) )
162
+ . consume_body ( body) ;
163
+ }
164
+ }
165
+ }
147
166
ctx
148
167
} ;
149
168
150
- let mut it = decl
151
- . inputs
152
- . iter ( )
153
- . zip ( fn_sig. inputs ( ) )
154
- . zip ( body. params )
155
- . filter ( |( ( & input, & ty) , arg) | !should_skip ( cx, input, ty, arg) )
156
- . peekable ( ) ;
157
- if it. peek ( ) . is_none ( ) {
158
- return ;
159
- }
160
169
let show_semver_warning = self . avoid_breaking_exported_api && cx. effective_visibilities . is_exported ( fn_def_id) ;
161
170
for ( ( & input, & _) , arg) in it {
162
171
// Only take `&mut` arguments.
@@ -197,7 +206,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut {
197
206
struct MutablyUsedVariablesCtxt {
198
207
mutably_used_vars : HirIdSet ,
199
208
prev_bind : Option < HirId > ,
209
+ prev_move_to_closure : HirIdSet ,
200
210
aliases : HirIdMap < HirId > ,
211
+ async_closures : FxHashSet < LocalDefId > ,
201
212
}
202
213
203
214
impl MutablyUsedVariablesCtxt {
@@ -213,16 +224,27 @@ impl MutablyUsedVariablesCtxt {
213
224
impl < ' tcx > euv:: Delegate < ' tcx > for MutablyUsedVariablesCtxt {
214
225
fn consume ( & mut self , cmt : & euv:: PlaceWithHirId < ' tcx > , _id : HirId ) {
215
226
if let euv:: Place {
216
- base : euv:: PlaceBase :: Local ( vid) ,
227
+ base :
228
+ euv:: PlaceBase :: Local ( vid)
229
+ | euv:: PlaceBase :: Upvar ( UpvarId {
230
+ var_path : UpvarPath { hir_id : vid } ,
231
+ ..
232
+ } ) ,
217
233
base_ty,
218
234
..
219
235
} = & cmt. place
220
236
{
221
237
if let Some ( bind_id) = self . prev_bind . take ( ) {
222
- self . aliases . insert ( bind_id, * vid) ;
223
- } else if matches ! ( base_ty. ref_mutability( ) , Some ( Mutability :: Mut ) ) {
238
+ if bind_id != * vid {
239
+ self . aliases . insert ( bind_id, * vid) ;
240
+ }
241
+ } else if !self . prev_move_to_closure . contains ( vid)
242
+ && matches ! ( base_ty. ref_mutability( ) , Some ( Mutability :: Mut ) )
243
+ {
224
244
self . add_mutably_used_var ( * vid) ;
225
245
}
246
+ self . prev_bind = None ;
247
+ self . prev_move_to_closure . remove ( vid) ;
226
248
}
227
249
}
228
250
@@ -265,7 +287,30 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt {
265
287
self . prev_bind = None ;
266
288
}
267
289
268
- fn fake_read ( & mut self , _: & rustc_hir_typeck:: expr_use_visitor:: PlaceWithHirId < ' tcx > , _: FakeReadCause , _: HirId ) { }
290
+ fn fake_read (
291
+ & mut self ,
292
+ cmt : & rustc_hir_typeck:: expr_use_visitor:: PlaceWithHirId < ' tcx > ,
293
+ cause : FakeReadCause ,
294
+ _id : HirId ,
295
+ ) {
296
+ if let euv:: Place {
297
+ base :
298
+ euv:: PlaceBase :: Upvar ( UpvarId {
299
+ var_path : UpvarPath { hir_id : vid } ,
300
+ ..
301
+ } ) ,
302
+ ..
303
+ } = & cmt. place
304
+ {
305
+ if let FakeReadCause :: ForLet ( Some ( inner) ) = cause {
306
+ // Seems like we are inside an async function. We need to store the closure `DefId`
307
+ // to go through it afterwards.
308
+ self . async_closures . insert ( inner) ;
309
+ self . aliases . insert ( cmt. hir_id , * vid) ;
310
+ self . prev_move_to_closure . insert ( * vid) ;
311
+ }
312
+ }
313
+ }
269
314
270
315
fn bind ( & mut self , _cmt : & euv:: PlaceWithHirId < ' tcx > , id : HirId ) {
271
316
self . prev_bind = Some ( id) ;
0 commit comments