@@ -5,14 +5,16 @@ use clippy_utils::{get_parent_node, inherits_cfg, is_from_proc_macro, is_self};
55use rustc_data_structures:: fx:: { FxHashSet , FxIndexMap } ;
66use rustc_errors:: Applicability ;
77use 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+ } ;
911use rustc_hir_typeck:: expr_use_visitor as euv;
1012use rustc_infer:: infer:: TyCtxtInferExt ;
1113use rustc_lint:: { LateContext , LateLintPass } ;
1214use rustc_middle:: hir:: map:: associated_body;
1315use rustc_middle:: hir:: nested_filter:: OnlyBodies ;
1416use rustc_middle:: mir:: FakeReadCause ;
15- use rustc_middle:: ty:: { self , Ty , UpvarId , UpvarPath } ;
17+ use rustc_middle:: ty:: { self , Ty , TyCtxt , UpvarId , UpvarPath } ;
1618use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
1719use rustc_span:: def_id:: LocalDefId ;
1820use rustc_span:: symbol:: kw;
@@ -147,22 +149,36 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
147149 // Collect variables mutably used and spans which will need dereferencings from the
148150 // function body.
149151 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+ } ;
151160 let infcx = cx. tcx . infer_ctxt ( ) . build ( ) ;
152161 euv:: ExprUseVisitor :: new ( & mut ctx, & infcx, fn_def_id, cx. param_env , cx. typeck_results ( ) ) . consume_body ( body) ;
153162 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+ }
166182 }
167183 }
168184 }
@@ -225,26 +241,44 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
225241 }
226242}
227243
228- #[ derive( Default ) ]
229- struct MutablyUsedVariablesCtxt {
244+ struct MutablyUsedVariablesCtxt < ' tcx > {
230245 mutably_used_vars : HirIdSet ,
231246 prev_bind : Option < HirId > ,
232247 prev_move_to_closure : HirIdSet ,
233248 aliases : HirIdMap < HirId > ,
234249 async_closures : FxHashSet < LocalDefId > ,
250+ tcx : TyCtxt < ' tcx > ,
235251}
236252
237- impl MutablyUsedVariablesCtxt {
253+ impl < ' tcx > MutablyUsedVariablesCtxt < ' tcx > {
238254 fn add_mutably_used_var ( & mut self , mut used_id : HirId ) {
239255 while let Some ( id) = self . aliases . get ( & used_id) {
240256 self . mutably_used_vars . insert ( used_id) ;
241257 used_id = * id;
242258 }
243259 self . mutably_used_vars . insert ( used_id) ;
244260 }
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+ }
245279}
246280
247- impl < ' tcx > euv:: Delegate < ' tcx > for MutablyUsedVariablesCtxt {
281+ impl < ' tcx > euv:: Delegate < ' tcx > for MutablyUsedVariablesCtxt < ' tcx > {
248282 fn consume ( & mut self , cmt : & euv:: PlaceWithHirId < ' tcx > , _id : HirId ) {
249283 if let euv:: Place {
250284 base :
@@ -259,7 +293,7 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt {
259293 {
260294 if let Some ( bind_id) = self . prev_bind . take ( ) {
261295 if bind_id != * vid {
262- self . aliases . insert ( bind_id, * vid) ;
296+ self . add_alias ( bind_id, * vid) ;
263297 }
264298 } else if !self . prev_move_to_closure . contains ( vid)
265299 && matches ! ( base_ty. ref_mutability( ) , Some ( Mutability :: Mut ) )
@@ -289,14 +323,38 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt {
289323 {
290324 self . add_mutably_used_var ( * vid) ;
291325 }
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+ }
292345 }
293346 }
294347
295348 fn mutate ( & mut self , cmt : & euv:: PlaceWithHirId < ' tcx > , _id : HirId ) {
296349 self . prev_bind = None ;
297350 if let euv:: Place {
298351 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+ } ) ,
300358 ..
301359 } = & cmt. place
302360 {
@@ -329,8 +387,9 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt {
329387 // Seems like we are inside an async function. We need to store the closure `DefId`
330388 // to go through it afterwards.
331389 self . async_closures . insert ( inner) ;
332- self . aliases . insert ( cmt. hir_id , * vid) ;
390+ self . add_alias ( cmt. hir_id , * vid) ;
333391 self . prev_move_to_closure . insert ( * vid) ;
392+ self . prev_bind = None ;
334393 }
335394 }
336395 }
0 commit comments