Skip to content

Commit c98ce18

Browse files
authored
fix: needless_for_each suggests wrongly with explicit closure input types (#15595)
Fixes rust-lang/rust-clippy#9912 changelog: [`needless_for_each`]: fix wrong suggestion with explicit closure input types
2 parents 3f74c96 + 04c6fb7 commit c98ce18

File tree

3 files changed

+76
-17
lines changed

3 files changed

+76
-17
lines changed

clippy_lints/src/needless_for_each.rs

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use rustc_errors::Applicability;
22
use rustc_hir::intravisit::{Visitor, walk_expr};
3-
use rustc_hir::{Block, BlockCheckMode, Closure, Expr, ExprKind, Stmt, StmtKind};
3+
use rustc_hir::{Block, BlockCheckMode, Closure, Expr, ExprKind, Stmt, StmtKind, TyKind};
44
use rustc_lint::{LateContext, LateLintPass};
55
use rustc_session::declare_lint_pass;
66
use rustc_span::Span;
@@ -70,12 +70,24 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach {
7070
&& has_iter_method(cx, cx.typeck_results().expr_ty(iter_recv)).is_some()
7171
// Skip the lint if the body is not block because this is simpler than `for` loop.
7272
// e.g. `v.iter().for_each(f)` is simpler and clearer than using `for` loop.
73-
&& let ExprKind::Closure(&Closure { body, .. }) = for_each_arg.kind
73+
&& let ExprKind::Closure(&Closure { body, fn_decl, .. }) = for_each_arg.kind
7474
&& let body = cx.tcx.hir_body(body)
7575
// Skip the lint if the body is not safe, so as not to suggest `for … in … unsafe {}`
7676
// and suggesting `for … in … { unsafe { } }` is a little ugly.
7777
&& !matches!(body.value.kind, ExprKind::Block(Block { rules: BlockCheckMode::UnsafeBlock(_), .. }, ..))
7878
{
79+
let mut applicability = Applicability::MachineApplicable;
80+
81+
// If any closure parameter has an explicit type specified, applying the lint would necessarily
82+
// remove that specification, possibly breaking type inference
83+
if fn_decl
84+
.inputs
85+
.iter()
86+
.any(|input| matches!(input.kind, TyKind::Infer(..)))
87+
{
88+
applicability = Applicability::MaybeIncorrect;
89+
}
90+
7991
let mut ret_collector = RetCollector::default();
8092
ret_collector.visit_expr(body.value);
8193

@@ -84,18 +96,16 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach {
8496
return;
8597
}
8698

87-
let (mut applicability, ret_suggs) = if ret_collector.spans.is_empty() {
88-
(Applicability::MachineApplicable, None)
99+
let ret_suggs = if ret_collector.spans.is_empty() {
100+
None
89101
} else {
90-
(
91-
Applicability::MaybeIncorrect,
92-
Some(
93-
ret_collector
94-
.spans
95-
.into_iter()
96-
.map(|span| (span, "continue".to_string()))
97-
.collect(),
98-
),
102+
applicability = Applicability::MaybeIncorrect;
103+
Some(
104+
ret_collector
105+
.spans
106+
.into_iter()
107+
.map(|span| (span, "continue".to_string()))
108+
.collect(),
99109
)
100110
};
101111

tests/ui/needless_for_each_unfixable.rs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//@no-rustfix: overlapping suggestions
22
#![warn(clippy::needless_for_each)]
3-
#![allow(clippy::needless_return, clippy::uninlined_format_args)]
3+
#![allow(clippy::needless_return)]
44

55
fn main() {
66
let v: Vec<i32> = Vec::new();
@@ -11,7 +11,22 @@ fn main() {
1111
if *v == 10 {
1212
return;
1313
} else {
14-
println!("{}", v);
14+
println!("{v}");
1515
}
1616
});
1717
}
18+
19+
fn issue9912() {
20+
let mut i = 0;
21+
// Changing this to a `for` loop would break type inference
22+
[].iter().for_each(move |_: &i32| {
23+
//~^ needless_for_each
24+
i += 1;
25+
});
26+
27+
// Changing this would actually be okay, but we still suggest `MaybeIncorrect`ly
28+
[1i32].iter().for_each(move |_: &i32| {
29+
//~^ needless_for_each
30+
i += 1;
31+
});
32+
}

tests/ui/needless_for_each_unfixable.stderr

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ LL +
1919
LL + if *v == 10 {
2020
LL + return;
2121
LL + } else {
22-
LL + println!("{}", v);
22+
LL + println!("{v}");
2323
LL + }
2424
LL + }
2525
|
@@ -29,5 +29,39 @@ LL - return;
2929
LL + continue;
3030
|
3131

32-
error: aborting due to 1 previous error
32+
error: needless use of `for_each`
33+
--> tests/ui/needless_for_each_unfixable.rs:22:5
34+
|
35+
LL | / [].iter().for_each(move |_: &i32| {
36+
LL | |
37+
LL | | i += 1;
38+
LL | | });
39+
| |_______^
40+
|
41+
help: try
42+
|
43+
LL ~ for _ in [].iter() {
44+
LL +
45+
LL + i += 1;
46+
LL + }
47+
|
48+
49+
error: needless use of `for_each`
50+
--> tests/ui/needless_for_each_unfixable.rs:28:5
51+
|
52+
LL | / [1i32].iter().for_each(move |_: &i32| {
53+
LL | |
54+
LL | | i += 1;
55+
LL | | });
56+
| |_______^
57+
|
58+
help: try
59+
|
60+
LL ~ for _ in [1i32].iter() {
61+
LL +
62+
LL + i += 1;
63+
LL + }
64+
|
65+
66+
error: aborting due to 3 previous errors
3367

0 commit comments

Comments
 (0)