Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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