@@ -3,14 +3,16 @@ use clippy_utils::diagnostics::span_lint_and_then;
33use clippy_utils:: source:: snippet;
44use clippy_utils:: { is_from_proc_macro, is_self} ;
55use if_chain:: if_chain;
6+ use rustc_data_structures:: fx:: FxHashSet ;
67use rustc_errors:: Applicability ;
78use rustc_hir:: intravisit:: FnKind ;
89use rustc_hir:: { Body , FnDecl , HirId , HirIdMap , HirIdSet , Impl , ItemKind , Mutability , Node , PatKind } ;
910use rustc_hir_typeck:: expr_use_visitor as euv;
1011use rustc_infer:: infer:: TyCtxtInferExt ;
1112use rustc_lint:: { LateContext , LateLintPass } ;
13+ use rustc_middle:: hir:: map:: associated_body;
1214use rustc_middle:: mir:: FakeReadCause ;
13- use rustc_middle:: ty:: { self , Ty } ;
15+ use rustc_middle:: ty:: { self , Ty , UpvarId , UpvarPath } ;
1416use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
1517use rustc_span:: def_id:: LocalDefId ;
1618use rustc_span:: symbol:: kw;
@@ -102,17 +104,17 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut {
102104 }
103105
104106 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 {
107108 FnKind :: ItemFn ( .., header) => {
108109 let attrs = cx. tcx . hir ( ) . attrs ( hir_id) ;
109110 if header. abi != Abi :: Rust || requires_exact_signature ( attrs) {
110111 return ;
111112 }
113+ header. is_async ( )
112114 } ,
113- FnKind :: Method ( ..) => ( ) ,
115+ FnKind :: Method ( .., sig ) => sig . header . is_async ( ) ,
114116 FnKind :: Closure => return ,
115- }
117+ } ;
116118
117119 // Exclude non-inherent impls
118120 if let Some ( Node :: Item ( item) ) = cx. tcx . hir ( ) . find_parent ( hir_id) {
@@ -128,13 +130,14 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut {
128130 let fn_sig = cx. tcx . liberate_late_bound_regions ( fn_def_id. to_def_id ( ) , fn_sig) ;
129131
130132 // If there are no `&mut` argument, no need to go any further.
131- if ! decl
133+ let mut it = decl
132134 . inputs
133135 . iter ( )
134136 . zip ( fn_sig. inputs ( ) )
135137 . 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 ( ) {
138141 return ;
139142 }
140143
@@ -144,19 +147,25 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut {
144147 let mut ctx = MutablyUsedVariablesCtxt :: default ( ) ;
145148 let infcx = cx. tcx . infer_ctxt ( ) . build ( ) ;
146149 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+ }
147166 ctx
148167 } ;
149168
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- }
160169 let show_semver_warning = self . avoid_breaking_exported_api && cx. effective_visibilities . is_exported ( fn_def_id) ;
161170 for ( ( & input, & _) , arg) in it {
162171 // Only take `&mut` arguments.
@@ -197,7 +206,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut {
197206struct MutablyUsedVariablesCtxt {
198207 mutably_used_vars : HirIdSet ,
199208 prev_bind : Option < HirId > ,
209+ prev_move_to_closure : HirIdSet ,
200210 aliases : HirIdMap < HirId > ,
211+ async_closures : FxHashSet < LocalDefId > ,
201212}
202213
203214impl MutablyUsedVariablesCtxt {
@@ -213,16 +224,27 @@ impl MutablyUsedVariablesCtxt {
213224impl < ' tcx > euv:: Delegate < ' tcx > for MutablyUsedVariablesCtxt {
214225 fn consume ( & mut self , cmt : & euv:: PlaceWithHirId < ' tcx > , _id : HirId ) {
215226 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+ } ) ,
217233 base_ty,
218234 ..
219235 } = & cmt. place
220236 {
221237 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+ {
224244 self . add_mutably_used_var ( * vid) ;
225245 }
246+ self . prev_bind = None ;
247+ self . prev_move_to_closure . remove ( vid) ;
226248 }
227249 }
228250
@@ -265,7 +287,30 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt {
265287 self . prev_bind = None ;
266288 }
267289
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+ }
269314
270315 fn bind ( & mut self , _cmt : & euv:: PlaceWithHirId < ' tcx > , id : HirId ) {
271316 self . prev_bind = Some ( id) ;
0 commit comments