From 3bba7c4adcf95cb9003b5050f0e2735e5b482286 Mon Sep 17 00:00:00 2001 From: shulaoda <165626830+shulaoda@users.noreply.github.com> Date: Sat, 11 Jul 2026 21:20:16 +0800 Subject: [PATCH] Respect the configured MSRV in `implicit_saturating_sub`'s `if x != 0 { x -= 1 }` rewrite --- book/src/lint_configuration.md | 1 + clippy_config/src/conf.rs | 1 + clippy_lints/src/implicit_saturating_sub.rs | 7 +++++- tests/ui/implicit_saturating_sub.fixed | 20 ++++++++++++++++ tests/ui/implicit_saturating_sub.rs | 26 +++++++++++++++++++++ tests/ui/implicit_saturating_sub.stderr | 20 +++++++++++++++- 6 files changed, 73 insertions(+), 2 deletions(-) diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index fb4df6234dac..c713dffcba8b 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -941,6 +941,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio * [`filter_map_next`](https://rust-lang.github.io/rust-clippy/master/index.html#filter_map_next) * [`from_over_into`](https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into) * [`if_then_some_else_none`](https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none) +* [`implicit_saturating_sub`](https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_sub) * [`index_refutable_slice`](https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice) * [`inefficient_to_string`](https://rust-lang.github.io/rust-clippy/master/index.html#inefficient_to_string) * [`io_other_error`](https://rust-lang.github.io/rust-clippy/master/index.html#io_other_error) diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 8a2795defc08..c00bb6890df9 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -796,6 +796,7 @@ define_Conf! { filter_map_next, from_over_into, if_then_some_else_none, + implicit_saturating_sub, index_refutable_slice, inefficient_to_string, io_other_error, diff --git a/clippy_lints/src/implicit_saturating_sub.rs b/clippy_lints/src/implicit_saturating_sub.rs index 4093ff9f4b2d..960b187de0b5 100644 --- a/clippy_lints/src/implicit_saturating_sub.rs +++ b/clippy_lints/src/implicit_saturating_sub.rs @@ -104,7 +104,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub { // Check if the conditional expression is a binary operation && let ExprKind::Binary(ref cond_op, cond_left, cond_right) = cond.kind { - check_with_condition(cx, expr, cond_op.node, cond_left, cond_right, then); + check_with_condition(cx, expr, cond_op.node, cond_left, cond_right, then, self.msrv); } else if let Some(higher::If { cond, then: if_block, @@ -309,6 +309,7 @@ fn check_with_condition<'tcx>( cond_left: &Expr<'tcx>, cond_right: &Expr<'tcx>, then: &Expr<'tcx>, + msrv: Msrv, ) { // Ensure that the binary operator is >, !=, or < if (BinOpKind::Ne == cond_op || BinOpKind::Gt == cond_op || BinOpKind::Lt == cond_op) @@ -342,6 +343,10 @@ fn check_with_condition<'tcx>( return; } + if is_in_const_context(cx) && !msrv.meets(cx, msrvs::SATURATING_SUB_CONST) { + return; + } + // Get the variable name let var_name = ares_path.segments[0].ident.name; match cond_num_val.kind { diff --git a/tests/ui/implicit_saturating_sub.fixed b/tests/ui/implicit_saturating_sub.fixed index c7ab65cbb015..7756541224a3 100644 --- a/tests/ui/implicit_saturating_sub.fixed +++ b/tests/ui/implicit_saturating_sub.fixed @@ -278,3 +278,23 @@ fn wrongly_unmangled_macros() { let b = 20u64; let _ = test_big!(a).saturating_sub(test_little!(b)); } + +#[clippy::msrv = "1.46.0"] +pub const fn const_msrv_too_low(mut x: u32) -> u32 { + if x != 0 { + x -= 1; + } + x +} + +#[clippy::msrv = "1.47.0"] +pub const fn const_msrv_ok(mut x: u32) -> u32 { + x = x.saturating_sub(1); + x +} + +#[clippy::msrv = "1.46.0"] +pub fn nonconst_msrv_low(mut x: u32) -> u32 { + x = x.saturating_sub(1); + x +} diff --git a/tests/ui/implicit_saturating_sub.rs b/tests/ui/implicit_saturating_sub.rs index 1371928e15b5..72f6e719ae1c 100644 --- a/tests/ui/implicit_saturating_sub.rs +++ b/tests/ui/implicit_saturating_sub.rs @@ -357,3 +357,29 @@ fn wrongly_unmangled_macros() { 0 }; } + +#[clippy::msrv = "1.46.0"] +pub const fn const_msrv_too_low(mut x: u32) -> u32 { + if x != 0 { + x -= 1; + } + x +} + +#[clippy::msrv = "1.47.0"] +pub const fn const_msrv_ok(mut x: u32) -> u32 { + if x != 0 { + //~^ implicit_saturating_sub + x -= 1; + } + x +} + +#[clippy::msrv = "1.46.0"] +pub fn nonconst_msrv_low(mut x: u32) -> u32 { + if x != 0 { + //~^ implicit_saturating_sub + x -= 1; + } + x +} diff --git a/tests/ui/implicit_saturating_sub.stderr b/tests/ui/implicit_saturating_sub.stderr index c7a4330603a2..bb099e6288a6 100644 --- a/tests/ui/implicit_saturating_sub.stderr +++ b/tests/ui/implicit_saturating_sub.stderr @@ -256,5 +256,23 @@ LL | | 0 LL | | }; | |_____^ help: replace it with: `test_big!(a).saturating_sub(test_little!(b))` -error: aborting due to 29 previous errors +error: implicitly performing saturating subtraction + --> tests/ui/implicit_saturating_sub.rs:371:5 + | +LL | / if x != 0 { +LL | | +LL | | x -= 1; +LL | | } + | |_____^ help: try: `x = x.saturating_sub(1);` + +error: implicitly performing saturating subtraction + --> tests/ui/implicit_saturating_sub.rs:380:5 + | +LL | / if x != 0 { +LL | | +LL | | x -= 1; +LL | | } + | |_____^ help: try: `x = x.saturating_sub(1);` + +error: aborting due to 31 previous errors