Skip to content

Commit 5dccb10

Browse files
authored
Fix needless_for_each suggests wrongly when closure has no braces (#14735)
Closes #14734 changelog: [`needless_for_each`] wrong suggestions when closure has no braces
2 parents a6e40fa + 520cb09 commit 5dccb10

File tree

5 files changed

+74
-8
lines changed

5 files changed

+74
-8
lines changed

clippy_lints/src/needless_for_each.rs

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach {
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.
77-
&& let ExprKind::Block(Block { rules: BlockCheckMode::DefaultBlock, .. }, ..) = body.value.kind
77+
&& !matches!(body.value.kind, ExprKind::Block(Block { rules: BlockCheckMode::UnsafeBlock(_), .. }, ..))
7878
{
7979
let mut ret_collector = RetCollector::default();
8080
ret_collector.visit_expr(body.value);
@@ -99,11 +99,21 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach {
9999
)
100100
};
101101

102+
let body_param_sugg = snippet_with_applicability(cx, body.params[0].pat.span, "..", &mut applicability);
103+
let for_each_rev_sugg = snippet_with_applicability(cx, for_each_recv.span, "..", &mut applicability);
104+
let body_value_sugg = snippet_with_applicability(cx, body.value.span, "..", &mut applicability);
105+
102106
let sugg = format!(
103107
"for {} in {} {}",
104-
snippet_with_applicability(cx, body.params[0].pat.span, "..", &mut applicability),
105-
snippet_with_applicability(cx, for_each_recv.span, "..", &mut applicability),
106-
snippet_with_applicability(cx, body.value.span, "..", &mut applicability),
108+
body_param_sugg,
109+
for_each_rev_sugg,
110+
match body.value.kind {
111+
ExprKind::Block(block, _) if is_let_desugar(block) => {
112+
format!("{{ {body_value_sugg} }}")
113+
},
114+
ExprKind::Block(_, _) => body_value_sugg.to_string(),
115+
_ => format!("{{ {body_value_sugg}; }}"),
116+
}
107117
);
108118

109119
span_lint_and_then(cx, NEEDLESS_FOR_EACH, stmt.span, "needless use of `for_each`", |diag| {
@@ -116,6 +126,20 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach {
116126
}
117127
}
118128

129+
/// Check if the block is a desugared `_ = expr` statement.
130+
fn is_let_desugar(block: &Block<'_>) -> bool {
131+
matches!(
132+
block,
133+
Block {
134+
stmts: [Stmt {
135+
kind: StmtKind::Let(_),
136+
..
137+
},],
138+
..
139+
}
140+
)
141+
}
142+
119143
/// This type plays two roles.
120144
/// 1. Collect spans of `return` in the closure body.
121145
/// 2. Detect use of `return` in `Loop` in the closure body.

lintcheck/src/output.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,9 @@ pub fn summarize_and_print_changes(
162162
fn gather_stats(warnings: &[ClippyWarning]) -> (String, HashMap<&String, usize>) {
163163
// count lint type occurrences
164164
let mut counter: HashMap<&String, usize> = HashMap::new();
165-
warnings
166-
.iter()
167-
.for_each(|wrn| *counter.entry(&wrn.name).or_insert(0) += 1);
165+
for wrn in warnings {
166+
*counter.entry(&wrn.name).or_insert(0) += 1;
167+
}
168168

169169
// collect into a tupled list for sorting
170170
let mut stats: Vec<(&&String, &usize)> = counter.iter().collect();

tests/ui/needless_for_each_fixable.fixed

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,18 @@ fn should_not_lint() {
128128
}
129129

130130
fn main() {}
131+
132+
mod issue14734 {
133+
fn let_desugar(rows: &[u8]) {
134+
let mut v = vec![];
135+
for x in rows.iter() { _ = v.push(x) }
136+
//~^ needless_for_each
137+
}
138+
139+
fn do_something(_: &u8, _: u8) {}
140+
141+
fn single_expr(rows: &[u8]) {
142+
for x in rows.iter() { do_something(x, 1u8); }
143+
//~^ needless_for_each
144+
}
145+
}

tests/ui/needless_for_each_fixable.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,18 @@ fn should_not_lint() {
128128
}
129129

130130
fn main() {}
131+
132+
mod issue14734 {
133+
fn let_desugar(rows: &[u8]) {
134+
let mut v = vec![];
135+
rows.iter().for_each(|x| _ = v.push(x));
136+
//~^ needless_for_each
137+
}
138+
139+
fn do_something(_: &u8, _: u8) {}
140+
141+
fn single_expr(rows: &[u8]) {
142+
rows.iter().for_each(|x| do_something(x, 1u8));
143+
//~^ needless_for_each
144+
}
145+
}

tests/ui/needless_for_each_fixable.stderr

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,5 +136,17 @@ LL + acc += elem;
136136
LL + }
137137
|
138138

139-
error: aborting due to 8 previous errors
139+
error: needless use of `for_each`
140+
--> tests/ui/needless_for_each_fixable.rs:135:9
141+
|
142+
LL | rows.iter().for_each(|x| _ = v.push(x));
143+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in rows.iter() { _ = v.push(x) }`
144+
145+
error: needless use of `for_each`
146+
--> tests/ui/needless_for_each_fixable.rs:142:9
147+
|
148+
LL | rows.iter().for_each(|x| do_something(x, 1u8));
149+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in rows.iter() { do_something(x, 1u8); }`
150+
151+
error: aborting due to 10 previous errors
140152

0 commit comments

Comments
 (0)