Skip to content

Commit 8e3d769

Browse files
committed
Skip needless_for_each if an input stmt is local
1 parent 4d892a5 commit 8e3d769

5 files changed

+38
-33
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
77

8-
[There are over 400 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
8+
[There are over 450 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
99

1010
Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html).
1111
You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category.

clippy_lints/src/needless_for_each.rs

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -49,31 +49,28 @@ impl LateLintPass<'_> for NeedlessForEach {
4949
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
5050
let expr = match stmt.kind {
5151
StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr,
52-
StmtKind::Local(local) if local.init.is_some() => local.init.unwrap(),
5352
_ => return,
5453
};
5554

5655
if_chain! {
5756
// Check the method name is `for_each`.
58-
if let ExprKind::MethodCall(method_name, _, for_each_args, _) = expr.kind;
57+
if let ExprKind::MethodCall(method_name, _, [for_each_recv, for_each_arg], _) = expr.kind;
5958
if method_name.ident.name == Symbol::intern("for_each");
6059
// Check `for_each` is an associated function of `Iterator`.
6160
if is_trait_method(cx, expr, sym::Iterator);
6261
// Checks the receiver of `for_each` is also a method call.
63-
if let Some(for_each_receiver) = for_each_args.get(0);
64-
if let ExprKind::MethodCall(_, _, iter_args, _) = for_each_receiver.kind;
62+
if let ExprKind::MethodCall(_, _, [iter_recv], _) = for_each_recv.kind;
6563
// Skip the lint if the call chain is too long. e.g. `v.field.iter().for_each()` or
6664
// `v.foo().iter().for_each()` must be skipped.
67-
if let Some(iter_receiver) = iter_args.get(0);
6865
if matches!(
69-
iter_receiver.kind,
66+
iter_recv.kind,
7067
ExprKind::Array(..) | ExprKind::Call(..) | ExprKind::Path(..)
7168
);
7269
// Checks the type of the `iter` method receiver is NOT a user defined type.
73-
if has_iter_method(cx, cx.typeck_results().expr_ty(&iter_receiver)).is_some();
70+
if has_iter_method(cx, cx.typeck_results().expr_ty(&iter_recv)).is_some();
7471
// Skip the lint if the body is not block because this is simpler than `for` loop.
7572
// e.g. `v.iter().for_each(f)` is simpler and clearer than using `for` loop.
76-
if let ExprKind::Closure(_, _, body_id, ..) = for_each_args[1].kind;
73+
if let ExprKind::Closure(_, _, body_id, ..) = for_each_arg.kind;
7774
let body = cx.tcx.hir().body(body_id);
7875
if let ExprKind::Block(..) = body.value.kind;
7976
then {
@@ -85,38 +82,37 @@ impl LateLintPass<'_> for NeedlessForEach {
8582
return;
8683
}
8784

88-
// We can't use `Applicability::MachineApplicable` when the closure contains `return`
89-
// because `Diagnostic::multipart_suggestion` doesn't work with multiple overlapped
90-
// spans.
91-
let mut applicability = if ret_collector.spans.is_empty() {
92-
Applicability::MachineApplicable
85+
let (mut applicability, ret_suggs) = if ret_collector.spans.is_empty() {
86+
(Applicability::MachineApplicable, None)
9387
} else {
94-
Applicability::MaybeIncorrect
88+
(
89+
Applicability::MaybeIncorrect,
90+
Some(
91+
ret_collector
92+
.spans
93+
.into_iter()
94+
.map(|span| (span, "continue".to_string()))
95+
.collect(),
96+
),
97+
)
9598
};
9699

97-
let mut suggs = vec![];
98-
suggs.push((stmt.span, format!(
100+
let sugg = format!(
99101
"for {} in {} {}",
100102
snippet_with_applicability(cx, body.params[0].pat.span, "..", &mut applicability),
101-
snippet_with_applicability(cx, for_each_args[0].span, "..", &mut applicability),
103+
snippet_with_applicability(cx, for_each_recv.span, "..", &mut applicability),
102104
snippet_with_applicability(cx, body.value.span, "..", &mut applicability),
103-
)));
104-
105-
for span in &ret_collector.spans {
106-
suggs.push((*span, "continue".to_string()));
107-
}
105+
);
108106

109107
span_lint_and_then(
110108
cx,
111109
NEEDLESS_FOR_EACH,
112110
stmt.span,
113111
"needless use of `for_each`",
114112
|diag| {
115-
diag.multipart_suggestion("try", suggs, applicability);
116-
// `Diagnostic::multipart_suggestion` ignores the second and subsequent overlapped spans,
117-
// so `span_note` is needed here even though `suggs` includes the replacements.
118-
for span in ret_collector.spans {
119-
diag.span_note(span, "replace `return` with `continue`");
113+
diag.span_suggestion(stmt.span, "try", sugg, applicability);
114+
if let Some(ret_suggs) = ret_suggs {
115+
diag.multipart_suggestion("try replacing `return` with `continue`", ret_suggs, applicability);
120116
}
121117
}
122118
)

tests/ui/needless_for_each_fixable.fixed

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,11 @@ fn should_not_lint() {
103103
acc += elem;
104104
}),
105105
}
106+
107+
// `for_each` is in a let bingind.
108+
let _ = v.iter().for_each(|elem| {
109+
acc += elem;
110+
});
106111
}
107112

108113
fn main() {}

tests/ui/needless_for_each_fixable.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,11 @@ fn should_not_lint() {
103103
acc += elem;
104104
}),
105105
}
106+
107+
// `for_each` is in a let bingind.
108+
let _ = v.iter().for_each(|elem| {
109+
acc += elem;
110+
});
106111
}
107112

108113
fn main() {}

tests/ui/needless_for_each_unfixable.stderr

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,6 @@ LL | | });
1111
| |_______^
1212
|
1313
= note: `-D clippy::needless-for-each` implied by `-D warnings`
14-
note: replace `return` with `continue`
15-
--> $DIR/needless_for_each_unfixable.rs:9:13
16-
|
17-
LL | return;
18-
| ^^^^^^
1914
help: try
2015
|
2116
LL | for v in v.iter() {
@@ -25,6 +20,10 @@ LL | } else {
2520
LL | println!("{}", v);
2621
LL | }
2722
...
23+
help: try replacing `return` with `continue`
24+
|
25+
LL | continue;
26+
| ^^^^^^^^
2827

2928
error: aborting due to previous error
3029

0 commit comments

Comments
 (0)