Skip to content

Commit

Permalink
Auto merge of #13512 - samueltardieu:issue-13511, r=xFrednet
Browse files Browse the repository at this point in the history
`infinite_loop`: continuing an outer loop leaves the inner loop

changelog: [`infinite_loop`]: detect a `continue` targeting an outer loop

Fix #13511
  • Loading branch information
bors committed Oct 8, 2024
2 parents 6f4bf90 + 99ce411 commit d9c8d97
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 2 deletions.
17 changes: 16 additions & 1 deletion clippy_lints/src/loops/infinite_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ pub(super) fn check<'tcx>(
let mut loop_visitor = LoopVisitor {
cx,
label,
inner_labels: label.into_iter().collect(),
is_finite: false,
loop_depth: 0,
};
Expand Down Expand Up @@ -93,6 +94,7 @@ fn get_parent_fn_ret_ty<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option
struct LoopVisitor<'hir, 'tcx> {
cx: &'hir LateContext<'tcx>,
label: Option<Label>,
inner_labels: Vec<Label>,
loop_depth: usize,
is_finite: bool,
}
Expand All @@ -108,11 +110,24 @@ impl<'hir> Visitor<'hir> for LoopVisitor<'hir, '_> {
self.is_finite = true;
}
},
ExprKind::Continue(hir::Destination { label, .. }) => {
// Check whether we are leaving this loop by continuing into an outer loop
// whose label we did not encounter.
if label.is_some_and(|label| !self.inner_labels.contains(&label)) {
self.is_finite = true;
}
},
ExprKind::Ret(..) => self.is_finite = true,
ExprKind::Loop(..) => {
ExprKind::Loop(_, label, _, _) => {
if let Some(label) = label {
self.inner_labels.push(*label);
}
self.loop_depth += 1;
walk_expr(self, ex);
self.loop_depth -= 1;
if label.is_some() {
self.inner_labels.pop();
}
},
_ => {
// Calls to a function that never return
Expand Down
38 changes: 38 additions & 0 deletions tests/ui/infinite_loops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -390,4 +390,42 @@ fn span_inside_fn() {
}
}

fn continue_outer() {
// Should not lint (issue #13511)
let mut count = 0;
'outer: loop {
if count != 0 {
break;
}

loop {
count += 1;
continue 'outer;
}
}

// This should lint as we continue the loop itself
'infinite: loop {
//~^ ERROR: infinite loop detected
loop {
continue 'infinite;
}
}
// This should lint as we continue an inner loop
loop {
//~^ ERROR: infinite loop detected
'inner: loop {
loop {
continue 'inner;
}
}
}

// This should lint as we continue the loop itself
loop {
//~^ ERROR: infinite loop detected
continue;
}
}

fn main() {}
64 changes: 63 additions & 1 deletion tests/ui/infinite_loops.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -255,5 +255,67 @@ LL | | })
|
= help: if this is not intended, try adding a `break` or `return` condition in the loop

error: aborting due to 17 previous errors
error: infinite loop detected
--> tests/ui/infinite_loops.rs:408:5
|
LL | / 'infinite: loop {
LL | |
LL | | loop {
LL | | continue 'infinite;
LL | | }
LL | | }
| |_____^
|
help: if this is intentional, consider specifying `!` as function return
|
LL | fn continue_outer() -> ! {
| ++++

error: infinite loop detected
--> tests/ui/infinite_loops.rs:415:5
|
LL | / loop {
LL | |
LL | | 'inner: loop {
LL | | loop {
... |
LL | | }
LL | | }
| |_____^
|
help: if this is intentional, consider specifying `!` as function return
|
LL | fn continue_outer() -> ! {
| ++++

error: infinite loop detected
--> tests/ui/infinite_loops.rs:417:9
|
LL | / 'inner: loop {
LL | | loop {
LL | | continue 'inner;
LL | | }
LL | | }
| |_________^
|
help: if this is intentional, consider specifying `!` as function return
|
LL | fn continue_outer() -> ! {
| ++++

error: infinite loop detected
--> tests/ui/infinite_loops.rs:425:5
|
LL | / loop {
LL | |
LL | | continue;
LL | | }
| |_____^
|
help: if this is intentional, consider specifying `!` as function return
|
LL | fn continue_outer() -> ! {
| ++++

error: aborting due to 21 previous errors

0 comments on commit d9c8d97

Please sign in to comment.