diff options
| author | lapla-cogito <me@lapla.dev> | 2025-02-12 22:25:58 +0900 |
|---|---|---|
| committer | lapla-cogito <me@lapla.dev> | 2025-03-10 15:00:57 +0900 |
| commit | 90dbc5bf94de29304c18d840c8c90f09f2d32cf6 (patch) | |
| tree | 18d9a491d7b4cc9c95517156cc90601a0cf124d7 /clippy_lints/src | |
| parent | 649cef0e81d1c095e9a643cac4998e1ff1910c6d (diff) | |
| download | rust-90dbc5bf94de29304c18d840c8c90f09f2d32cf6.tar.gz rust-90dbc5bf94de29304c18d840c8c90f09f2d32cf6.zip | |
make `never_loop` applicability more flexible
Diffstat (limited to 'clippy_lints/src')
| -rw-r--r-- | clippy_lints/src/loops/never_loop.rs | 25 |
1 files changed, 21 insertions, 4 deletions
diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index b679fdfadc3..3f8de774eef 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -4,11 +4,13 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::ForLoop; use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::source::snippet; +use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; use rustc_errors::Applicability; use rustc_hir::{Block, Destination, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind, StructTailExpr}; use rustc_lint::LateContext; use rustc_span::{Span, sym}; use std::iter::once; +use std::ops::ControlFlow; pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, @@ -24,17 +26,23 @@ pub(super) fn check<'tcx>( arg: iterator, pat, span: for_span, + label, .. }) = for_loop { - // Suggests using an `if let` instead. This is `Unspecified` because the - // loop may (probably) contain `break` statements which would be invalid - // in an `if let`. + // If the block contains a break or continue, or if the loop has a label, `MachineApplicable` is not + // appropriate. + let app = if !contains_any_break_or_continue(block) && label.is_none() { + Applicability::MachineApplicable + } else { + Applicability::Unspecified + }; + diag.span_suggestion_verbose( for_span.with_hi(iterator.span.hi()), "if you need the first element of the iterator, try writing", for_to_if_let_sugg(cx, iterator, pat), - Applicability::Unspecified, + app, ); } }); @@ -43,6 +51,15 @@ pub(super) fn check<'tcx>( } } +fn contains_any_break_or_continue(block: &Block<'_>) -> bool { + for_each_expr_without_closures(block, |e| match e.kind { + ExprKind::Break(..) | ExprKind::Continue(..) => ControlFlow::Break(()), + ExprKind::Loop(..) => ControlFlow::Continue(Descend::No), + _ => ControlFlow::Continue(Descend::Yes), + }) + .is_some() +} + /// The `never_loop` analysis keeps track of three things: /// /// * Has any (reachable) code path hit a `continue` of the main loop? |
