From d94a9febaed8554e1e9ebc9c007e0097f8f43545 Mon Sep 17 00:00:00 2001 From: Samuel Tardieu Date: Sun, 14 Sep 2025 09:02:15 +0200 Subject: [PATCH] Consider labels of inline asm as conditionally executed Since an `asm!()` statement is mostly unknown, as we do not know what it does, consider all labels' `break` as being conditionally executed only. --- clippy_lints/src/loops/never_loop.rs | 19 ++++++- tests/ui/never_loop.rs | 41 ++++++++++++--- tests/ui/never_loop.stderr | 77 ++++++++++++++++------------ 3 files changed, 94 insertions(+), 43 deletions(-) diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index 544c3c34d029..d01ce22c99a5 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -7,7 +7,7 @@ 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, Node, Pat, Stmt, StmtKind, StructTailExpr, + Block, Destination, Expr, ExprKind, HirId, InlineAsm, InlineAsmOperand, Node, Pat, Stmt, StmtKind, StructTailExpr, }; use rustc_lint::LateContext; use rustc_span::{BytePos, Span, sym}; @@ -75,12 +75,19 @@ 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::InlineAsm(asm) if contains_label(asm) => ControlFlow::Break(()), ExprKind::Loop(..) => ControlFlow::Continue(Descend::No), _ => ControlFlow::Continue(Descend::Yes), }) .is_some() } +fn contains_label(asm: &InlineAsm<'_>) -> bool { + asm.operands + .iter() + .any(|(op, _span)| matches!(op, InlineAsmOperand::Label { .. })) +} + /// The `never_loop` analysis keeps track of three things: /// /// * Has any (reachable) code path hit a `continue` of the main loop? @@ -378,7 +385,15 @@ fn never_loop_expr<'tcx>( InlineAsmOperand::Const { .. } | InlineAsmOperand::SymFn { .. } | InlineAsmOperand::SymStatic { .. } => { NeverLoopResult::Normal }, - InlineAsmOperand::Label { block } => never_loop_block(cx, block, local_labels, main_loop_id), + InlineAsmOperand::Label { block } => + // We do not know whether the label will be executed or not, so `Diverging` must be + // downgraded to `Normal`. + { + match never_loop_block(cx, block, local_labels, main_loop_id) { + NeverLoopResult::Diverging { .. } => NeverLoopResult::Normal, + result => result, + } + }, })), ExprKind::OffsetOf(_, _) | ExprKind::Yield(_, _) diff --git a/tests/ui/never_loop.rs b/tests/ui/never_loop.rs index 01db64a446c0..5f71f9f1729f 100644 --- a/tests/ui/never_loop.rs +++ b/tests/ui/never_loop.rs @@ -1,12 +1,9 @@ #![feature(try_blocks)] -#![allow( - clippy::eq_op, - clippy::single_match, - unused_assignments, - unused_variables, - clippy::while_immutable_condition -)] +#![expect(clippy::eq_op, clippy::single_match, clippy::while_immutable_condition)] //@no-rustfix + +use std::arch::asm; + fn test1() { let mut x = 0; loop { @@ -522,3 +519,33 @@ fn issue15350() { } } } + +fn issue15673() { + loop { + unsafe { + // No lint since we don't analyze the inside of the asm + asm! { + "/* {} */", + label { + break; + } + } + } + } + + //~v never_loop + loop { + // We don't check the applicability in tests, but this should + // be `Applicability::MaybeIncorrect` because of the `break` + // in the `asm!()` labels. + unsafe { + asm! { + "/* {} */", + label { + break; + } + } + } + return; + } +} diff --git a/tests/ui/never_loop.stderr b/tests/ui/never_loop.stderr index 4fda06cff4ab..7a33a5a5528a 100644 --- a/tests/ui/never_loop.stderr +++ b/tests/ui/never_loop.stderr @@ -1,5 +1,5 @@ error: this loop never actually loops - --> tests/ui/never_loop.rs:12:5 + --> tests/ui/never_loop.rs:9:5 | LL | / loop { ... | @@ -10,7 +10,7 @@ LL | | } = note: `#[deny(clippy::never_loop)]` on by default error: this loop never actually loops - --> tests/ui/never_loop.rs:36:5 + --> tests/ui/never_loop.rs:33:5 | LL | / loop { ... | @@ -19,7 +19,7 @@ LL | | } | |_____^ error: this loop never actually loops - --> tests/ui/never_loop.rs:58:5 + --> tests/ui/never_loop.rs:55:5 | LL | / loop { ... | @@ -28,7 +28,7 @@ LL | | } | |_____^ error: this loop never actually loops - --> tests/ui/never_loop.rs:62:9 + --> tests/ui/never_loop.rs:59:9 | LL | / while i == 0 { ... | @@ -36,7 +36,7 @@ LL | | } | |_________^ error: this loop never actually loops - --> tests/ui/never_loop.rs:76:9 + --> tests/ui/never_loop.rs:73:9 | LL | / loop { ... | @@ -45,7 +45,7 @@ LL | | } | |_________^ error: this loop never actually loops - --> tests/ui/never_loop.rs:114:5 + --> tests/ui/never_loop.rs:111:5 | LL | / while let Some(y) = x { ... | @@ -53,7 +53,7 @@ LL | | } | |_____^ error: this loop never actually loops - --> tests/ui/never_loop.rs:123:5 + --> tests/ui/never_loop.rs:120:5 | LL | / for x in 0..10 { ... | @@ -67,7 +67,7 @@ LL + if let Some(x) = (0..10).next() { | error: this loop never actually loops - --> tests/ui/never_loop.rs:173:5 + --> tests/ui/never_loop.rs:170:5 | LL | / 'outer: while a { ... | @@ -76,7 +76,7 @@ LL | | } | |_____^ error: this loop never actually loops - --> tests/ui/never_loop.rs:190:9 + --> tests/ui/never_loop.rs:187:9 | LL | / while false { LL | | @@ -86,7 +86,7 @@ LL | | } | |_________^ error: this loop never actually loops - --> tests/ui/never_loop.rs:243:13 + --> tests/ui/never_loop.rs:240:13 | LL | let _ = loop { | _____________^ @@ -99,7 +99,7 @@ LL | | }; | |_____^ error: this loop never actually loops - --> tests/ui/never_loop.rs:266:5 + --> tests/ui/never_loop.rs:263:5 | LL | / 'a: loop { LL | | @@ -110,7 +110,7 @@ LL | | } | |_____^ error: sub-expression diverges - --> tests/ui/never_loop.rs:271:17 + --> tests/ui/never_loop.rs:268:17 | LL | break 'a; | ^^^^^^^^ @@ -119,7 +119,7 @@ LL | break 'a; = help: to override `-D warnings` add `#[allow(clippy::diverging_sub_expression)]` error: this loop never actually loops - --> tests/ui/never_loop.rs:303:13 + --> tests/ui/never_loop.rs:300:13 | LL | / for _ in 0..20 { LL | | @@ -135,7 +135,7 @@ LL + if let Some(_) = (0..20).next() { | error: this loop never actually loops - --> tests/ui/never_loop.rs:388:13 + --> tests/ui/never_loop.rs:385:13 | LL | / 'c: loop { LL | | @@ -145,7 +145,7 @@ LL | | } | |_____________^ error: this loop never actually loops - --> tests/ui/never_loop.rs:400:5 + --> tests/ui/never_loop.rs:397:5 | LL | / loop { LL | | @@ -155,7 +155,7 @@ LL | | } | |_____^ error: this loop never actually loops - --> tests/ui/never_loop.rs:405:5 + --> tests/ui/never_loop.rs:402:5 | LL | / loop { LL | | @@ -165,7 +165,7 @@ LL | | } | |_____^ error: this loop never actually loops - --> tests/ui/never_loop.rs:426:5 + --> tests/ui/never_loop.rs:423:5 | LL | / for v in 0..10 { LL | | @@ -183,7 +183,7 @@ LL ~ | error: this loop never actually loops - --> tests/ui/never_loop.rs:434:5 + --> tests/ui/never_loop.rs:431:5 | LL | / 'outer: for v in 0..10 { LL | | @@ -194,7 +194,7 @@ LL | | } | |_____^ | help: this code is unreachable. Consider moving the reachable parts out - --> tests/ui/never_loop.rs:436:9 + --> tests/ui/never_loop.rs:433:9 | LL | / loop { LL | | @@ -202,7 +202,7 @@ LL | | break 'outer; LL | | } | |_________^ help: this code is unreachable. Consider moving the reachable parts out - --> tests/ui/never_loop.rs:440:9 + --> tests/ui/never_loop.rs:437:9 | LL | return; | ^^^^^^^ @@ -213,7 +213,7 @@ LL + if let Some(v) = (0..10).next() { | error: this loop never actually loops - --> tests/ui/never_loop.rs:436:9 + --> tests/ui/never_loop.rs:433:9 | LL | / loop { LL | | @@ -222,7 +222,7 @@ LL | | } | |_________^ error: this loop never actually loops - --> tests/ui/never_loop.rs:443:5 + --> tests/ui/never_loop.rs:440:5 | LL | / for v in 0..10 { LL | | @@ -239,7 +239,7 @@ LL + if let Some(v) = (0..10).next() { | error: this loop never actually loops - --> tests/ui/never_loop.rs:445:9 + --> tests/ui/never_loop.rs:442:9 | LL | / 'inner: loop { LL | | @@ -248,7 +248,7 @@ LL | | } | |_________^ error: this loop never actually loops - --> tests/ui/never_loop.rs:471:5 + --> tests/ui/never_loop.rs:468:5 | LL | / 'a: for _ in 0..1 { LL | | @@ -264,7 +264,7 @@ LL ~ | error: this loop never actually loops - --> tests/ui/never_loop.rs:477:5 + --> tests/ui/never_loop.rs:474:5 | LL | / 'a: for i in 0..1 { LL | | @@ -288,7 +288,7 @@ LL ~ | error: this loop never actually loops - --> tests/ui/never_loop.rs:492:5 + --> tests/ui/never_loop.rs:489:5 | LL | / for v in 0..10 { LL | | @@ -311,7 +311,7 @@ LL ~ | error: this loop never actually loops - --> tests/ui/never_loop.rs:503:5 + --> tests/ui/never_loop.rs:500:5 | LL | / 'bar: for _ in 0..100 { LL | | @@ -321,7 +321,7 @@ LL | | } | |_____^ | help: this code is unreachable. Consider moving the reachable parts out - --> tests/ui/never_loop.rs:505:9 + --> tests/ui/never_loop.rs:502:9 | LL | / loop { LL | | @@ -336,7 +336,7 @@ LL + if let Some(_) = (0..100).next() { | error: this loop never actually loops - --> tests/ui/never_loop.rs:505:9 + --> tests/ui/never_loop.rs:502:9 | LL | / loop { LL | | @@ -346,7 +346,7 @@ LL | | } | |_________^ error: this loop never actually loops - --> tests/ui/never_loop.rs:512:5 + --> tests/ui/never_loop.rs:509:5 | LL | / 'foo: for _ in 0..100 { LL | | @@ -356,7 +356,7 @@ LL | | } | |_____^ | help: this code is unreachable. Consider moving the reachable parts out - --> tests/ui/never_loop.rs:514:9 + --> tests/ui/never_loop.rs:511:9 | LL | / loop { LL | | @@ -372,7 +372,7 @@ LL + if let Some(_) = (0..100).next() { | error: this loop never actually loops - --> tests/ui/never_loop.rs:514:9 + --> tests/ui/never_loop.rs:511:9 | LL | / loop { LL | | @@ -383,7 +383,7 @@ LL | | } | |_________^ error: this loop never actually loops - --> tests/ui/never_loop.rs:517:13 + --> tests/ui/never_loop.rs:514:13 | LL | / loop { LL | | @@ -392,5 +392,14 @@ LL | | break 'foo; LL | | } | |_____________^ -error: aborting due to 29 previous errors +error: this loop never actually loops + --> tests/ui/never_loop.rs:537:5 + | +LL | / loop { +... | +LL | | return; +LL | | } + | |_____^ + +error: aborting due to 30 previous errors