11use super :: needless_pass_by_value:: requires_exact_signature;
22use clippy_utils:: diagnostics:: span_lint_hir_and_then;
33use clippy_utils:: source:: snippet;
4+ use clippy_utils:: visitors:: for_each_expr_with_closures;
45use clippy_utils:: { get_parent_node, inherits_cfg, is_from_proc_macro, is_self} ;
56use rustc_data_structures:: fx:: { FxHashSet , FxIndexMap } ;
67use rustc_errors:: Applicability ;
@@ -9,7 +10,7 @@ use rustc_hir::{
910 Body , Closure , Expr , ExprKind , FnDecl , HirId , HirIdMap , HirIdSet , Impl , ItemKind , Mutability , Node , PatKind , QPath ,
1011} ;
1112use rustc_hir_typeck:: expr_use_visitor as euv;
12- use rustc_infer:: infer:: TyCtxtInferExt ;
13+ use rustc_infer:: infer:: { InferCtxt , TyCtxtInferExt } ;
1314use rustc_lint:: { LateContext , LateLintPass } ;
1415use rustc_middle:: hir:: map:: associated_body;
1516use rustc_middle:: hir:: nested_filter:: OnlyBodies ;
@@ -21,6 +22,8 @@ use rustc_span::symbol::kw;
2122use rustc_span:: Span ;
2223use rustc_target:: spec:: abi:: Abi ;
2324
25+ use core:: ops:: ControlFlow ;
26+
2427declare_clippy_lint ! {
2528 /// ### What it does
2629 /// Check if a `&mut` function argument is actually used mutably.
@@ -95,6 +98,30 @@ fn should_skip<'tcx>(
9598 is_from_proc_macro ( cx, & input)
9699}
97100
101+ fn check_closures < ' tcx > (
102+ ctx : & mut MutablyUsedVariablesCtxt < ' tcx > ,
103+ cx : & LateContext < ' tcx > ,
104+ infcx : & InferCtxt < ' tcx > ,
105+ checked_closures : & mut FxHashSet < LocalDefId > ,
106+ closures : FxHashSet < LocalDefId > ,
107+ ) {
108+ let hir = cx. tcx . hir ( ) ;
109+ for closure in closures {
110+ if !checked_closures. insert ( closure) {
111+ continue ;
112+ }
113+ ctx. prev_bind = None ;
114+ ctx. prev_move_to_closure . clear ( ) ;
115+ if let Some ( body) = hir
116+ . find_by_def_id ( closure)
117+ . and_then ( associated_body)
118+ . map ( |( _, body_id) | hir. body ( body_id) )
119+ {
120+ euv:: ExprUseVisitor :: new ( ctx, infcx, closure, cx. param_env , cx. typeck_results ( ) ) . consume_body ( body) ;
121+ }
122+ }
123+ }
124+
98125impl < ' tcx > LateLintPass < ' tcx > for NeedlessPassByRefMut < ' tcx > {
99126 fn check_fn (
100127 & mut self ,
@@ -161,25 +188,22 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
161188 euv:: ExprUseVisitor :: new ( & mut ctx, & infcx, fn_def_id, cx. param_env , cx. typeck_results ( ) ) . consume_body ( body) ;
162189 if is_async {
163190 let mut checked_closures = FxHashSet :: default ( ) ;
191+
192+ // We retrieve all the closures declared in the async function because they will
193+ // not be found by `euv::Delegate`.
194+ let mut closures: FxHashSet < LocalDefId > = FxHashSet :: default ( ) ;
195+ for_each_expr_with_closures ( cx, body, |expr| {
196+ if let ExprKind :: Closure ( closure) = expr. kind {
197+ closures. insert ( closure. def_id ) ;
198+ }
199+ ControlFlow :: < ( ) > :: Continue ( ( ) )
200+ } ) ;
201+ check_closures ( & mut ctx, cx, & infcx, & mut checked_closures, closures) ;
202+
164203 while !ctx. async_closures . is_empty ( ) {
165- let closures = ctx. async_closures . clone ( ) ;
204+ let async_closures = ctx. async_closures . clone ( ) ;
166205 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- }
182- }
206+ check_closures ( & mut ctx, cx, & infcx, & mut checked_closures, async_closures) ;
183207 }
184208 }
185209 ctx
@@ -244,6 +268,10 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
244268struct MutablyUsedVariablesCtxt < ' tcx > {
245269 mutably_used_vars : HirIdSet ,
246270 prev_bind : Option < HirId > ,
271+ /// In async functions, the inner AST is composed of multiple layers until we reach the code
272+ /// defined by the user. Because of that, some variables are marked as mutably borrowed even
273+ /// though they're not. This field lists the `HirId` that should not be considered as mutable
274+ /// use of a variable.
247275 prev_move_to_closure : HirIdSet ,
248276 aliases : HirIdMap < HirId > ,
249277 async_closures : FxHashSet < LocalDefId > ,
0 commit comments