1
1
use super :: needless_pass_by_value:: requires_exact_signature;
2
2
use clippy_utils:: diagnostics:: span_lint_hir_and_then;
3
3
use clippy_utils:: source:: snippet;
4
+ use clippy_utils:: visitors:: for_each_expr_with_closures;
4
5
use clippy_utils:: { get_parent_node, inherits_cfg, is_from_proc_macro, is_self} ;
5
6
use rustc_data_structures:: fx:: { FxHashSet , FxIndexMap } ;
6
7
use rustc_errors:: Applicability ;
@@ -9,7 +10,7 @@ use rustc_hir::{
9
10
Body , Closure , Expr , ExprKind , FnDecl , HirId , HirIdMap , HirIdSet , Impl , ItemKind , Mutability , Node , PatKind , QPath ,
10
11
} ;
11
12
use rustc_hir_typeck:: expr_use_visitor as euv;
12
- use rustc_infer:: infer:: TyCtxtInferExt ;
13
+ use rustc_infer:: infer:: { InferCtxt , TyCtxtInferExt } ;
13
14
use rustc_lint:: { LateContext , LateLintPass } ;
14
15
use rustc_middle:: hir:: map:: associated_body;
15
16
use rustc_middle:: hir:: nested_filter:: OnlyBodies ;
@@ -21,6 +22,8 @@ use rustc_span::symbol::kw;
21
22
use rustc_span:: Span ;
22
23
use rustc_target:: spec:: abi:: Abi ;
23
24
25
+ use core:: ops:: ControlFlow ;
26
+
24
27
declare_clippy_lint ! {
25
28
/// ### What it does
26
29
/// Check if a `&mut` function argument is actually used mutably.
@@ -95,6 +98,30 @@ fn should_skip<'tcx>(
95
98
is_from_proc_macro ( cx, & input)
96
99
}
97
100
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
+
98
125
impl < ' tcx > LateLintPass < ' tcx > for NeedlessPassByRefMut < ' tcx > {
99
126
fn check_fn (
100
127
& mut self ,
@@ -161,25 +188,22 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
161
188
euv:: ExprUseVisitor :: new ( & mut ctx, & infcx, fn_def_id, cx. param_env , cx. typeck_results ( ) ) . consume_body ( body) ;
162
189
if is_async {
163
190
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
+
164
203
while !ctx. async_closures . is_empty ( ) {
165
- let closures = ctx. async_closures . clone ( ) ;
204
+ let async_closures = ctx. async_closures . clone ( ) ;
166
205
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) ;
183
207
}
184
208
}
185
209
ctx
@@ -244,6 +268,10 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
244
268
struct MutablyUsedVariablesCtxt < ' tcx > {
245
269
mutably_used_vars : HirIdSet ,
246
270
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.
247
275
prev_move_to_closure : HirIdSet ,
248
276
aliases : HirIdMap < HirId > ,
249
277
async_closures : FxHashSet < LocalDefId > ,
0 commit comments