Skip to content

Commit 3e52dee

Browse files
committed
Auto merge of #8941 - DevAccentor:for_loops_over_fallibles, r=llogiq
improve [`for_loops_over_fallibles`] to detect the usage of iter, iter_mut and into_iterator fix #6762 detects code like ```rust for _ in option.iter() { //.. } ``` changelog: Improve [`for_loops_over_fallibles`] to detect `for _ in option.iter() {}` or using `iter_mut()` or `into_iterator()`.
2 parents 542d474 + 3737abe commit 3e52dee

7 files changed

+110
-43
lines changed

clippy_lints/src/loops/for_loops_over_fallibles.rs

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,22 @@ use rustc_lint::LateContext;
77
use rustc_span::symbol::sym;
88

99
/// Checks for `for` loops over `Option`s and `Result`s.
10-
pub(super) fn check(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) {
10+
pub(super) fn check(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>, method_name: Option<&str>) {
1111
let ty = cx.typeck_results().expr_ty(arg);
1212
if is_type_diagnostic_item(cx, ty, sym::Option) {
13+
let help_string = if let Some(method_name) = method_name {
14+
format!(
15+
"consider replacing `for {0} in {1}.{method_name}()` with `if let Some({0}) = {1}`",
16+
snippet(cx, pat.span, "_"),
17+
snippet(cx, arg.span, "_")
18+
)
19+
} else {
20+
format!(
21+
"consider replacing `for {0} in {1}` with `if let Some({0}) = {1}`",
22+
snippet(cx, pat.span, "_"),
23+
snippet(cx, arg.span, "_")
24+
)
25+
};
1326
span_lint_and_help(
1427
cx,
1528
FOR_LOOPS_OVER_FALLIBLES,
@@ -20,13 +33,22 @@ pub(super) fn check(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) {
2033
snippet(cx, arg.span, "_")
2134
),
2235
None,
23-
&format!(
24-
"consider replacing `for {0} in {1}` with `if let Some({0}) = {1}`",
25-
snippet(cx, pat.span, "_"),
26-
snippet(cx, arg.span, "_")
27-
),
36+
&help_string,
2837
);
2938
} else if is_type_diagnostic_item(cx, ty, sym::Result) {
39+
let help_string = if let Some(method_name) = method_name {
40+
format!(
41+
"consider replacing `for {0} in {1}.{method_name}()` with `if let Ok({0}) = {1}`",
42+
snippet(cx, pat.span, "_"),
43+
snippet(cx, arg.span, "_")
44+
)
45+
} else {
46+
format!(
47+
"consider replacing `for {0} in {1}` with `if let Ok({0}) = {1}`",
48+
snippet(cx, pat.span, "_"),
49+
snippet(cx, arg.span, "_")
50+
)
51+
};
3052
span_lint_and_help(
3153
cx,
3254
FOR_LOOPS_OVER_FALLIBLES,
@@ -37,11 +59,7 @@ pub(super) fn check(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) {
3759
snippet(cx, arg.span, "_")
3860
),
3961
None,
40-
&format!(
41-
"consider replacing `for {0} in {1}` with `if let Ok({0}) = {1}`",
42-
snippet(cx, pat.span, "_"),
43-
snippet(cx, arg.span, "_")
44-
),
62+
&help_string,
4563
);
4664
}
4765
}

clippy_lints/src/loops/mod.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,10 @@ declare_clippy_lint! {
188188
/// for x in &res {
189189
/// // ..
190190
/// }
191+
///
192+
/// for x in res.iter() {
193+
/// // ..
194+
/// }
191195
/// ```
192196
///
193197
/// Use instead:
@@ -695,10 +699,14 @@ fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) {
695699
let method_name = method.ident.as_str();
696700
// check for looping over x.iter() or x.iter_mut(), could use &x or &mut x
697701
match method_name {
698-
"iter" | "iter_mut" => explicit_iter_loop::check(cx, self_arg, arg, method_name),
702+
"iter" | "iter_mut" => {
703+
explicit_iter_loop::check(cx, self_arg, arg, method_name);
704+
for_loops_over_fallibles::check(cx, pat, self_arg, Some(method_name));
705+
},
699706
"into_iter" => {
700707
explicit_iter_loop::check(cx, self_arg, arg, method_name);
701708
explicit_into_iter_loop::check(cx, self_arg, arg);
709+
for_loops_over_fallibles::check(cx, pat, self_arg, Some(method_name));
702710
},
703711
"next" => {
704712
next_loop_linted = iter_next_loop::check(cx, arg);
@@ -708,6 +716,6 @@ fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) {
708716
}
709717

710718
if !next_loop_linted {
711-
for_loops_over_fallibles::check(cx, pat, arg);
719+
for_loops_over_fallibles::check(cx, pat, arg, None);
712720
}
713721
}

tests/ui/for_loops_over_fallibles.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,34 @@
22

33
fn for_loops_over_fallibles() {
44
let option = Some(1);
5-
let result = option.ok_or("x not found");
5+
let mut result = option.ok_or("x not found");
66
let v = vec![0, 1, 2];
77

88
// check over an `Option`
99
for x in option {
1010
println!("{}", x);
1111
}
1212

13+
// check over an `Option`
14+
for x in option.iter() {
15+
println!("{}", x);
16+
}
17+
1318
// check over a `Result`
1419
for x in result {
1520
println!("{}", x);
1621
}
1722

23+
// check over a `Result`
24+
for x in result.iter_mut() {
25+
println!("{}", x);
26+
}
27+
28+
// check over a `Result`
29+
for x in result.into_iter() {
30+
println!("{}", x);
31+
}
32+
1833
for x in option.ok_or("x not found") {
1934
println!("{}", x);
2035
}

tests/ui/for_loops_over_fallibles.stderr

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,48 +7,72 @@ LL | for x in option {
77
= note: `-D clippy::for-loops-over-fallibles` implied by `-D warnings`
88
= help: consider replacing `for x in option` with `if let Some(x) = option`
99

10-
error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement
10+
error: for loop over `option`, which is an `Option`. This is more readably written as an `if let` statement
1111
--> $DIR/for_loops_over_fallibles.rs:14:14
1212
|
13+
LL | for x in option.iter() {
14+
| ^^^^^^
15+
|
16+
= help: consider replacing `for x in option.iter()` with `if let Some(x) = option`
17+
18+
error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement
19+
--> $DIR/for_loops_over_fallibles.rs:19:14
20+
|
1321
LL | for x in result {
1422
| ^^^^^^
1523
|
1624
= help: consider replacing `for x in result` with `if let Ok(x) = result`
1725

26+
error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement
27+
--> $DIR/for_loops_over_fallibles.rs:24:14
28+
|
29+
LL | for x in result.iter_mut() {
30+
| ^^^^^^
31+
|
32+
= help: consider replacing `for x in result.iter_mut()` with `if let Ok(x) = result`
33+
34+
error: for loop over `result`, which is a `Result`. This is more readably written as an `if let` statement
35+
--> $DIR/for_loops_over_fallibles.rs:29:14
36+
|
37+
LL | for x in result.into_iter() {
38+
| ^^^^^^
39+
|
40+
= help: consider replacing `for x in result.into_iter()` with `if let Ok(x) = result`
41+
1842
error: for loop over `option.ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement
19-
--> $DIR/for_loops_over_fallibles.rs:18:14
43+
--> $DIR/for_loops_over_fallibles.rs:33:14
2044
|
2145
LL | for x in option.ok_or("x not found") {
2246
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
2347
|
2448
= help: consider replacing `for x in option.ok_or("x not found")` with `if let Ok(x) = option.ok_or("x not found")`
2549

2650
error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want
27-
--> $DIR/for_loops_over_fallibles.rs:24:14
51+
--> $DIR/for_loops_over_fallibles.rs:39:14
2852
|
2953
LL | for x in v.iter().next() {
3054
| ^^^^^^^^^^^^^^^
3155
|
3256
= note: `#[deny(clippy::iter_next_loop)]` on by default
3357

3458
error: for loop over `v.iter().next().and(Some(0))`, which is an `Option`. This is more readably written as an `if let` statement
35-
--> $DIR/for_loops_over_fallibles.rs:29:14
59+
--> $DIR/for_loops_over_fallibles.rs:44:14
3660
|
3761
LL | for x in v.iter().next().and(Some(0)) {
3862
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
3963
|
4064
= help: consider replacing `for x in v.iter().next().and(Some(0))` with `if let Some(x) = v.iter().next().and(Some(0))`
4165

4266
error: for loop over `v.iter().next().ok_or("x not found")`, which is a `Result`. This is more readably written as an `if let` statement
43-
--> $DIR/for_loops_over_fallibles.rs:33:14
67+
--> $DIR/for_loops_over_fallibles.rs:48:14
4468
|
4569
LL | for x in v.iter().next().ok_or("x not found") {
4670
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
4771
|
4872
= help: consider replacing `for x in v.iter().next().ok_or("x not found")` with `if let Ok(x) = v.iter().next().ok_or("x not found")`
4973

5074
error: this loop never actually loops
51-
--> $DIR/for_loops_over_fallibles.rs:45:5
75+
--> $DIR/for_loops_over_fallibles.rs:60:5
5276
|
5377
LL | / while let Some(x) = option {
5478
LL | | println!("{}", x);
@@ -59,13 +83,13 @@ LL | | }
5983
= note: `#[deny(clippy::never_loop)]` on by default
6084

6185
error: this loop never actually loops
62-
--> $DIR/for_loops_over_fallibles.rs:51:5
86+
--> $DIR/for_loops_over_fallibles.rs:66:5
6387
|
6488
LL | / while let Ok(x) = result {
6589
LL | | println!("{}", x);
6690
LL | | break;
6791
LL | | }
6892
| |_____^
6993

70-
error: aborting due to 8 previous errors
94+
error: aborting due to 11 previous errors
7195

tests/ui/manual_map_option.fixed

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
clippy::unit_arg,
88
clippy::match_ref_pats,
99
clippy::redundant_pattern_matching,
10+
clippy::for_loops_over_fallibles,
1011
dead_code
1112
)]
1213

tests/ui/manual_map_option.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
clippy::unit_arg,
88
clippy::match_ref_pats,
99
clippy::redundant_pattern_matching,
10+
clippy::for_loops_over_fallibles,
1011
dead_code
1112
)]
1213

0 commit comments

Comments
 (0)