Skip to content

Commit 38ba055

Browse files
committed
Auto merge of #8676 - Alexendoo:local-used-across-loop, r=xFrednet
Check for loops/closures in `local_used_after_expr` Follow up to #8646, catches when a local is used multiple times because it's in a loop or a closure changelog: none
2 parents b6645d0 + 5e335a5 commit 38ba055

File tree

7 files changed

+77
-7
lines changed

7 files changed

+77
-7
lines changed

clippy_lints/src/eta_reduction.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use clippy_utils::higher::VecArgs;
33
use clippy_utils::source::snippet_opt;
44
use clippy_utils::ty::is_type_diagnostic_item;
55
use clippy_utils::usage::local_used_after_expr;
6-
use clippy_utils::{get_enclosing_loop_or_closure, higher, path_to_local, path_to_local_id};
6+
use clippy_utils::{higher, path_to_local, path_to_local_id};
77
use if_chain::if_chain;
88
use rustc_errors::Applicability;
99
use rustc_hir::def_id::DefId;
@@ -125,8 +125,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
125125
if_chain! {
126126
if let ty::Closure(_, substs) = callee_ty.peel_refs().kind();
127127
if substs.as_closure().kind() == ClosureKind::FnMut;
128-
if get_enclosing_loop_or_closure(cx.tcx, expr).is_some()
129-
|| path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, callee));
128+
if path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, expr));
130129

131130
then {
132131
// Mutable closure is used after current expr; we cannot consume it.

clippy_utils/src/usage.rs

+31-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::visitors::{expr_visitor, expr_visitor_no_bodies};
33
use rustc_hir as hir;
44
use rustc_hir::intravisit::{self, Visitor};
55
use rustc_hir::HirIdSet;
6-
use rustc_hir::{Expr, ExprKind, HirId};
6+
use rustc_hir::{Expr, ExprKind, HirId, Node};
77
use rustc_infer::infer::TyCtxtInferExt;
88
use rustc_lint::LateContext;
99
use rustc_middle::hir::nested_filter;
@@ -169,6 +169,32 @@ pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool {
169169

170170
pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr<'_>) -> bool {
171171
let Some(block) = utils::get_enclosing_block(cx, local_id) else { return false };
172+
173+
// for _ in 1..3 {
174+
// local
175+
// }
176+
//
177+
// let closure = || local;
178+
// closure();
179+
// closure();
180+
let in_loop_or_closure = cx
181+
.tcx
182+
.hir()
183+
.parent_iter(after.hir_id)
184+
.take_while(|&(id, _)| id != block.hir_id)
185+
.any(|(_, node)| {
186+
matches!(
187+
node,
188+
Node::Expr(Expr {
189+
kind: ExprKind::Loop(..) | ExprKind::Closure(..),
190+
..
191+
})
192+
)
193+
});
194+
if in_loop_or_closure {
195+
return true;
196+
}
197+
172198
let mut used_after_expr = false;
173199
let mut past_expr = false;
174200
expr_visitor(cx, |expr| {
@@ -178,7 +204,10 @@ pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr
178204

179205
if expr.hir_id == after.hir_id {
180206
past_expr = true;
181-
} else if past_expr && utils::path_to_local_id(expr, local_id) {
207+
return false;
208+
}
209+
210+
if past_expr && utils::path_to_local_id(expr, local_id) {
182211
used_after_expr = true;
183212
}
184213
!used_after_expr

tests/ui/eta.fixed

+4
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,10 @@ fn mutable_closure_in_loop() {
211211
let mut closure = |n| value += n;
212212
for _ in 0..5 {
213213
Some(1).map(&mut closure);
214+
215+
let mut value = 0;
216+
let mut in_loop = |n| value += n;
217+
Some(1).map(in_loop);
214218
}
215219
}
216220

tests/ui/eta.rs

+4
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,10 @@ fn mutable_closure_in_loop() {
211211
let mut closure = |n| value += n;
212212
for _ in 0..5 {
213213
Some(1).map(|n| closure(n));
214+
215+
let mut value = 0;
216+
let mut in_loop = |n| value += n;
217+
Some(1).map(|n| in_loop(n));
214218
}
215219
}
216220

tests/ui/eta.stderr

+8-2
Original file line numberDiff line numberDiff line change
@@ -117,10 +117,16 @@ LL | Some(1).map(|n| closure(n));
117117
| ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut closure`
118118

119119
error: redundant closure
120-
--> $DIR/eta.rs:232:21
120+
--> $DIR/eta.rs:217:21
121+
|
122+
LL | Some(1).map(|n| in_loop(n));
123+
| ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `in_loop`
124+
125+
error: redundant closure
126+
--> $DIR/eta.rs:236:21
121127
|
122128
LL | map_str_to_path(|s| s.as_ref());
123129
| ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::convert::AsRef::as_ref`
124130

125-
error: aborting due to 20 previous errors
131+
error: aborting due to 21 previous errors
126132

tests/ui/needless_option_as_deref.fixed

+14
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,20 @@ fn main() {
1616
let _ = Some(Box::new(1)).as_deref();
1717
let _ = Some(Box::new(1)).as_deref_mut();
1818

19+
let mut y = 0;
20+
let mut x = Some(&mut y);
21+
for _ in 0..3 {
22+
let _ = x.as_deref_mut();
23+
}
24+
25+
let mut y = 0;
26+
let mut x = Some(&mut y);
27+
let mut closure = || {
28+
let _ = x.as_deref_mut();
29+
};
30+
closure();
31+
closure();
32+
1933
// #7846
2034
let mut i = 0;
2135
let mut opt_vec = vec![Some(&mut i)];

tests/ui/needless_option_as_deref.rs

+14
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,20 @@ fn main() {
1616
let _ = Some(Box::new(1)).as_deref();
1717
let _ = Some(Box::new(1)).as_deref_mut();
1818

19+
let mut y = 0;
20+
let mut x = Some(&mut y);
21+
for _ in 0..3 {
22+
let _ = x.as_deref_mut();
23+
}
24+
25+
let mut y = 0;
26+
let mut x = Some(&mut y);
27+
let mut closure = || {
28+
let _ = x.as_deref_mut();
29+
};
30+
closure();
31+
closure();
32+
1933
// #7846
2034
let mut i = 0;
2135
let mut opt_vec = vec![Some(&mut i)];

0 commit comments

Comments
 (0)