Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 7e16f9f

Browse files
authoredOct 10, 2022
Rollup merge of #99696 - WaffleLapkin:uplift, r=fee1-dead
Uplift `clippy::for_loops_over_fallibles` lint into rustc This PR, as the title suggests, uplifts [`clippy::for_loops_over_fallibles`] lint into rustc. This lint warns for code like this: ```rust for _ in Some(1) {} for _ in Ok::<_, ()>(1) {} ``` i.e. directly iterating over `Option` and `Result` using `for` loop. There are a number of suggestions that this PR adds (on top of what clippy suggested): 1. If the argument (? is there a better name for that expression) of a `for` loop is a `.next()` call, then we can suggest removing it (or rather replacing with `.by_ref()` to allow iterator being used later) ```rust for _ in iter.next() {} // turns into for _ in iter.by_ref() {} ``` 2. (otherwise) We can suggest using `while let`, this is useful for non-iterator, iterator-like things like [async] channels ```rust for _ in rx.recv() {} // turns into while let Some(_) = rx.recv() {} ``` 3. If the argument type is `Result<impl IntoIterator, _>` and the body has a `Result<_, _>` type, we can suggest using `?` ```rust for _ in f() {} // turns into for _ in f()? {} ``` 4. To preserve the original behavior and clear intent, we can suggest using `if let` ```rust for _ in f() {} // turns into if let Some(_) = f() {} ``` (P.S. `Some` and `Ok` are interchangeable depending on the type) I still feel that the lint wording/look is somewhat off, so I'll be happy to hear suggestions (on how to improve suggestions :D)! Resolves #99272 [`clippy::for_loops_over_fallibles`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_fallibles
2 parents e495b37 + 9d4edff commit 7e16f9f

26 files changed

+381
-366
lines changed
 

‎compiler/rustc_ast/src/visit.rs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -244,14 +244,12 @@ pub trait Visitor<'ast>: Sized {
244244

245245
#[macro_export]
246246
macro_rules! walk_list {
247-
($visitor: expr, $method: ident, $list: expr) => {
248-
for elem in $list {
249-
$visitor.$method(elem)
250-
}
251-
};
252-
($visitor: expr, $method: ident, $list: expr, $($extra_args: expr),*) => {
253-
for elem in $list {
254-
$visitor.$method(elem, $($extra_args,)*)
247+
($visitor: expr, $method: ident, $list: expr $(, $($extra_args: expr),* )?) => {
248+
{
249+
#[cfg_attr(not(bootstrap), allow(for_loops_over_fallibles))]
250+
for elem in $list {
251+
$visitor.$method(elem $(, $($extra_args,)* )?)
252+
}
255253
}
256254
}
257255
}
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
use crate::{LateContext, LateLintPass, LintContext};
2+
3+
use hir::{Expr, Pat};
4+
use rustc_errors::{Applicability, DelayDm};
5+
use rustc_hir as hir;
6+
use rustc_infer::traits::TraitEngine;
7+
use rustc_infer::{infer::TyCtxtInferExt, traits::ObligationCause};
8+
use rustc_middle::ty::{self, List};
9+
use rustc_span::{sym, Span};
10+
use rustc_trait_selection::traits::TraitEngineExt;
11+
12+
declare_lint! {
13+
/// The `for_loops_over_fallibles` lint checks for `for` loops over `Option` or `Result` values.
14+
///
15+
/// ### Example
16+
///
17+
/// ```rust
18+
/// let opt = Some(1);
19+
/// for x in opt { /* ... */}
20+
/// ```
21+
///
22+
/// {{produces}}
23+
///
24+
/// ### Explanation
25+
///
26+
/// Both `Option` and `Result` implement `IntoIterator` trait, which allows using them in a `for` loop.
27+
/// `for` loop over `Option` or `Result` will iterate either 0 (if the value is `None`/`Err(_)`)
28+
/// or 1 time (if the value is `Some(_)`/`Ok(_)`). This is not very useful and is more clearly expressed
29+
/// via `if let`.
30+
///
31+
/// `for` loop can also be accidentally written with the intention to call a function multiple times,
32+
/// while the function returns `Some(_)`, in these cases `while let` loop should be used instead.
33+
///
34+
/// The "intended" use of `IntoIterator` implementations for `Option` and `Result` is passing them to
35+
/// generic code that expects something implementing `IntoIterator`. For example using `.chain(option)`
36+
/// to optionally add a value to an iterator.
37+
pub FOR_LOOPS_OVER_FALLIBLES,
38+
Warn,
39+
"for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`"
40+
}
41+
42+
declare_lint_pass!(ForLoopsOverFallibles => [FOR_LOOPS_OVER_FALLIBLES]);
43+
44+
impl<'tcx> LateLintPass<'tcx> for ForLoopsOverFallibles {
45+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
46+
let Some((pat, arg)) = extract_for_loop(expr) else { return };
47+
48+
let ty = cx.typeck_results().expr_ty(arg);
49+
50+
let &ty::Adt(adt, substs) = ty.kind() else { return };
51+
52+
let (article, ty, var) = match adt.did() {
53+
did if cx.tcx.is_diagnostic_item(sym::Option, did) => ("an", "Option", "Some"),
54+
did if cx.tcx.is_diagnostic_item(sym::Result, did) => ("a", "Result", "Ok"),
55+
_ => return,
56+
};
57+
58+
let msg = DelayDm(|| {
59+
format!(
60+
"for loop over {article} `{ty}`. This is more readably written as an `if let` statement",
61+
)
62+
});
63+
64+
cx.struct_span_lint(FOR_LOOPS_OVER_FALLIBLES, arg.span, msg, |lint| {
65+
if let Some(recv) = extract_iterator_next_call(cx, arg)
66+
&& let Ok(recv_snip) = cx.sess().source_map().span_to_snippet(recv.span)
67+
{
68+
lint.span_suggestion(
69+
recv.span.between(arg.span.shrink_to_hi()),
70+
format!("to iterate over `{recv_snip}` remove the call to `next`"),
71+
".by_ref()",
72+
Applicability::MaybeIncorrect
73+
);
74+
} else {
75+
lint.multipart_suggestion_verbose(
76+
format!("to check pattern in a loop use `while let`"),
77+
vec![
78+
// NB can't use `until` here because `expr.span` and `pat.span` have different syntax contexts
79+
(expr.span.with_hi(pat.span.lo()), format!("while let {var}(")),
80+
(pat.span.between(arg.span), format!(") = ")),
81+
],
82+
Applicability::MaybeIncorrect
83+
);
84+
}
85+
86+
if suggest_question_mark(cx, adt, substs, expr.span) {
87+
lint.span_suggestion(
88+
arg.span.shrink_to_hi(),
89+
"consider unwrapping the `Result` with `?` to iterate over its contents",
90+
"?",
91+
Applicability::MaybeIncorrect,
92+
);
93+
}
94+
95+
lint.multipart_suggestion_verbose(
96+
"consider using `if let` to clear intent",
97+
vec![
98+
// NB can't use `until` here because `expr.span` and `pat.span` have different syntax contexts
99+
(expr.span.with_hi(pat.span.lo()), format!("if let {var}(")),
100+
(pat.span.between(arg.span), format!(") = ")),
101+
],
102+
Applicability::MaybeIncorrect,
103+
)
104+
})
105+
}
106+
}
107+
108+
fn extract_for_loop<'tcx>(expr: &Expr<'tcx>) -> Option<(&'tcx Pat<'tcx>, &'tcx Expr<'tcx>)> {
109+
if let hir::ExprKind::DropTemps(e) = expr.kind
110+
&& let hir::ExprKind::Match(iterexpr, [arm], hir::MatchSource::ForLoopDesugar) = e.kind
111+
&& let hir::ExprKind::Call(_, [arg]) = iterexpr.kind
112+
&& let hir::ExprKind::Loop(block, ..) = arm.body.kind
113+
&& let [stmt] = block.stmts
114+
&& let hir::StmtKind::Expr(e) = stmt.kind
115+
&& let hir::ExprKind::Match(_, [_, some_arm], _) = e.kind
116+
&& let hir::PatKind::Struct(_, [field], _) = some_arm.pat.kind
117+
{
118+
Some((field.pat, arg))
119+
} else {
120+
None
121+
}
122+
}
123+
124+
fn extract_iterator_next_call<'tcx>(
125+
cx: &LateContext<'_>,
126+
expr: &Expr<'tcx>,
127+
) -> Option<&'tcx Expr<'tcx>> {
128+
// This won't work for `Iterator::next(iter)`, is this an issue?
129+
if let hir::ExprKind::MethodCall(_, recv, _, _) = expr.kind
130+
&& cx.typeck_results().type_dependent_def_id(expr.hir_id) == cx.tcx.lang_items().next_fn()
131+
{
132+
Some(recv)
133+
} else {
134+
return None
135+
}
136+
}
137+
138+
fn suggest_question_mark<'tcx>(
139+
cx: &LateContext<'tcx>,
140+
adt: ty::AdtDef<'tcx>,
141+
substs: &List<ty::GenericArg<'tcx>>,
142+
span: Span,
143+
) -> bool {
144+
let Some(body_id) = cx.enclosing_body else { return false };
145+
let Some(into_iterator_did) = cx.tcx.get_diagnostic_item(sym::IntoIterator) else { return false };
146+
147+
if !cx.tcx.is_diagnostic_item(sym::Result, adt.did()) {
148+
return false;
149+
}
150+
151+
// Check that the function/closure/constant we are in has a `Result` type.
152+
// Otherwise suggesting using `?` may not be a good idea.
153+
{
154+
let ty = cx.typeck_results().expr_ty(&cx.tcx.hir().body(body_id).value);
155+
let ty::Adt(ret_adt, ..) = ty.kind() else { return false };
156+
if !cx.tcx.is_diagnostic_item(sym::Result, ret_adt.did()) {
157+
return false;
158+
}
159+
}
160+
161+
let ty = substs.type_at(0);
162+
let infcx = cx.tcx.infer_ctxt().build();
163+
let mut fulfill_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
164+
165+
let cause = ObligationCause::new(
166+
span,
167+
body_id.hir_id,
168+
rustc_infer::traits::ObligationCauseCode::MiscObligation,
169+
);
170+
fulfill_cx.register_bound(
171+
&infcx,
172+
ty::ParamEnv::empty(),
173+
// Erase any region vids from the type, which may not be resolved
174+
infcx.tcx.erase_regions(ty),
175+
into_iterator_did,
176+
cause,
177+
);
178+
179+
// Select all, including ambiguous predicates
180+
let errors = fulfill_cx.select_all_or_error(&infcx);
181+
182+
errors.is_empty()
183+
}

‎compiler/rustc_lint/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ mod early;
5252
mod enum_intrinsics_non_enums;
5353
mod errors;
5454
mod expect;
55+
mod for_loops_over_fallibles;
5556
pub mod hidden_unicode_codepoints;
5657
mod internal;
5758
mod late;
@@ -86,6 +87,7 @@ use rustc_span::Span;
8687
use array_into_iter::ArrayIntoIter;
8788
use builtin::*;
8889
use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums;
90+
use for_loops_over_fallibles::*;
8991
use hidden_unicode_codepoints::*;
9092
use internal::*;
9193
use let_underscore::*;
@@ -188,6 +190,7 @@ macro_rules! late_lint_mod_passes {
188190
$macro!(
189191
$args,
190192
[
193+
ForLoopsOverFallibles: ForLoopsOverFallibles,
191194
HardwiredLints: HardwiredLints,
192195
ImproperCTypesDeclarations: ImproperCTypesDeclarations,
193196
ImproperCTypesDefinitions: ImproperCTypesDefinitions,

‎library/core/tests/option.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ fn test_get_resource() {
5757
}
5858

5959
#[test]
60+
#[cfg_attr(not(bootstrap), allow(for_loops_over_fallibles))]
6061
fn test_option_dance() {
6162
let x = Some(());
6263
let mut y = Some(5);

‎src/test/ui/drop/dropck_legal_cycles.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1017,7 +1017,7 @@ impl<'a> Children<'a> for HM<'a> {
10171017
where C: Context + PrePost<Self>, Self: Sized
10181018
{
10191019
if let Some(ref hm) = self.contents.get() {
1020-
for (k, v) in hm.iter().nth(index / 2) {
1020+
if let Some((k, v)) = hm.iter().nth(index / 2) {
10211021
[k, v][index % 2].descend_into_self(context);
10221022
}
10231023
}
@@ -1032,7 +1032,7 @@ impl<'a> Children<'a> for VD<'a> {
10321032
where C: Context + PrePost<Self>, Self: Sized
10331033
{
10341034
if let Some(ref vd) = self.contents.get() {
1035-
for r in vd.iter().nth(index) {
1035+
if let Some(r) = vd.iter().nth(index) {
10361036
r.descend_into_self(context);
10371037
}
10381038
}
@@ -1047,7 +1047,7 @@ impl<'a> Children<'a> for VM<'a> {
10471047
where C: Context + PrePost<VM<'a>>
10481048
{
10491049
if let Some(ref vd) = self.contents.get() {
1050-
for (_idx, r) in vd.iter().nth(index) {
1050+
if let Some((_idx, r)) = vd.iter().nth(index) {
10511051
r.descend_into_self(context);
10521052
}
10531053
}
@@ -1062,7 +1062,7 @@ impl<'a> Children<'a> for LL<'a> {
10621062
where C: Context + PrePost<LL<'a>>
10631063
{
10641064
if let Some(ref ll) = self.contents.get() {
1065-
for r in ll.iter().nth(index) {
1065+
if let Some(r) = ll.iter().nth(index) {
10661066
r.descend_into_self(context);
10671067
}
10681068
}
@@ -1077,7 +1077,7 @@ impl<'a> Children<'a> for BH<'a> {
10771077
where C: Context + PrePost<BH<'a>>
10781078
{
10791079
if let Some(ref bh) = self.contents.get() {
1080-
for r in bh.iter().nth(index) {
1080+
if let Some(r) = bh.iter().nth(index) {
10811081
r.descend_into_self(context);
10821082
}
10831083
}
@@ -1092,7 +1092,7 @@ impl<'a> Children<'a> for BTM<'a> {
10921092
where C: Context + PrePost<BTM<'a>>
10931093
{
10941094
if let Some(ref bh) = self.contents.get() {
1095-
for (k, v) in bh.iter().nth(index / 2) {
1095+
if let Some((k, v)) = bh.iter().nth(index / 2) {
10961096
[k, v][index % 2].descend_into_self(context);
10971097
}
10981098
}
@@ -1107,7 +1107,7 @@ impl<'a> Children<'a> for BTS<'a> {
11071107
where C: Context + PrePost<BTS<'a>>
11081108
{
11091109
if let Some(ref bh) = self.contents.get() {
1110-
for r in bh.iter().nth(index) {
1110+
if let Some(r) = bh.iter().nth(index) {
11111111
r.descend_into_self(context);
11121112
}
11131113
}

‎src/test/ui/issues/issue-30371.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// run-pass
22
#![allow(unreachable_code)]
3+
#![allow(for_loops_over_fallibles)]
34
#![deny(unused_variables)]
45

56
fn main() {
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// check-pass
2+
3+
fn main() {
4+
// Common
5+
for _ in Some(1) {}
6+
//~^ WARN for loop over an `Option`. This is more readably written as an `if let` statement
7+
//~| HELP to check pattern in a loop use `while let`
8+
//~| HELP consider using `if let` to clear intent
9+
for _ in Ok::<_, ()>(1) {}
10+
//~^ WARN for loop over a `Result`. This is more readably written as an `if let` statement
11+
//~| HELP to check pattern in a loop use `while let`
12+
//~| HELP consider using `if let` to clear intent
13+
14+
// `Iterator::next` specific
15+
for _ in [0; 0].iter().next() {}
16+
//~^ WARN for loop over an `Option`. This is more readably written as an `if let` statement
17+
//~| HELP to iterate over `[0; 0].iter()` remove the call to `next`
18+
//~| HELP consider using `if let` to clear intent
19+
20+
// `Result<impl Iterator, _>`, but function doesn't return `Result`
21+
for _ in Ok::<_, ()>([0; 0].iter()) {}
22+
//~^ WARN for loop over a `Result`. This is more readably written as an `if let` statement
23+
//~| HELP to check pattern in a loop use `while let`
24+
//~| HELP consider using `if let` to clear intent
25+
}
26+
27+
fn _returns_result() -> Result<(), ()> {
28+
// `Result<impl Iterator, _>`
29+
for _ in Ok::<_, ()>([0; 0].iter()) {}
30+
//~^ WARN for loop over a `Result`. This is more readably written as an `if let` statement
31+
//~| HELP to check pattern in a loop use `while let`
32+
//~| HELP consider unwrapping the `Result` with `?` to iterate over its contents
33+
//~| HELP consider using `if let` to clear intent
34+
35+
// `Result<impl IntoIterator>`
36+
for _ in Ok::<_, ()>([0; 0]) {}
37+
//~^ WARN for loop over a `Result`. This is more readably written as an `if let` statement
38+
//~| HELP to check pattern in a loop use `while let`
39+
//~| HELP consider unwrapping the `Result` with `?` to iterate over its contents
40+
//~| HELP consider using `if let` to clear intent
41+
42+
Ok(())
43+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
warning: for loop over an `Option`. This is more readably written as an `if let` statement
2+
--> $DIR/for_loop_over_fallibles.rs:5:14
3+
|
4+
LL | for _ in Some(1) {}
5+
| ^^^^^^^
6+
|
7+
= note: `#[warn(for_loops_over_fallibles)]` on by default
8+
help: to check pattern in a loop use `while let`
9+
|
10+
LL | while let Some(_) = Some(1) {}
11+
| ~~~~~~~~~~~~~~~ ~~~
12+
help: consider using `if let` to clear intent
13+
|
14+
LL | if let Some(_) = Some(1) {}
15+
| ~~~~~~~~~~~~ ~~~
16+
17+
warning: for loop over a `Result`. This is more readably written as an `if let` statement
18+
--> $DIR/for_loop_over_fallibles.rs:9:14
19+
|
20+
LL | for _ in Ok::<_, ()>(1) {}
21+
| ^^^^^^^^^^^^^^
22+
|
23+
help: to check pattern in a loop use `while let`
24+
|
25+
LL | while let Ok(_) = Ok::<_, ()>(1) {}
26+
| ~~~~~~~~~~~~~ ~~~
27+
help: consider using `if let` to clear intent
28+
|
29+
LL | if let Ok(_) = Ok::<_, ()>(1) {}
30+
| ~~~~~~~~~~ ~~~
31+
32+
warning: for loop over an `Option`. This is more readably written as an `if let` statement
33+
--> $DIR/for_loop_over_fallibles.rs:15:14
34+
|
35+
LL | for _ in [0; 0].iter().next() {}
36+
| ^^^^^^^^^^^^^^^^^^^^
37+
|
38+
help: to iterate over `[0; 0].iter()` remove the call to `next`
39+
|
40+
LL | for _ in [0; 0].iter().by_ref() {}
41+
| ~~~~~~~~~
42+
help: consider using `if let` to clear intent
43+
|
44+
LL | if let Some(_) = [0; 0].iter().next() {}
45+
| ~~~~~~~~~~~~ ~~~
46+
47+
warning: for loop over a `Result`. This is more readably written as an `if let` statement
48+
--> $DIR/for_loop_over_fallibles.rs:21:14
49+
|
50+
LL | for _ in Ok::<_, ()>([0; 0].iter()) {}
51+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
52+
|
53+
help: to check pattern in a loop use `while let`
54+
|
55+
LL | while let Ok(_) = Ok::<_, ()>([0; 0].iter()) {}
56+
| ~~~~~~~~~~~~~ ~~~
57+
help: consider using `if let` to clear intent
58+
|
59+
LL | if let Ok(_) = Ok::<_, ()>([0; 0].iter()) {}
60+
| ~~~~~~~~~~ ~~~
61+
62+
warning: for loop over a `Result`. This is more readably written as an `if let` statement
63+
--> $DIR/for_loop_over_fallibles.rs:29:14
64+
|
65+
LL | for _ in Ok::<_, ()>([0; 0].iter()) {}
66+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
67+
|
68+
help: to check pattern in a loop use `while let`
69+
|
70+
LL | while let Ok(_) = Ok::<_, ()>([0; 0].iter()) {}
71+
| ~~~~~~~~~~~~~ ~~~
72+
help: consider unwrapping the `Result` with `?` to iterate over its contents
73+
|
74+
LL | for _ in Ok::<_, ()>([0; 0].iter())? {}
75+
| +
76+
help: consider using `if let` to clear intent
77+
|
78+
LL | if let Ok(_) = Ok::<_, ()>([0; 0].iter()) {}
79+
| ~~~~~~~~~~ ~~~
80+
81+
warning: for loop over a `Result`. This is more readably written as an `if let` statement
82+
--> $DIR/for_loop_over_fallibles.rs:36:14
83+
|
84+
LL | for _ in Ok::<_, ()>([0; 0]) {}
85+
| ^^^^^^^^^^^^^^^^^^^
86+
|
87+
help: to check pattern in a loop use `while let`
88+
|
89+
LL | while let Ok(_) = Ok::<_, ()>([0; 0]) {}
90+
| ~~~~~~~~~~~~~ ~~~
91+
help: consider unwrapping the `Result` with `?` to iterate over its contents
92+
|
93+
LL | for _ in Ok::<_, ()>([0; 0])? {}
94+
| +
95+
help: consider using `if let` to clear intent
96+
|
97+
LL | if let Ok(_) = Ok::<_, ()>([0; 0]) {}
98+
| ~~~~~~~~~~ ~~~
99+
100+
warning: 6 warnings emitted
101+

‎src/tools/clippy/clippy_lints/src/lib.register_all.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
109109
LintId::of(loops::EMPTY_LOOP),
110110
LintId::of(loops::EXPLICIT_COUNTER_LOOP),
111111
LintId::of(loops::FOR_KV_MAP),
112-
LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES),
113112
LintId::of(loops::ITER_NEXT_LOOP),
114113
LintId::of(loops::MANUAL_FIND),
115114
LintId::of(loops::MANUAL_FLATTEN),

‎src/tools/clippy/clippy_lints/src/lib.register_lints.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,6 @@ store.register_lints(&[
227227
loops::EXPLICIT_INTO_ITER_LOOP,
228228
loops::EXPLICIT_ITER_LOOP,
229229
loops::FOR_KV_MAP,
230-
loops::FOR_LOOPS_OVER_FALLIBLES,
231230
loops::ITER_NEXT_LOOP,
232231
loops::MANUAL_FIND,
233232
loops::MANUAL_FLATTEN,

‎src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
2121
LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING),
2222
LintId::of(formatting::SUSPICIOUS_UNARY_OP_FORMATTING),
2323
LintId::of(loops::EMPTY_LOOP),
24-
LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES),
2524
LintId::of(loops::MUT_RANGE_BOUND),
2625
LintId::of(methods::NO_EFFECT_REPLACE),
2726
LintId::of(methods::SUSPICIOUS_MAP),

‎src/tools/clippy/clippy_lints/src/loops/for_loops_over_fallibles.rs

Lines changed: 0 additions & 65 deletions
This file was deleted.

‎src/tools/clippy/clippy_lints/src/loops/iter_next_loop.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use rustc_hir::Expr;
55
use rustc_lint::LateContext;
66
use rustc_span::sym;
77

8-
pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>) -> bool {
8+
pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>) {
99
if is_trait_method(cx, arg, sym::Iterator) {
1010
span_lint(
1111
cx,
@@ -14,8 +14,5 @@ pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>) -> bool {
1414
"you are iterating over `Iterator::next()` which is an Option; this will compile but is \
1515
probably not what you want",
1616
);
17-
true
18-
} else {
19-
false
2017
}
2118
}

‎src/tools/clippy/clippy_lints/src/loops/mod.rs

Lines changed: 2 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ mod explicit_counter_loop;
33
mod explicit_into_iter_loop;
44
mod explicit_iter_loop;
55
mod for_kv_map;
6-
mod for_loops_over_fallibles;
76
mod iter_next_loop;
87
mod manual_find;
98
mod manual_flatten;
@@ -173,49 +172,6 @@ declare_clippy_lint! {
173172
"for-looping over `_.next()` which is probably not intended"
174173
}
175174

176-
declare_clippy_lint! {
177-
/// ### What it does
178-
/// Checks for `for` loops over `Option` or `Result` values.
179-
///
180-
/// ### Why is this bad?
181-
/// Readability. This is more clearly expressed as an `if
182-
/// let`.
183-
///
184-
/// ### Example
185-
/// ```rust
186-
/// # let opt = Some(1);
187-
/// # let res: Result<i32, std::io::Error> = Ok(1);
188-
/// for x in opt {
189-
/// // ..
190-
/// }
191-
///
192-
/// for x in &res {
193-
/// // ..
194-
/// }
195-
///
196-
/// for x in res.iter() {
197-
/// // ..
198-
/// }
199-
/// ```
200-
///
201-
/// Use instead:
202-
/// ```rust
203-
/// # let opt = Some(1);
204-
/// # let res: Result<i32, std::io::Error> = Ok(1);
205-
/// if let Some(x) = opt {
206-
/// // ..
207-
/// }
208-
///
209-
/// if let Ok(x) = res {
210-
/// // ..
211-
/// }
212-
/// ```
213-
#[clippy::version = "1.45.0"]
214-
pub FOR_LOOPS_OVER_FALLIBLES,
215-
suspicious,
216-
"for-looping over an `Option` or a `Result`, which is more clearly expressed as an `if let`"
217-
}
218-
219175
declare_clippy_lint! {
220176
/// ### What it does
221177
/// Detects `loop + match` combinations that are easier
@@ -648,7 +604,6 @@ declare_lint_pass!(Loops => [
648604
EXPLICIT_ITER_LOOP,
649605
EXPLICIT_INTO_ITER_LOOP,
650606
ITER_NEXT_LOOP,
651-
FOR_LOOPS_OVER_FALLIBLES,
652607
WHILE_LET_LOOP,
653608
NEEDLESS_COLLECT,
654609
EXPLICIT_COUNTER_LOOP,
@@ -739,30 +694,22 @@ fn check_for_loop<'tcx>(
739694
manual_find::check(cx, pat, arg, body, span, expr);
740695
}
741696

742-
fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) {
743-
let mut next_loop_linted = false; // whether or not ITER_NEXT_LOOP lint was used
744-
697+
fn check_for_loop_arg(cx: &LateContext<'_>, _: &Pat<'_>, arg: &Expr<'_>) {
745698
if let ExprKind::MethodCall(method, self_arg, [], _) = arg.kind {
746699
let method_name = method.ident.as_str();
747700
// check for looping over x.iter() or x.iter_mut(), could use &x or &mut x
748701
match method_name {
749702
"iter" | "iter_mut" => {
750703
explicit_iter_loop::check(cx, self_arg, arg, method_name);
751-
for_loops_over_fallibles::check(cx, pat, self_arg, Some(method_name));
752704
},
753705
"into_iter" => {
754706
explicit_iter_loop::check(cx, self_arg, arg, method_name);
755707
explicit_into_iter_loop::check(cx, self_arg, arg);
756-
for_loops_over_fallibles::check(cx, pat, self_arg, Some(method_name));
757708
},
758709
"next" => {
759-
next_loop_linted = iter_next_loop::check(cx, arg);
710+
iter_next_loop::check(cx, arg);
760711
},
761712
_ => {},
762713
}
763714
}
764-
765-
if !next_loop_linted {
766-
for_loops_over_fallibles::check(cx, pat, arg, None);
767-
}
768715
}

‎src/tools/clippy/clippy_lints/src/renamed_lints.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[
1111
("clippy::disallowed_method", "clippy::disallowed_methods"),
1212
("clippy::disallowed_type", "clippy::disallowed_types"),
1313
("clippy::eval_order_dependence", "clippy::mixed_read_write_in_expression"),
14-
("clippy::for_loop_over_option", "clippy::for_loops_over_fallibles"),
15-
("clippy::for_loop_over_result", "clippy::for_loops_over_fallibles"),
14+
("clippy::for_loop_over_option", "for_loops_over_fallibles"),
15+
("clippy::for_loop_over_result", "for_loops_over_fallibles"),
1616
("clippy::identity_conversion", "clippy::useless_conversion"),
1717
("clippy::if_let_some_result", "clippy::match_result_ok"),
1818
("clippy::logic_bug", "clippy::overly_complex_bool_expr"),
@@ -31,6 +31,7 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[
3131
("clippy::to_string_in_display", "clippy::recursive_format_impl"),
3232
("clippy::zero_width_space", "clippy::invisible_characters"),
3333
("clippy::drop_bounds", "drop_bounds"),
34+
("clippy::for_loops_over_fallibles", "for_loops_over_fallibles"),
3435
("clippy::into_iter_on_array", "array_into_iter"),
3536
("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"),
3637
("clippy::invalid_ref", "invalid_value"),

‎src/tools/clippy/src/docs.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,6 @@ docs! {
170170
"fn_to_numeric_cast_any",
171171
"fn_to_numeric_cast_with_truncation",
172172
"for_kv_map",
173-
"for_loops_over_fallibles",
174173
"forget_copy",
175174
"forget_non_drop",
176175
"forget_ref",

‎src/tools/clippy/src/docs/for_loops_over_fallibles.txt

Lines changed: 0 additions & 32 deletions
This file was deleted.

‎src/tools/clippy/tests/ui/for_loop_unfixable.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
clippy::for_kv_map
99
)]
1010
#[allow(clippy::linkedlist, clippy::unnecessary_mut_passed, clippy::similar_names)]
11+
#[allow(for_loops_over_fallibles)]
1112
fn main() {
1213
let vec = vec![1, 2, 3, 4];
1314

‎src/tools/clippy/tests/ui/for_loop_unfixable.stderr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want
2-
--> $DIR/for_loop_unfixable.rs:14:15
2+
--> $DIR/for_loop_unfixable.rs:15:15
33
|
44
LL | for _v in vec.iter().next() {}
55
| ^^^^^^^^^^^^^^^^^

‎src/tools/clippy/tests/ui/for_loops_over_fallibles.rs

Lines changed: 0 additions & 73 deletions
This file was deleted.

‎src/tools/clippy/tests/ui/for_loops_over_fallibles.stderr

Lines changed: 0 additions & 95 deletions
This file was deleted.

‎src/tools/clippy/tests/ui/manual_map_option.fixed

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
clippy::unit_arg,
88
clippy::match_ref_pats,
99
clippy::redundant_pattern_matching,
10-
clippy::for_loops_over_fallibles,
10+
for_loops_over_fallibles,
1111
dead_code
1212
)]
1313

‎src/tools/clippy/tests/ui/manual_map_option.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
clippy::unit_arg,
88
clippy::match_ref_pats,
99
clippy::redundant_pattern_matching,
10-
clippy::for_loops_over_fallibles,
10+
for_loops_over_fallibles,
1111
dead_code
1212
)]
1313

‎src/tools/clippy/tests/ui/rename.fixed

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
#![allow(clippy::disallowed_methods)]
1313
#![allow(clippy::disallowed_types)]
1414
#![allow(clippy::mixed_read_write_in_expression)]
15-
#![allow(clippy::for_loops_over_fallibles)]
15+
#![allow(for_loops_over_fallibles)]
1616
#![allow(clippy::useless_conversion)]
1717
#![allow(clippy::match_result_ok)]
1818
#![allow(clippy::overly_complex_bool_expr)]
@@ -45,8 +45,8 @@
4545
#![warn(clippy::disallowed_methods)]
4646
#![warn(clippy::disallowed_types)]
4747
#![warn(clippy::mixed_read_write_in_expression)]
48-
#![warn(clippy::for_loops_over_fallibles)]
49-
#![warn(clippy::for_loops_over_fallibles)]
48+
#![warn(for_loops_over_fallibles)]
49+
#![warn(for_loops_over_fallibles)]
5050
#![warn(clippy::useless_conversion)]
5151
#![warn(clippy::match_result_ok)]
5252
#![warn(clippy::overly_complex_bool_expr)]
@@ -65,6 +65,7 @@
6565
#![warn(clippy::recursive_format_impl)]
6666
#![warn(clippy::invisible_characters)]
6767
#![warn(drop_bounds)]
68+
#![warn(for_loops_over_fallibles)]
6869
#![warn(array_into_iter)]
6970
#![warn(invalid_atomic_ordering)]
7071
#![warn(invalid_value)]

‎src/tools/clippy/tests/ui/rename.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
#![allow(clippy::disallowed_methods)]
1313
#![allow(clippy::disallowed_types)]
1414
#![allow(clippy::mixed_read_write_in_expression)]
15-
#![allow(clippy::for_loops_over_fallibles)]
15+
#![allow(for_loops_over_fallibles)]
1616
#![allow(clippy::useless_conversion)]
1717
#![allow(clippy::match_result_ok)]
1818
#![allow(clippy::overly_complex_bool_expr)]
@@ -65,6 +65,7 @@
6565
#![warn(clippy::to_string_in_display)]
6666
#![warn(clippy::zero_width_space)]
6767
#![warn(clippy::drop_bounds)]
68+
#![warn(clippy::for_loops_over_fallibles)]
6869
#![warn(clippy::into_iter_on_array)]
6970
#![warn(clippy::invalid_atomic_ordering)]
7071
#![warn(clippy::invalid_ref)]

‎src/tools/clippy/tests/ui/rename.stderr

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -54,17 +54,17 @@ error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_r
5454
LL | #![warn(clippy::eval_order_dependence)]
5555
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression`
5656

57-
error: lint `clippy::for_loop_over_option` has been renamed to `clippy::for_loops_over_fallibles`
57+
error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles`
5858
--> $DIR/rename.rs:48:9
5959
|
6060
LL | #![warn(clippy::for_loop_over_option)]
61-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles`
61+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles`
6262

63-
error: lint `clippy::for_loop_over_result` has been renamed to `clippy::for_loops_over_fallibles`
63+
error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles`
6464
--> $DIR/rename.rs:49:9
6565
|
6666
LL | #![warn(clippy::for_loop_over_result)]
67-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::for_loops_over_fallibles`
67+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles`
6868

6969
error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion`
7070
--> $DIR/rename.rs:50:9
@@ -174,59 +174,65 @@ error: lint `clippy::drop_bounds` has been renamed to `drop_bounds`
174174
LL | #![warn(clippy::drop_bounds)]
175175
| ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds`
176176

177-
error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter`
177+
error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles`
178178
--> $DIR/rename.rs:68:9
179179
|
180+
LL | #![warn(clippy::for_loops_over_fallibles)]
181+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles`
182+
183+
error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter`
184+
--> $DIR/rename.rs:69:9
185+
|
180186
LL | #![warn(clippy::into_iter_on_array)]
181187
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter`
182188

183189
error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering`
184-
--> $DIR/rename.rs:69:9
190+
--> $DIR/rename.rs:70:9
185191
|
186192
LL | #![warn(clippy::invalid_atomic_ordering)]
187193
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering`
188194

189195
error: lint `clippy::invalid_ref` has been renamed to `invalid_value`
190-
--> $DIR/rename.rs:70:9
196+
--> $DIR/rename.rs:71:9
191197
|
192198
LL | #![warn(clippy::invalid_ref)]
193199
| ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value`
194200

195201
error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums`
196-
--> $DIR/rename.rs:71:9
202+
--> $DIR/rename.rs:72:9
197203
|
198204
LL | #![warn(clippy::mem_discriminant_non_enum)]
199205
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums`
200206

201207
error: lint `clippy::panic_params` has been renamed to `non_fmt_panics`
202-
--> $DIR/rename.rs:72:9
208+
--> $DIR/rename.rs:73:9
203209
|
204210
LL | #![warn(clippy::panic_params)]
205211
| ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics`
206212

207213
error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally`
208-
--> $DIR/rename.rs:73:9
214+
--> $DIR/rename.rs:74:9
209215
|
210216
LL | #![warn(clippy::positional_named_format_parameters)]
211217
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally`
212218

213219
error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr`
214-
--> $DIR/rename.rs:74:9
220+
--> $DIR/rename.rs:75:9
215221
|
216222
LL | #![warn(clippy::temporary_cstring_as_ptr)]
217223
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr`
218224

219225
error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints`
220-
--> $DIR/rename.rs:75:9
226+
--> $DIR/rename.rs:76:9
221227
|
222228
LL | #![warn(clippy::unknown_clippy_lints)]
223229
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints`
224230

225231
error: lint `clippy::unused_label` has been renamed to `unused_labels`
226-
--> $DIR/rename.rs:76:9
232+
--> $DIR/rename.rs:77:9
227233
|
228234
LL | #![warn(clippy::unused_label)]
229235
| ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels`
230236

231-
error: aborting due to 38 previous errors
237+
error: aborting due to 39 previous errors
232238

0 commit comments

Comments
 (0)
Please sign in to comment.