Skip to content

Commit b527468

Browse files
authored
Fix needless_for_each FN when for_each is in the expr of a block (rust-lang#16295)
Closes rust-lang#16294 changelog: [`needless_for_each`] fix FN when `for_each` is in the expr of a block
2 parents 86eaeaa + 21eaa04 commit b527468

File tree

4 files changed

+107
-57
lines changed

4 files changed

+107
-57
lines changed

clippy_lints/src/needless_for_each.rs

Lines changed: 73 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,20 @@ declare_lint_pass!(NeedlessForEach => [NEEDLESS_FOR_EACH]);
5656

5757
impl<'tcx> LateLintPass<'tcx> for NeedlessForEach {
5858
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
59-
if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind
60-
&& let ExprKind::MethodCall(method_name, for_each_recv, [for_each_arg], _) = expr.kind
59+
if let StmtKind::Expr(expr) | StmtKind::Semi(expr) = stmt.kind {
60+
check_expr(cx, expr, stmt.span);
61+
}
62+
}
63+
64+
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
65+
if let Some(expr) = block.expr {
66+
check_expr(cx, expr, expr.span);
67+
}
68+
}
69+
}
70+
71+
fn check_expr(cx: &LateContext<'_>, expr: &Expr<'_>, outer_span: Span) {
72+
if let ExprKind::MethodCall(method_name, for_each_recv, [for_each_arg], _) = expr.kind
6173
&& let ExprKind::MethodCall(_, iter_recv, [], _) = for_each_recv.kind
6274
// Skip the lint if the call chain is too long. e.g. `v.field.iter().for_each()` or
6375
// `v.foo().iter().for_each()` must be skipped.
@@ -76,69 +88,74 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach {
7688
// Skip the lint if the body is not safe, so as not to suggest `for … in … unsafe {}`
7789
// and suggesting `for … in … { unsafe { } }` is a little ugly.
7890
&& !matches!(body.value.kind, ExprKind::Block(Block { rules: BlockCheckMode::UnsafeBlock(_), .. }, ..))
91+
{
92+
let mut applicability = Applicability::MachineApplicable;
93+
94+
// If any closure parameter has an explicit type specified, applying the lint would necessarily
95+
// remove that specification, possibly breaking type inference
96+
if fn_decl
97+
.inputs
98+
.iter()
99+
.any(|input| matches!(input.kind, TyKind::Infer(..)))
79100
{
80-
let mut applicability = Applicability::MachineApplicable;
81-
82-
// If any closure parameter has an explicit type specified, applying the lint would necessarily
83-
// remove that specification, possibly breaking type inference
84-
if fn_decl
85-
.inputs
86-
.iter()
87-
.any(|input| matches!(input.kind, TyKind::Infer(..)))
88-
{
89-
applicability = Applicability::MaybeIncorrect;
90-
}
101+
applicability = Applicability::MaybeIncorrect;
102+
}
91103

92-
let mut ret_collector = RetCollector::default();
93-
ret_collector.visit_expr(body.value);
104+
let mut ret_collector = RetCollector::default();
105+
ret_collector.visit_expr(body.value);
94106

95-
// Skip the lint if `return` is used in `Loop` in order not to suggest using `'label`.
96-
if ret_collector.ret_in_loop {
97-
return;
98-
}
107+
// Skip the lint if `return` is used in `Loop` in order not to suggest using `'label`.
108+
if ret_collector.ret_in_loop {
109+
return;
110+
}
99111

100-
let ret_suggs = if ret_collector.spans.is_empty() {
101-
None
112+
let ret_suggs = if ret_collector.spans.is_empty() {
113+
None
114+
} else {
115+
applicability = Applicability::MaybeIncorrect;
116+
Some(
117+
ret_collector
118+
.spans
119+
.into_iter()
120+
.map(|span| (span, "continue".to_string()))
121+
.collect(),
122+
)
123+
};
124+
125+
let body_param_sugg = snippet_with_applicability(cx, body.params[0].pat.span, "..", &mut applicability);
126+
let for_each_rev_sugg = snippet_with_applicability(cx, for_each_recv.span, "..", &mut applicability);
127+
let (body_value_sugg, is_macro_call) =
128+
snippet_with_context(cx, body.value.span, for_each_recv.span.ctxt(), "..", &mut applicability);
129+
130+
let sugg = format!(
131+
"for {} in {} {}",
132+
body_param_sugg,
133+
for_each_rev_sugg,
134+
if is_macro_call {
135+
format!("{{ {body_value_sugg}; }}")
102136
} else {
103-
applicability = Applicability::MaybeIncorrect;
104-
Some(
105-
ret_collector
106-
.spans
107-
.into_iter()
108-
.map(|span| (span, "continue".to_string()))
109-
.collect(),
110-
)
111-
};
112-
113-
let body_param_sugg = snippet_with_applicability(cx, body.params[0].pat.span, "..", &mut applicability);
114-
let for_each_rev_sugg = snippet_with_applicability(cx, for_each_recv.span, "..", &mut applicability);
115-
let (body_value_sugg, is_macro_call) =
116-
snippet_with_context(cx, body.value.span, for_each_recv.span.ctxt(), "..", &mut applicability);
117-
118-
let sugg = format!(
119-
"for {} in {} {}",
120-
body_param_sugg,
121-
for_each_rev_sugg,
122-
if is_macro_call {
123-
format!("{{ {body_value_sugg}; }}")
124-
} else {
125-
match body.value.kind {
126-
ExprKind::Block(block, _) if is_let_desugar(block) => {
127-
format!("{{ {body_value_sugg} }}")
128-
},
129-
ExprKind::Block(_, _) => body_value_sugg.to_string(),
130-
_ => format!("{{ {body_value_sugg}; }}"),
131-
}
137+
match body.value.kind {
138+
ExprKind::Block(block, _) if is_let_desugar(block) => {
139+
format!("{{ {body_value_sugg} }}")
140+
},
141+
ExprKind::Block(_, _) => body_value_sugg.to_string(),
142+
_ => format!("{{ {body_value_sugg}; }}"),
132143
}
133-
);
134-
135-
span_lint_and_then(cx, NEEDLESS_FOR_EACH, stmt.span, "needless use of `for_each`", |diag| {
136-
diag.span_suggestion(stmt.span, "try", sugg, applicability);
144+
}
145+
);
146+
147+
span_lint_and_then(
148+
cx,
149+
NEEDLESS_FOR_EACH,
150+
outer_span,
151+
"needless use of `for_each`",
152+
|diag| {
153+
diag.span_suggestion(outer_span, "try", sugg, applicability);
137154
if let Some(ret_suggs) = ret_suggs {
138155
diag.multipart_suggestion("...and replace `return` with `continue`", ret_suggs, applicability);
139156
}
140-
});
141-
}
157+
},
158+
);
142159
}
143160
}
144161

tests/ui/needless_for_each_fixable.fixed

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,3 +149,11 @@ fn issue15256() {
149149
for v in vec.iter() { println!("{v}"); }
150150
//~^ needless_for_each
151151
}
152+
153+
fn issue16294() {
154+
let vec: Vec<i32> = Vec::new();
155+
for elem in vec.iter() {
156+
//~^ needless_for_each
157+
println!("{elem}");
158+
}
159+
}

tests/ui/needless_for_each_fixable.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,3 +149,11 @@ fn issue15256() {
149149
vec.iter().for_each(|v| println!("{v}"));
150150
//~^ needless_for_each
151151
}
152+
153+
fn issue16294() {
154+
let vec: Vec<i32> = Vec::new();
155+
vec.iter().for_each(|elem| {
156+
//~^ needless_for_each
157+
println!("{elem}");
158+
})
159+
}

tests/ui/needless_for_each_fixable.stderr

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,5 +154,22 @@ error: needless use of `for_each`
154154
LL | vec.iter().for_each(|v| println!("{v}"));
155155
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for v in vec.iter() { println!("{v}"); }`
156156

157-
error: aborting due to 11 previous errors
157+
error: needless use of `for_each`
158+
--> tests/ui/needless_for_each_fixable.rs:155:5
159+
|
160+
LL | / vec.iter().for_each(|elem| {
161+
LL | |
162+
LL | | println!("{elem}");
163+
LL | | })
164+
| |______^
165+
|
166+
help: try
167+
|
168+
LL ~ for elem in vec.iter() {
169+
LL +
170+
LL + println!("{elem}");
171+
LL + }
172+
|
173+
174+
error: aborting due to 12 previous errors
158175

0 commit comments

Comments
 (0)