diff options
| author | Samuel Tardieu <sam@rfc1149.net> | 2024-10-06 17:38:36 +0200 |
|---|---|---|
| committer | Samuel Tardieu <sam@rfc1149.net> | 2024-10-06 19:19:27 +0200 |
| commit | 99ce411ed67fd73fc9d3e4afebbdf3472d25bd10 (patch) | |
| tree | ac443abaaa22bc7674e04371765aa68e0dccd439 | |
| parent | 1c9e5d0e5caa0ffacda31774d7eca334bf55ce7d (diff) | |
| download | rust-99ce411ed67fd73fc9d3e4afebbdf3472d25bd10.tar.gz rust-99ce411ed67fd73fc9d3e4afebbdf3472d25bd10.zip | |
`infinite_loop`: continuing an outer loop leaves the inner loop
| -rw-r--r-- | clippy_lints/src/loops/infinite_loop.rs | 17 | ||||
| -rw-r--r-- | tests/ui/infinite_loops.rs | 38 | ||||
| -rw-r--r-- | tests/ui/infinite_loops.stderr | 64 |
3 files changed, 117 insertions, 2 deletions
diff --git a/clippy_lints/src/loops/infinite_loop.rs b/clippy_lints/src/loops/infinite_loop.rs index 64ae0807015..e25c03db534 100644 --- a/clippy_lints/src/loops/infinite_loop.rs +++ b/clippy_lints/src/loops/infinite_loop.rs @@ -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, }; @@ -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, } @@ -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 diff --git a/tests/ui/infinite_loops.rs b/tests/ui/infinite_loops.rs index b2d522fa011..b6cb7ff49b0 100644 --- a/tests/ui/infinite_loops.rs +++ b/tests/ui/infinite_loops.rs @@ -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() {} diff --git a/tests/ui/infinite_loops.stderr b/tests/ui/infinite_loops.stderr index ec6bd81dc17..7635a7442f4 100644 --- a/tests/ui/infinite_loops.stderr +++ b/tests/ui/infinite_loops.stderr @@ -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 |
