@@ -5,14 +5,16 @@ use clippy_utils::{get_parent_node, inherits_cfg, is_from_proc_macro, is_self};
5
5
use rustc_data_structures:: fx:: { FxHashSet , FxIndexMap } ;
6
6
use rustc_errors:: Applicability ;
7
7
use rustc_hir:: intravisit:: { walk_qpath, FnKind , Visitor } ;
8
- use rustc_hir:: { Body , ExprKind , FnDecl , HirId , HirIdMap , HirIdSet , Impl , ItemKind , Mutability , Node , PatKind , QPath } ;
8
+ use rustc_hir:: {
9
+ Body , Closure , Expr , ExprKind , FnDecl , HirId , HirIdMap , HirIdSet , Impl , ItemKind , Mutability , Node , PatKind , QPath ,
10
+ } ;
9
11
use rustc_hir_typeck:: expr_use_visitor as euv;
10
12
use rustc_infer:: infer:: TyCtxtInferExt ;
11
13
use rustc_lint:: { LateContext , LateLintPass } ;
12
14
use rustc_middle:: hir:: map:: associated_body;
13
15
use rustc_middle:: hir:: nested_filter:: OnlyBodies ;
14
16
use rustc_middle:: mir:: FakeReadCause ;
15
- use rustc_middle:: ty:: { self , Ty , UpvarId , UpvarPath } ;
17
+ use rustc_middle:: ty:: { self , Ty , TyCtxt , UpvarId , UpvarPath } ;
16
18
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
17
19
use rustc_span:: def_id:: LocalDefId ;
18
20
use rustc_span:: symbol:: kw;
@@ -147,22 +149,36 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
147
149
// Collect variables mutably used and spans which will need dereferencings from the
148
150
// function body.
149
151
let MutablyUsedVariablesCtxt { mutably_used_vars, .. } = {
150
- let mut ctx = MutablyUsedVariablesCtxt :: default ( ) ;
152
+ let mut ctx = MutablyUsedVariablesCtxt {
153
+ mutably_used_vars : HirIdSet :: default ( ) ,
154
+ prev_bind : None ,
155
+ prev_move_to_closure : HirIdSet :: default ( ) ,
156
+ aliases : HirIdMap :: default ( ) ,
157
+ async_closures : FxHashSet :: default ( ) ,
158
+ tcx : cx. tcx ,
159
+ } ;
151
160
let infcx = cx. tcx . infer_ctxt ( ) . build ( ) ;
152
161
euv:: ExprUseVisitor :: new ( & mut ctx, & infcx, fn_def_id, cx. param_env , cx. typeck_results ( ) ) . consume_body ( body) ;
153
162
if is_async {
154
- let closures = ctx. async_closures . clone ( ) ;
155
- let hir = cx. tcx . hir ( ) ;
156
- for closure in closures {
157
- ctx. prev_bind = None ;
158
- ctx. prev_move_to_closure . clear ( ) ;
159
- if let Some ( body) = hir
160
- . find_by_def_id ( closure)
161
- . and_then ( associated_body)
162
- . map ( |( _, body_id) | hir. body ( body_id) )
163
- {
164
- euv:: ExprUseVisitor :: new ( & mut ctx, & infcx, closure, cx. param_env , cx. typeck_results ( ) )
165
- . consume_body ( body) ;
163
+ let mut checked_closures = FxHashSet :: default ( ) ;
164
+ while !ctx. async_closures . is_empty ( ) {
165
+ let closures = ctx. async_closures . clone ( ) ;
166
+ ctx. async_closures . clear ( ) ;
167
+ let hir = cx. tcx . hir ( ) ;
168
+ for closure in closures {
169
+ if !checked_closures. insert ( closure) {
170
+ continue ;
171
+ }
172
+ ctx. prev_bind = None ;
173
+ ctx. prev_move_to_closure . clear ( ) ;
174
+ if let Some ( body) = hir
175
+ . find_by_def_id ( closure)
176
+ . and_then ( associated_body)
177
+ . map ( |( _, body_id) | hir. body ( body_id) )
178
+ {
179
+ euv:: ExprUseVisitor :: new ( & mut ctx, & infcx, closure, cx. param_env , cx. typeck_results ( ) )
180
+ . consume_body ( body) ;
181
+ }
166
182
}
167
183
}
168
184
}
@@ -225,26 +241,44 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
225
241
}
226
242
}
227
243
228
- #[ derive( Default ) ]
229
- struct MutablyUsedVariablesCtxt {
244
+ struct MutablyUsedVariablesCtxt < ' tcx > {
230
245
mutably_used_vars : HirIdSet ,
231
246
prev_bind : Option < HirId > ,
232
247
prev_move_to_closure : HirIdSet ,
233
248
aliases : HirIdMap < HirId > ,
234
249
async_closures : FxHashSet < LocalDefId > ,
250
+ tcx : TyCtxt < ' tcx > ,
235
251
}
236
252
237
- impl MutablyUsedVariablesCtxt {
253
+ impl < ' tcx > MutablyUsedVariablesCtxt < ' tcx > {
238
254
fn add_mutably_used_var ( & mut self , mut used_id : HirId ) {
239
255
while let Some ( id) = self . aliases . get ( & used_id) {
240
256
self . mutably_used_vars . insert ( used_id) ;
241
257
used_id = * id;
242
258
}
243
259
self . mutably_used_vars . insert ( used_id) ;
244
260
}
261
+
262
+ fn would_be_alias_cycle ( & self , alias : HirId , mut target : HirId ) -> bool {
263
+ while let Some ( id) = self . aliases . get ( & target) {
264
+ if * id == alias {
265
+ return true ;
266
+ }
267
+ target = * id;
268
+ }
269
+ false
270
+ }
271
+
272
+ fn add_alias ( & mut self , alias : HirId , target : HirId ) {
273
+ // This is to prevent alias loop.
274
+ if alias == target || self . would_be_alias_cycle ( alias, target) {
275
+ return ;
276
+ }
277
+ self . aliases . insert ( alias, target) ;
278
+ }
245
279
}
246
280
247
- impl < ' tcx > euv:: Delegate < ' tcx > for MutablyUsedVariablesCtxt {
281
+ impl < ' tcx > euv:: Delegate < ' tcx > for MutablyUsedVariablesCtxt < ' tcx > {
248
282
fn consume ( & mut self , cmt : & euv:: PlaceWithHirId < ' tcx > , _id : HirId ) {
249
283
if let euv:: Place {
250
284
base :
@@ -259,7 +293,7 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt {
259
293
{
260
294
if let Some ( bind_id) = self . prev_bind . take ( ) {
261
295
if bind_id != * vid {
262
- self . aliases . insert ( bind_id, * vid) ;
296
+ self . add_alias ( bind_id, * vid) ;
263
297
}
264
298
} else if !self . prev_move_to_closure . contains ( vid)
265
299
&& matches ! ( base_ty. ref_mutability( ) , Some ( Mutability :: Mut ) )
@@ -289,14 +323,38 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt {
289
323
{
290
324
self . add_mutably_used_var ( * vid) ;
291
325
}
326
+ } else if borrow == ty:: ImmBorrow {
327
+ // If there is an `async block`, it'll contain a call to a closure which we need to
328
+ // go into to ensure all "mutate" checks are found.
329
+ if let Node :: Expr ( Expr {
330
+ kind :
331
+ ExprKind :: Call (
332
+ _,
333
+ [
334
+ Expr {
335
+ kind : ExprKind :: Closure ( Closure { def_id, .. } ) ,
336
+ ..
337
+ } ,
338
+ ] ,
339
+ ) ,
340
+ ..
341
+ } ) = self . tcx . hir ( ) . get ( cmt. hir_id )
342
+ {
343
+ self . async_closures . insert ( * def_id) ;
344
+ }
292
345
}
293
346
}
294
347
295
348
fn mutate ( & mut self , cmt : & euv:: PlaceWithHirId < ' tcx > , _id : HirId ) {
296
349
self . prev_bind = None ;
297
350
if let euv:: Place {
298
351
projections,
299
- base : euv:: PlaceBase :: Local ( vid) ,
352
+ base :
353
+ euv:: PlaceBase :: Local ( vid)
354
+ | euv:: PlaceBase :: Upvar ( UpvarId {
355
+ var_path : UpvarPath { hir_id : vid } ,
356
+ ..
357
+ } ) ,
300
358
..
301
359
} = & cmt. place
302
360
{
@@ -329,8 +387,9 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt {
329
387
// Seems like we are inside an async function. We need to store the closure `DefId`
330
388
// to go through it afterwards.
331
389
self . async_closures . insert ( inner) ;
332
- self . aliases . insert ( cmt. hir_id , * vid) ;
390
+ self . add_alias ( cmt. hir_id , * vid) ;
333
391
self . prev_move_to_closure . insert ( * vid) ;
392
+ self . prev_bind = None ;
334
393
}
335
394
}
336
395
}
0 commit comments