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
This commit is contained in:
Dylan DPC 2022-10-10 13:43:40 +05:30 committed by GitHub
commit 7e16f9f1ea
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 381 additions and 366 deletions

View file

@ -1017,7 +1017,7 @@ impl<'a> Children<'a> for HM<'a> {
where C: Context + PrePost<Self>, Self: Sized
{
if let Some(ref hm) = self.contents.get() {
for (k, v) in hm.iter().nth(index / 2) {
if let Some((k, v)) = hm.iter().nth(index / 2) {
[k, v][index % 2].descend_into_self(context);
}
}
@ -1032,7 +1032,7 @@ impl<'a> Children<'a> for VD<'a> {
where C: Context + PrePost<Self>, Self: Sized
{
if let Some(ref vd) = self.contents.get() {
for r in vd.iter().nth(index) {
if let Some(r) = vd.iter().nth(index) {
r.descend_into_self(context);
}
}
@ -1047,7 +1047,7 @@ impl<'a> Children<'a> for VM<'a> {
where C: Context + PrePost<VM<'a>>
{
if let Some(ref vd) = self.contents.get() {
for (_idx, r) in vd.iter().nth(index) {
if let Some((_idx, r)) = vd.iter().nth(index) {
r.descend_into_self(context);
}
}
@ -1062,7 +1062,7 @@ impl<'a> Children<'a> for LL<'a> {
where C: Context + PrePost<LL<'a>>
{
if let Some(ref ll) = self.contents.get() {
for r in ll.iter().nth(index) {
if let Some(r) = ll.iter().nth(index) {
r.descend_into_self(context);
}
}
@ -1077,7 +1077,7 @@ impl<'a> Children<'a> for BH<'a> {
where C: Context + PrePost<BH<'a>>
{
if let Some(ref bh) = self.contents.get() {
for r in bh.iter().nth(index) {
if let Some(r) = bh.iter().nth(index) {
r.descend_into_self(context);
}
}
@ -1092,7 +1092,7 @@ impl<'a> Children<'a> for BTM<'a> {
where C: Context + PrePost<BTM<'a>>
{
if let Some(ref bh) = self.contents.get() {
for (k, v) in bh.iter().nth(index / 2) {
if let Some((k, v)) = bh.iter().nth(index / 2) {
[k, v][index % 2].descend_into_self(context);
}
}
@ -1107,7 +1107,7 @@ impl<'a> Children<'a> for BTS<'a> {
where C: Context + PrePost<BTS<'a>>
{
if let Some(ref bh) = self.contents.get() {
for r in bh.iter().nth(index) {
if let Some(r) = bh.iter().nth(index) {
r.descend_into_self(context);
}
}

View file

@ -1,5 +1,6 @@
// run-pass
#![allow(unreachable_code)]
#![allow(for_loops_over_fallibles)]
#![deny(unused_variables)]
fn main() {

View file

@ -0,0 +1,43 @@
// check-pass
fn main() {
// Common
for _ in Some(1) {}
//~^ WARN for loop over an `Option`. This is more readably written as an `if let` statement
//~| HELP to check pattern in a loop use `while let`
//~| HELP consider using `if let` to clear intent
for _ in Ok::<_, ()>(1) {}
//~^ WARN for loop over a `Result`. This is more readably written as an `if let` statement
//~| HELP to check pattern in a loop use `while let`
//~| HELP consider using `if let` to clear intent
// `Iterator::next` specific
for _ in [0; 0].iter().next() {}
//~^ WARN for loop over an `Option`. This is more readably written as an `if let` statement
//~| HELP to iterate over `[0; 0].iter()` remove the call to `next`
//~| HELP consider using `if let` to clear intent
// `Result<impl Iterator, _>`, but function doesn't return `Result`
for _ in Ok::<_, ()>([0; 0].iter()) {}
//~^ WARN for loop over a `Result`. This is more readably written as an `if let` statement
//~| HELP to check pattern in a loop use `while let`
//~| HELP consider using `if let` to clear intent
}
fn _returns_result() -> Result<(), ()> {
// `Result<impl Iterator, _>`
for _ in Ok::<_, ()>([0; 0].iter()) {}
//~^ WARN for loop over a `Result`. This is more readably written as an `if let` statement
//~| HELP to check pattern in a loop use `while let`
//~| HELP consider unwrapping the `Result` with `?` to iterate over its contents
//~| HELP consider using `if let` to clear intent
// `Result<impl IntoIterator>`
for _ in Ok::<_, ()>([0; 0]) {}
//~^ WARN for loop over a `Result`. This is more readably written as an `if let` statement
//~| HELP to check pattern in a loop use `while let`
//~| HELP consider unwrapping the `Result` with `?` to iterate over its contents
//~| HELP consider using `if let` to clear intent
Ok(())
}

View file

@ -0,0 +1,101 @@
warning: for loop over an `Option`. This is more readably written as an `if let` statement
--> $DIR/for_loop_over_fallibles.rs:5:14
|
LL | for _ in Some(1) {}
| ^^^^^^^
|
= note: `#[warn(for_loops_over_fallibles)]` on by default
help: to check pattern in a loop use `while let`
|
LL | while let Some(_) = Some(1) {}
| ~~~~~~~~~~~~~~~ ~~~
help: consider using `if let` to clear intent
|
LL | if let Some(_) = Some(1) {}
| ~~~~~~~~~~~~ ~~~
warning: for loop over a `Result`. This is more readably written as an `if let` statement
--> $DIR/for_loop_over_fallibles.rs:9:14
|
LL | for _ in Ok::<_, ()>(1) {}
| ^^^^^^^^^^^^^^
|
help: to check pattern in a loop use `while let`
|
LL | while let Ok(_) = Ok::<_, ()>(1) {}
| ~~~~~~~~~~~~~ ~~~
help: consider using `if let` to clear intent
|
LL | if let Ok(_) = Ok::<_, ()>(1) {}
| ~~~~~~~~~~ ~~~
warning: for loop over an `Option`. This is more readably written as an `if let` statement
--> $DIR/for_loop_over_fallibles.rs:15:14
|
LL | for _ in [0; 0].iter().next() {}
| ^^^^^^^^^^^^^^^^^^^^
|
help: to iterate over `[0; 0].iter()` remove the call to `next`
|
LL | for _ in [0; 0].iter().by_ref() {}
| ~~~~~~~~~
help: consider using `if let` to clear intent
|
LL | if let Some(_) = [0; 0].iter().next() {}
| ~~~~~~~~~~~~ ~~~
warning: for loop over a `Result`. This is more readably written as an `if let` statement
--> $DIR/for_loop_over_fallibles.rs:21:14
|
LL | for _ in Ok::<_, ()>([0; 0].iter()) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: to check pattern in a loop use `while let`
|
LL | while let Ok(_) = Ok::<_, ()>([0; 0].iter()) {}
| ~~~~~~~~~~~~~ ~~~
help: consider using `if let` to clear intent
|
LL | if let Ok(_) = Ok::<_, ()>([0; 0].iter()) {}
| ~~~~~~~~~~ ~~~
warning: for loop over a `Result`. This is more readably written as an `if let` statement
--> $DIR/for_loop_over_fallibles.rs:29:14
|
LL | for _ in Ok::<_, ()>([0; 0].iter()) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: to check pattern in a loop use `while let`
|
LL | while let Ok(_) = Ok::<_, ()>([0; 0].iter()) {}
| ~~~~~~~~~~~~~ ~~~
help: consider unwrapping the `Result` with `?` to iterate over its contents
|
LL | for _ in Ok::<_, ()>([0; 0].iter())? {}
| +
help: consider using `if let` to clear intent
|
LL | if let Ok(_) = Ok::<_, ()>([0; 0].iter()) {}
| ~~~~~~~~~~ ~~~
warning: for loop over a `Result`. This is more readably written as an `if let` statement
--> $DIR/for_loop_over_fallibles.rs:36:14
|
LL | for _ in Ok::<_, ()>([0; 0]) {}
| ^^^^^^^^^^^^^^^^^^^
|
help: to check pattern in a loop use `while let`
|
LL | while let Ok(_) = Ok::<_, ()>([0; 0]) {}
| ~~~~~~~~~~~~~ ~~~
help: consider unwrapping the `Result` with `?` to iterate over its contents
|
LL | for _ in Ok::<_, ()>([0; 0])? {}
| +
help: consider using `if let` to clear intent
|
LL | if let Ok(_) = Ok::<_, ()>([0; 0]) {}
| ~~~~~~~~~~ ~~~
warning: 6 warnings emitted