Auto merge of #13512 - samueltardieu:issue-13511, r=xFrednet

`infinite_loop`: continuing an outer loop leaves the inner loop

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

Fix #13511
This commit is contained in:
bors 2024-10-08 08:49:07 +00:00
commit d9c8d976cb
3 changed files with 117 additions and 2 deletions

View File

@ -42,6 +42,7 @@ pub(super) fn check<'tcx>(
let mut loop_visitor = LoopVisitor { let mut loop_visitor = LoopVisitor {
cx, cx,
label, label,
inner_labels: label.into_iter().collect(),
is_finite: false, is_finite: false,
loop_depth: 0, loop_depth: 0,
}; };
@ -93,6 +94,7 @@ fn get_parent_fn_ret_ty<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option
struct LoopVisitor<'hir, 'tcx> { struct LoopVisitor<'hir, 'tcx> {
cx: &'hir LateContext<'tcx>, cx: &'hir LateContext<'tcx>,
label: Option<Label>, label: Option<Label>,
inner_labels: Vec<Label>,
loop_depth: usize, loop_depth: usize,
is_finite: bool, is_finite: bool,
} }
@ -108,11 +110,24 @@ fn visit_expr(&mut self, ex: &'hir Expr<'_>) {
self.is_finite = true; 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::Ret(..) => self.is_finite = true,
ExprKind::Loop(..) => { ExprKind::Loop(_, label, _, _) => {
if let Some(label) = label {
self.inner_labels.push(*label);
}
self.loop_depth += 1; self.loop_depth += 1;
walk_expr(self, ex); walk_expr(self, ex);
self.loop_depth -= 1; self.loop_depth -= 1;
if label.is_some() {
self.inner_labels.pop();
}
}, },
_ => { _ => {
// Calls to a function that never return // Calls to a function that never return

View File

@ -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() {} fn main() {}

View File

@ -255,5 +255,67 @@ LL | | })
| |
= help: if this is not intended, try adding a `break` or `return` condition in the loop = 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