Skip to content

Commit

Permalink
Auto merge of #11450 - digama0:never_loop2, r=llogiq
Browse files Browse the repository at this point in the history
`never_loop` catches `loop { panic!() }`

* Depends on: #11447

This is an outgrowth of #11447 which I felt would best be done as a separate PR because it yields significant new results.

This uses typecheck results to determine divergence, meaning we can now detect cases like `loop { std::process::abort() }` or `loop { panic!() }`. A downside is that `loop { unimplemented!() }` is also being linted, which is arguably a false positive. I'm not really sure how to check this from HIR though, and it seems best to leave this epicycle for a later PR.

changelog: [`never_loop`]: Now lints on `loop { panic!() }` and similar constructs
  • Loading branch information
bors committed Sep 2, 2023
2 parents b65e544 + 44f64ac commit b9906ac
Show file tree
Hide file tree
Showing 16 changed files with 281 additions and 193 deletions.
256 changes: 128 additions & 128 deletions clippy_lints/src/loops/never_loop.rs

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion tests/ui/crashes/ice-360.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ fn main() {}
fn no_panic<T>(slice: &[T]) {
let mut iter = slice.iter();
loop {
//~^ ERROR: this loop could be written as a `while let` loop
//~^ ERROR: this loop never actually loops
//~| ERROR: this loop could be written as a `while let` loop
//~| NOTE: `-D clippy::while-let-loop` implied by `-D warnings`
let _ = match iter.next() {
Some(ele) => ele,
Expand Down
20 changes: 17 additions & 3 deletions tests/ui/crashes/ice-360.stderr
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
error: this loop never actually loops
--> $DIR/ice-360.rs:5:5
|
LL | / loop {
LL | |
LL | |
LL | |
... |
LL | |
LL | | }
| |_____^
|
= note: `#[deny(clippy::never_loop)]` on by default

error: this loop could be written as a `while let` loop
--> $DIR/ice-360.rs:5:5
|
LL | / loop {
LL | |
LL | |
LL | | let _ = match iter.next() {
LL | |
... |
LL | |
LL | | }
Expand All @@ -13,13 +27,13 @@ LL | | }
= note: `-D clippy::while-let-loop` implied by `-D warnings`

error: empty `loop {}` wastes CPU cycles
--> $DIR/ice-360.rs:12:9
--> $DIR/ice-360.rs:13:9
|
LL | loop {}
| ^^^^^^^
|
= help: you should either use `panic!()` or add `std::thread::sleep(..);` to the loop body
= note: `-D clippy::empty-loop` implied by `-D warnings`

error: aborting due to 2 previous errors
error: aborting due to 3 previous errors

3 changes: 3 additions & 0 deletions tests/ui/empty_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,20 @@ use proc_macros::{external, inline_macros};

fn should_trigger() {
loop {}
#[allow(clippy::never_loop)]
loop {
loop {}
}

#[allow(clippy::never_loop)]
'outer: loop {
'inner: loop {}
}
}

#[inline_macros]
fn should_not_trigger() {
#[allow(clippy::never_loop)]
loop {
panic!("This is fine")
}
Expand Down
4 changes: 2 additions & 2 deletions tests/ui/empty_loop.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ LL | loop {}
= note: `-D clippy::empty-loop` implied by `-D warnings`

error: empty `loop {}` wastes CPU cycles
--> $DIR/empty_loop.rs:11:9
--> $DIR/empty_loop.rs:12:9
|
LL | loop {}
| ^^^^^^^
|
= help: you should either use `panic!()` or add `std::thread::sleep(..);` to the loop body

error: empty `loop {}` wastes CPU cycles
--> $DIR/empty_loop.rs:15:9
--> $DIR/empty_loop.rs:17:9
|
LL | 'inner: loop {}
| ^^^^^^^^^^^^^^^
Expand Down
1 change: 1 addition & 0 deletions tests/ui/iter_out_of_bounds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ fn opaque_empty_iter() -> impl Iterator<Item = ()> {
}

fn main() {
#[allow(clippy::never_loop)]
for _ in [1, 2, 3].iter().skip(4) {
//~^ ERROR: this `.skip()` call skips more items than the iterator will produce
unreachable!();
Expand Down
28 changes: 14 additions & 14 deletions tests/ui/iter_out_of_bounds.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: this `.skip()` call skips more items than the iterator will produce
--> $DIR/iter_out_of_bounds.rs:11:14
--> $DIR/iter_out_of_bounds.rs:12:14
|
LL | for _ in [1, 2, 3].iter().skip(4) {
| ^^^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -12,103 +12,103 @@ LL | #![deny(clippy::iter_out_of_bounds)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^

error: this `.take()` call takes more items than the iterator will produce
--> $DIR/iter_out_of_bounds.rs:15:19
--> $DIR/iter_out_of_bounds.rs:16:19
|
LL | for (i, _) in [1, 2, 3].iter().take(4).enumerate() {
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this operation is useless and the returned iterator will simply yield the same items

error: this `.take()` call takes more items than the iterator will produce
--> $DIR/iter_out_of_bounds.rs:21:14
--> $DIR/iter_out_of_bounds.rs:22:14
|
LL | for _ in (&&&&&&[1, 2, 3]).iter().take(4) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this operation is useless and the returned iterator will simply yield the same items

error: this `.skip()` call skips more items than the iterator will produce
--> $DIR/iter_out_of_bounds.rs:24:14
--> $DIR/iter_out_of_bounds.rs:25:14
|
LL | for _ in [1, 2, 3].iter().skip(4) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this operation is useless and will create an empty iterator

error: this `.skip()` call skips more items than the iterator will produce
--> $DIR/iter_out_of_bounds.rs:27:14
--> $DIR/iter_out_of_bounds.rs:28:14
|
LL | for _ in [1; 3].iter().skip(4) {}
| ^^^^^^^^^^^^^^^^^^^^^
|
= note: this operation is useless and will create an empty iterator

error: this `.skip()` call skips more items than the iterator will produce
--> $DIR/iter_out_of_bounds.rs:33:14
--> $DIR/iter_out_of_bounds.rs:34:14
|
LL | for _ in vec![1, 2, 3].iter().skip(4) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this operation is useless and will create an empty iterator

error: this `.skip()` call skips more items than the iterator will produce
--> $DIR/iter_out_of_bounds.rs:36:14
--> $DIR/iter_out_of_bounds.rs:37:14
|
LL | for _ in vec![1; 3].iter().skip(4) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this operation is useless and will create an empty iterator

error: this `.skip()` call skips more items than the iterator will produce
--> $DIR/iter_out_of_bounds.rs:40:14
--> $DIR/iter_out_of_bounds.rs:41:14
|
LL | for _ in x.iter().skip(4) {}
| ^^^^^^^^^^^^^^^^
|
= note: this operation is useless and will create an empty iterator

error: this `.skip()` call skips more items than the iterator will produce
--> $DIR/iter_out_of_bounds.rs:44:14
--> $DIR/iter_out_of_bounds.rs:45:14
|
LL | for _ in x.iter().skip(n) {}
| ^^^^^^^^^^^^^^^^
|
= note: this operation is useless and will create an empty iterator

error: this `.skip()` call skips more items than the iterator will produce
--> $DIR/iter_out_of_bounds.rs:49:14
--> $DIR/iter_out_of_bounds.rs:50:14
|
LL | for _ in empty().skip(1) {}
| ^^^^^^^^^^^^^^^
|
= note: this operation is useless and will create an empty iterator

error: this `.take()` call takes more items than the iterator will produce
--> $DIR/iter_out_of_bounds.rs:52:14
--> $DIR/iter_out_of_bounds.rs:53:14
|
LL | for _ in empty().take(1) {}
| ^^^^^^^^^^^^^^^
|
= note: this operation is useless and the returned iterator will simply yield the same items

error: this `.skip()` call skips more items than the iterator will produce
--> $DIR/iter_out_of_bounds.rs:55:14
--> $DIR/iter_out_of_bounds.rs:56:14
|
LL | for _ in std::iter::once(1).skip(2) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this operation is useless and will create an empty iterator

error: this `.take()` call takes more items than the iterator will produce
--> $DIR/iter_out_of_bounds.rs:58:14
--> $DIR/iter_out_of_bounds.rs:59:14
|
LL | for _ in std::iter::once(1).take(2) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this operation is useless and the returned iterator will simply yield the same items

error: this `.take()` call takes more items than the iterator will produce
--> $DIR/iter_out_of_bounds.rs:61:14
--> $DIR/iter_out_of_bounds.rs:62:14
|
LL | for x in [].iter().take(1) {
| ^^^^^^^^^^^^^^^^^
Expand Down
6 changes: 6 additions & 0 deletions tests/ui/needless_collect_indirect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ mod issue_8553 {
let w = v.iter().collect::<Vec<_>>();
//~^ ERROR: avoid using `collect()` when not needed
// Do lint
#[allow(clippy::never_loop)]
for _ in 0..w.len() {
todo!();
}
Expand All @@ -270,6 +271,7 @@ mod issue_8553 {
let v: Vec<usize> = vec.iter().map(|i| i * i).collect();
let w = v.iter().collect::<Vec<_>>();
// Do not lint, because w is used.
#[allow(clippy::never_loop)]
for _ in 0..w.len() {
todo!();
}
Expand All @@ -283,6 +285,7 @@ mod issue_8553 {
let mut w = v.iter().collect::<Vec<_>>();
//~^ ERROR: avoid using `collect()` when not needed
// Do lint
#[allow(clippy::never_loop)]
while 1 == w.len() {
todo!();
}
Expand All @@ -293,6 +296,7 @@ mod issue_8553 {
let mut v: Vec<usize> = vec.iter().map(|i| i * i).collect();
let mut w = v.iter().collect::<Vec<_>>();
// Do not lint, because w is used.
#[allow(clippy::never_loop)]
while 1 == w.len() {
todo!();
}
Expand All @@ -306,6 +310,7 @@ mod issue_8553 {
let mut w = v.iter().collect::<Vec<_>>();
//~^ ERROR: avoid using `collect()` when not needed
// Do lint
#[allow(clippy::never_loop)]
while let Some(i) = Some(w.len()) {
todo!();
}
Expand All @@ -316,6 +321,7 @@ mod issue_8553 {
let mut v: Vec<usize> = vec.iter().map(|i| i * i).collect();
let mut w = v.iter().collect::<Vec<_>>();
// Do not lint, because w is used.
#[allow(clippy::never_loop)]
while let Some(i) = Some(w.len()) {
todo!();
}
Expand Down
7 changes: 5 additions & 2 deletions tests/ui/needless_collect_indirect.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -230,11 +230,12 @@ help: take the original Iterator's count instead of collecting it and finding th
LL ~
LL |
LL | // Do lint
LL | #[allow(clippy::never_loop)]
LL ~ for _ in 0..v.iter().count() {
|

error: avoid using `collect()` when not needed
--> $DIR/needless_collect_indirect.rs:283:30
--> $DIR/needless_collect_indirect.rs:285:30
|
LL | let mut w = v.iter().collect::<Vec<_>>();
| ^^^^^^^
Expand All @@ -247,11 +248,12 @@ help: take the original Iterator's count instead of collecting it and finding th
LL ~
LL |
LL | // Do lint
LL | #[allow(clippy::never_loop)]
LL ~ while 1 == v.iter().count() {
|

error: avoid using `collect()` when not needed
--> $DIR/needless_collect_indirect.rs:306:30
--> $DIR/needless_collect_indirect.rs:310:30
|
LL | let mut w = v.iter().collect::<Vec<_>>();
| ^^^^^^^
Expand All @@ -264,6 +266,7 @@ help: take the original Iterator's count instead of collecting it and finding th
LL ~
LL |
LL | // Do lint
LL | #[allow(clippy::never_loop)]
LL ~ while let Some(i) = Some(v.iter().count()) {
|

Expand Down
47 changes: 45 additions & 2 deletions tests/ui/never_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -337,10 +337,8 @@ pub fn test26() {

pub fn test27() {
loop {
//~^ ERROR: this loop never actually loops
'label: {
let x = true;
// Lints because we cannot prove it's always `true`
if x {
break 'label;
}
Expand All @@ -349,6 +347,51 @@ pub fn test27() {
}
}

// issue 11004
pub fn test29() {
loop {
'label: {
if true {
break 'label;
}
return;
}
}
}

pub fn test30() {
'a: loop {
'b: {
for j in 0..2 {
if j == 1 {
break 'b;
}
}
break 'a;
}
}
}

pub fn test31(b: bool) {
'a: loop {
'b: {
'c: loop {
//~^ ERROR: this loop never actually loops
if b { break 'c } else { break 'b }
}
continue 'a;
}
break 'a;
}
}

pub fn test32(b: bool) {
loop {
//~^ ERROR: this loop never actually loops
panic!("oh no");
}
}

fn main() {
test1();
test2();
Expand Down
18 changes: 12 additions & 6 deletions tests/ui/never_loop.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -153,16 +153,22 @@ LL | if let Some(_) = (0..20).next() {
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

error: this loop never actually loops
--> $DIR/never_loop.rs:339:5
--> $DIR/never_loop.rs:378:13
|
LL | / 'c: loop {
LL | |
LL | | if b { break 'c } else { break 'b }
LL | | }
| |_____________^

error: this loop never actually loops
--> $DIR/never_loop.rs:389:5
|
LL | / loop {
LL | |
LL | | 'label: {
LL | | let x = true;
... |
LL | | }
LL | | panic!("oh no");
LL | | }
| |_____^

error: aborting due to 14 previous errors
error: aborting due to 15 previous errors

Loading

0 comments on commit b9906ac

Please sign in to comment.