about summary refs log tree commit diff
path: root/src/test
diff options
context:
space:
mode:
authorDylan DPC <99973273+Dylan-DPC@users.noreply.github.com>2022-10-10 13:43:40 +0530
committerGitHub <noreply@github.com>2022-10-10 13:43:40 +0530
commit7e16f9f1eaea28e7fe2fc93319fdfcd4e1968c91 (patch)
treeb96433616143a8f0a449cec841fc9be07de08d4b /src/test
parente495b37c9a301d776a7bd0c72d5c4a202e159032 (diff)
parent9d4edff1b0179e3514bcc27618edd6348903e99a (diff)
downloadrust-7e16f9f1eaea28e7fe2fc93319fdfcd4e1968c91.tar.gz
rust-7e16f9f1eaea28e7fe2fc93319fdfcd4e1968c91.zip
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
Diffstat (limited to 'src/test')
-rw-r--r--src/test/ui/drop/dropck_legal_cycles.rs14
-rw-r--r--src/test/ui/issues/issue-30371.rs1
-rw-r--r--src/test/ui/lint/for_loop_over_fallibles.rs43
-rw-r--r--src/test/ui/lint/for_loop_over_fallibles.stderr101
4 files changed, 152 insertions, 7 deletions
diff --git a/src/test/ui/drop/dropck_legal_cycles.rs b/src/test/ui/drop/dropck_legal_cycles.rs
index 27a599315dc..6a0fe7784fb 100644
--- a/src/test/ui/drop/dropck_legal_cycles.rs
+++ b/src/test/ui/drop/dropck_legal_cycles.rs
@@ -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);
             }
         }
diff --git a/src/test/ui/issues/issue-30371.rs b/src/test/ui/issues/issue-30371.rs
index a1ae9a36bc1..eea548c482f 100644
--- a/src/test/ui/issues/issue-30371.rs
+++ b/src/test/ui/issues/issue-30371.rs
@@ -1,5 +1,6 @@
 // run-pass
 #![allow(unreachable_code)]
+#![allow(for_loops_over_fallibles)]
 #![deny(unused_variables)]
 
 fn main() {
diff --git a/src/test/ui/lint/for_loop_over_fallibles.rs b/src/test/ui/lint/for_loop_over_fallibles.rs
new file mode 100644
index 00000000000..43d71c2e808
--- /dev/null
+++ b/src/test/ui/lint/for_loop_over_fallibles.rs
@@ -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(())
+}
diff --git a/src/test/ui/lint/for_loop_over_fallibles.stderr b/src/test/ui/lint/for_loop_over_fallibles.stderr
new file mode 100644
index 00000000000..96efdf85c49
--- /dev/null
+++ b/src/test/ui/lint/for_loop_over_fallibles.stderr
@@ -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
+