From f55cb7e9e2ce4f14a2c2a3ce53363382cc820616 Mon Sep 17 00:00:00 2001 From: JafarTano <81587305+JaafarTanoukhi@users.noreply.github.com> Date: Tue, 19 May 2026 21:48:31 +0300 Subject: [PATCH 01/11] Added `identity_assign_op` lint --- CHANGELOG.md | 1 + clippy_lints/src/declared_lints.rs | 1 + .../src/operators/identity_assign_op.rs | 39 +++++++++++ clippy_lints/src/operators/mod.rs | 22 +++++++ tests/ui/identity_assign_op.fixed | 45 +++++++++++++ tests/ui/identity_assign_op.rs | 45 +++++++++++++ tests/ui/identity_assign_op.stderr | 65 +++++++++++++++++++ 7 files changed, 218 insertions(+) create mode 100644 clippy_lints/src/operators/identity_assign_op.rs create mode 100644 tests/ui/identity_assign_op.fixed create mode 100644 tests/ui/identity_assign_op.rs create mode 100644 tests/ui/identity_assign_op.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index 0764a52ea033..872061d6ebb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6969,6 +6969,7 @@ Released 2018-09-13 [`get_last_with_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_last_with_len [`get_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#get_unwrap [`host_endian_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#host_endian_bytes +[`identity_assign_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_assign_op [`identity_conversion`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_conversion [`identity_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#identity_op [`if_let_mutex`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_mutex diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 207ccdc4fcd4..900542327ed1 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -613,6 +613,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::operators::FLOAT_CMP_INFO, crate::operators::FLOAT_CMP_CONST_INFO, crate::operators::FLOAT_EQUALITY_WITHOUT_ABS_INFO, + crate::operators::IDENTITY_ASSIGN_OP_INFO, crate::operators::IDENTITY_OP_INFO, crate::operators::IMPOSSIBLE_COMPARISONS_INFO, crate::operators::INEFFECTIVE_BIT_MASK_INFO, diff --git a/clippy_lints/src/operators/identity_assign_op.rs b/clippy_lints/src/operators/identity_assign_op.rs new file mode 100644 index 000000000000..254625266e05 --- /dev/null +++ b/clippy_lints/src/operators/identity_assign_op.rs @@ -0,0 +1,39 @@ +use clippy_utils::consts::{ConstEvalCtxt, Constant}; +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr}; +use rustc_lint::LateContext; + +use super::IDENTITY_ASSIGN_OP; + +// TODO: Adjust the parameters as necessary +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, + op: BinOpKind, + left: &'tcx Expr<'_>, + right: &'tcx Expr<'_>, +) { + if match op { + BinOpKind::Add | BinOpKind::Sub | BinOpKind::BitOr | BinOpKind::BitXor | BinOpKind::Shl | BinOpKind::Shr => { + matches!(ConstEvalCtxt::new(cx).eval(right), Some(Constant::Int(0))) + }, + + BinOpKind::Mul | BinOpKind::Div => { + matches!(ConstEvalCtxt::new(cx).eval(right), Some(Constant::Int(1))) + }, + + _ => false, + } { + span_lint_and_sugg( + cx, + IDENTITY_ASSIGN_OP, + expr.span, + "this assignment operation has no effect", + "consider replacing it with", + snippet(cx, left.span, "..").to_string(), + Applicability::MachineApplicable, + ); + } +} diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index 120716edc203..8ac7e21e6053 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -10,6 +10,7 @@ mod eq_op; mod erasing_op; mod float_cmp; mod float_equality_without_abs; +mod identity_assign_op; mod identity_op; mod integer_division; mod integer_division_remainder_used; @@ -518,6 +519,25 @@ declare_clippy_lint! { "float equality check without `.abs()`" } +declare_clippy_lint! { + /// ### What it does + /// + /// ### Why is this bad? + /// + /// ### Example + /// ```no_run + /// // example code where clippy issues a warning + /// ``` + /// Use instead: + /// ```no_run + /// // example code which does not raise clippy warning + /// ``` + #[clippy::version = "1.97.0"] + pub IDENTITY_ASSIGN_OP, + pedantic, + "default lint description" +} + declare_clippy_lint! { /// ### What it does /// Checks for identity operations, e.g., `x + 0`. @@ -1007,6 +1027,7 @@ impl_lint_pass!(Operators => [ FLOAT_CMP, FLOAT_CMP_CONST, FLOAT_EQUALITY_WITHOUT_ABS, + IDENTITY_ASSIGN_OP, IDENTITY_OP, IMPOSSIBLE_COMPARISONS, INEFFECTIVE_BIT_MASK, @@ -1095,6 +1116,7 @@ impl<'tcx> LateLintPass<'tcx> for Operators { self.arithmetic_context.check_binary(cx, e, bin_op, lhs, rhs); misrefactored_assign_op::check(cx, e, bin_op, lhs, rhs); modulo_arithmetic::check(cx, e, bin_op, lhs, rhs, false); + identity_assign_op::check(cx, e, bin_op, lhs, rhs); }, ExprKind::Assign(lhs, rhs, _) => { assign_op_pattern::check(cx, e, lhs, rhs, self.msrv); diff --git a/tests/ui/identity_assign_op.fixed b/tests/ui/identity_assign_op.fixed new file mode 100644 index 000000000000..4389f7f8dfa7 --- /dev/null +++ b/tests/ui/identity_assign_op.fixed @@ -0,0 +1,45 @@ +#![warn(clippy::identity_assign_op)] +#![allow(unused)] + +const ONE: i64 = 1; +const ZERO: i64 = 0; + +#[rustfmt::skip] +fn main() { + let mut x = 1i64; + + x; + //~^ identity_assign_op + + x; + //~^ identity_assign_op + + x; + //~^ identity_assign_op + + x; + //~^ identity_assign_op + + x; + //~^ identity_assign_op + + x; + //~^ identity_assign_op + + x; + //~^ identity_assign_op + + x; + //~^ identity_assign_op + + x; + //~^ identity_assign_op + + x; + //~^ identity_assign_op + + x += 1; // no error + x *= 2; // no error + x -= 1; // no error + x <<= 1; // no error +} diff --git a/tests/ui/identity_assign_op.rs b/tests/ui/identity_assign_op.rs new file mode 100644 index 000000000000..387979034a95 --- /dev/null +++ b/tests/ui/identity_assign_op.rs @@ -0,0 +1,45 @@ +#![warn(clippy::identity_assign_op)] +#![allow(unused)] + +const ONE: i64 = 1; +const ZERO: i64 = 0; + +#[rustfmt::skip] +fn main() { + let mut x = 1i64; + + x += 0; + //~^ identity_assign_op + + x -= 0; + //~^ identity_assign_op + + x |= 0; + //~^ identity_assign_op + + x ^= 0; + //~^ identity_assign_op + + x <<= 0; + //~^ identity_assign_op + + x >>= 0; + //~^ identity_assign_op + + x *= 1; + //~^ identity_assign_op + + x /= 1; + //~^ identity_assign_op + + x += ZERO; + //~^ identity_assign_op + + x *= ONE; + //~^ identity_assign_op + + x += 1; // no error + x *= 2; // no error + x -= 1; // no error + x <<= 1; // no error +} diff --git a/tests/ui/identity_assign_op.stderr b/tests/ui/identity_assign_op.stderr new file mode 100644 index 000000000000..56f4bbb15723 --- /dev/null +++ b/tests/ui/identity_assign_op.stderr @@ -0,0 +1,65 @@ +error: this assignment operation has no effect + --> tests/ui/identity_assign_op.rs:11:5 + | +LL | x += 0; + | ^^^^^^ help: consider replacing it with: `x` + | + = note: `-D clippy::identity-assign-op` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::identity_assign_op)]` + +error: this assignment operation has no effect + --> tests/ui/identity_assign_op.rs:14:5 + | +LL | x -= 0; + | ^^^^^^ help: consider replacing it with: `x` + +error: this assignment operation has no effect + --> tests/ui/identity_assign_op.rs:17:5 + | +LL | x |= 0; + | ^^^^^^ help: consider replacing it with: `x` + +error: this assignment operation has no effect + --> tests/ui/identity_assign_op.rs:20:5 + | +LL | x ^= 0; + | ^^^^^^ help: consider replacing it with: `x` + +error: this assignment operation has no effect + --> tests/ui/identity_assign_op.rs:23:5 + | +LL | x <<= 0; + | ^^^^^^^ help: consider replacing it with: `x` + +error: this assignment operation has no effect + --> tests/ui/identity_assign_op.rs:26:5 + | +LL | x >>= 0; + | ^^^^^^^ help: consider replacing it with: `x` + +error: this assignment operation has no effect + --> tests/ui/identity_assign_op.rs:29:5 + | +LL | x *= 1; + | ^^^^^^ help: consider replacing it with: `x` + +error: this assignment operation has no effect + --> tests/ui/identity_assign_op.rs:32:5 + | +LL | x /= 1; + | ^^^^^^ help: consider replacing it with: `x` + +error: this assignment operation has no effect + --> tests/ui/identity_assign_op.rs:35:5 + | +LL | x += ZERO; + | ^^^^^^^^^ help: consider replacing it with: `x` + +error: this assignment operation has no effect + --> tests/ui/identity_assign_op.rs:38:5 + | +LL | x *= ONE; + | ^^^^^^^^ help: consider replacing it with: `x` + +error: aborting due to 10 previous errors + From 37fc89851d007c0a6b86f9021e88fcbc62800738 Mon Sep 17 00:00:00 2001 From: JafarTano <81587305+JaafarTanoukhi@users.noreply.github.com> Date: Tue, 19 May 2026 21:58:43 +0300 Subject: [PATCH 02/11] fixed lint description --- clippy_lints/src/operators/mod.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index 8ac7e21e6053..130a99c81551 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -521,21 +521,25 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does + /// Checks for assignment operations with identity operands. /// /// ### Why is this bad? + /// These operations have no effect and can be removed for clarity. /// /// ### Example /// ```no_run - /// // example code where clippy issues a warning + /// let mut x = 1; + /// x += 0; /// ``` /// Use instead: /// ```no_run - /// // example code which does not raise clippy warning + /// let mut x = 1; + /// x; /// ``` #[clippy::version = "1.97.0"] pub IDENTITY_ASSIGN_OP, pedantic, - "default lint description" + "assignment operation with an identity operand" } declare_clippy_lint! { From 19e64f809cf944e43a6539ba5319c95005ce7ef3 Mon Sep 17 00:00:00 2001 From: JafarTano <81587305+JaafarTanoukhi@users.noreply.github.com> Date: Sat, 18 Jul 2026 19:51:52 +0300 Subject: [PATCH 03/11] remove unnecessary TODO comment and add newline at end of file --- clippy_lints/src/operators/identity_assign_op.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/operators/identity_assign_op.rs b/clippy_lints/src/operators/identity_assign_op.rs index 254625266e05..bf4baea6e7bf 100644 --- a/clippy_lints/src/operators/identity_assign_op.rs +++ b/clippy_lints/src/operators/identity_assign_op.rs @@ -7,7 +7,6 @@ use rustc_lint::LateContext; use super::IDENTITY_ASSIGN_OP; -// TODO: Adjust the parameters as necessary pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, @@ -15,6 +14,7 @@ pub(super) fn check<'tcx>( left: &'tcx Expr<'_>, right: &'tcx Expr<'_>, ) { + if match op { BinOpKind::Add | BinOpKind::Sub | BinOpKind::BitOr | BinOpKind::BitXor | BinOpKind::Shl | BinOpKind::Shr => { matches!(ConstEvalCtxt::new(cx).eval(right), Some(Constant::Int(0))) @@ -36,4 +36,4 @@ pub(super) fn check<'tcx>( Applicability::MachineApplicable, ); } -} +} \ No newline at end of file From 1cf608189ca79d197bd43379458f701a81631db8 Mon Sep 17 00:00:00 2001 From: JafarTano <81587305+JaafarTanoukhi@users.noreply.github.com> Date: Thu, 23 Jul 2026 13:53:49 +0300 Subject: [PATCH 04/11] initial fixes --- .../src/operators/identity_assign_op.rs | 19 +++++---- clippy_lints/src/operators/mod.rs | 16 +++++++- tests/ui/identity_assign_op.fixed | 20 +++++----- tests/ui/identity_assign_op.stderr | 40 +++++++++---------- 4 files changed, 53 insertions(+), 42 deletions(-) diff --git a/clippy_lints/src/operators/identity_assign_op.rs b/clippy_lints/src/operators/identity_assign_op.rs index bf4baea6e7bf..406701341688 100644 --- a/clippy_lints/src/operators/identity_assign_op.rs +++ b/clippy_lints/src/operators/identity_assign_op.rs @@ -1,20 +1,19 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::snippet; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr}; +use rustc_hir::{BinOpKind, Expr, Stmt}; use rustc_lint::LateContext; use super::IDENTITY_ASSIGN_OP; pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, - expr: &'tcx Expr<'_>, + stmt: &'tcx Stmt<'_>, + _expr: &'tcx Expr<'_>, op: BinOpKind, - left: &'tcx Expr<'_>, + _left: &'tcx Expr<'_>, right: &'tcx Expr<'_>, ) { - if match op { BinOpKind::Add | BinOpKind::Sub | BinOpKind::BitOr | BinOpKind::BitXor | BinOpKind::Shl | BinOpKind::Shr => { matches!(ConstEvalCtxt::new(cx).eval(right), Some(Constant::Int(0))) @@ -29,11 +28,11 @@ pub(super) fn check<'tcx>( span_lint_and_sugg( cx, IDENTITY_ASSIGN_OP, - expr.span, - "this assignment operation has no effect", - "consider replacing it with", - snippet(cx, left.span, "..").to_string(), + stmt.span, + "this operation has no effect", + "remove it", + String::new(), Applicability::MachineApplicable, ); } -} \ No newline at end of file +} diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index 130a99c81551..c05a77d2c36d 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -32,7 +32,7 @@ pub(crate) mod arithmetic_side_effects; use clippy_config::Conf; use clippy_utils::msrvs::Msrv; -use rustc_hir::{Body, Expr, ExprKind, UnOp}; +use rustc_hir::{Body, Expr, ExprKind, Stmt, StmtKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; @@ -1120,7 +1120,6 @@ impl<'tcx> LateLintPass<'tcx> for Operators { self.arithmetic_context.check_binary(cx, e, bin_op, lhs, rhs); misrefactored_assign_op::check(cx, e, bin_op, lhs, rhs); modulo_arithmetic::check(cx, e, bin_op, lhs, rhs, false); - identity_assign_op::check(cx, e, bin_op, lhs, rhs); }, ExprKind::Assign(lhs, rhs, _) => { assign_op_pattern::check(cx, e, lhs, rhs, self.msrv); @@ -1137,6 +1136,19 @@ impl<'tcx> LateLintPass<'tcx> for Operators { } } + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { + match stmt.kind { + StmtKind::Semi(e) => match e.kind { + ExprKind::AssignOp(op, lhs, rhs) => { + let bin_op = op.node.into(); + identity_assign_op::check(cx, stmt, e, bin_op, lhs, rhs); + }, + _ => (), + }, + _ => (), + } + } + fn check_expr_post(&mut self, _: &LateContext<'_>, e: &Expr<'_>) { self.arithmetic_context.expr_post(e.hir_id); } diff --git a/tests/ui/identity_assign_op.fixed b/tests/ui/identity_assign_op.fixed index 4389f7f8dfa7..203d2ced64b8 100644 --- a/tests/ui/identity_assign_op.fixed +++ b/tests/ui/identity_assign_op.fixed @@ -8,34 +8,34 @@ const ZERO: i64 = 0; fn main() { let mut x = 1i64; - x; + //~^ identity_assign_op - x; + //~^ identity_assign_op - x; + //~^ identity_assign_op - x; + //~^ identity_assign_op - x; + //~^ identity_assign_op - x; + //~^ identity_assign_op - x; + //~^ identity_assign_op - x; + //~^ identity_assign_op - x; + //~^ identity_assign_op - x; + //~^ identity_assign_op x += 1; // no error diff --git a/tests/ui/identity_assign_op.stderr b/tests/ui/identity_assign_op.stderr index 56f4bbb15723..c664898b61ef 100644 --- a/tests/ui/identity_assign_op.stderr +++ b/tests/ui/identity_assign_op.stderr @@ -1,65 +1,65 @@ -error: this assignment operation has no effect +error: this operation has no effect --> tests/ui/identity_assign_op.rs:11:5 | LL | x += 0; - | ^^^^^^ help: consider replacing it with: `x` + | ^^^^^^^ help: remove it | = note: `-D clippy::identity-assign-op` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::identity_assign_op)]` -error: this assignment operation has no effect +error: this operation has no effect --> tests/ui/identity_assign_op.rs:14:5 | LL | x -= 0; - | ^^^^^^ help: consider replacing it with: `x` + | ^^^^^^^ help: remove it -error: this assignment operation has no effect +error: this operation has no effect --> tests/ui/identity_assign_op.rs:17:5 | LL | x |= 0; - | ^^^^^^ help: consider replacing it with: `x` + | ^^^^^^^ help: remove it -error: this assignment operation has no effect +error: this operation has no effect --> tests/ui/identity_assign_op.rs:20:5 | LL | x ^= 0; - | ^^^^^^ help: consider replacing it with: `x` + | ^^^^^^^ help: remove it -error: this assignment operation has no effect +error: this operation has no effect --> tests/ui/identity_assign_op.rs:23:5 | LL | x <<= 0; - | ^^^^^^^ help: consider replacing it with: `x` + | ^^^^^^^^ help: remove it -error: this assignment operation has no effect +error: this operation has no effect --> tests/ui/identity_assign_op.rs:26:5 | LL | x >>= 0; - | ^^^^^^^ help: consider replacing it with: `x` + | ^^^^^^^^ help: remove it -error: this assignment operation has no effect +error: this operation has no effect --> tests/ui/identity_assign_op.rs:29:5 | LL | x *= 1; - | ^^^^^^ help: consider replacing it with: `x` + | ^^^^^^^ help: remove it -error: this assignment operation has no effect +error: this operation has no effect --> tests/ui/identity_assign_op.rs:32:5 | LL | x /= 1; - | ^^^^^^ help: consider replacing it with: `x` + | ^^^^^^^ help: remove it -error: this assignment operation has no effect +error: this operation has no effect --> tests/ui/identity_assign_op.rs:35:5 | LL | x += ZERO; - | ^^^^^^^^^ help: consider replacing it with: `x` + | ^^^^^^^^^^ help: remove it -error: this assignment operation has no effect +error: this operation has no effect --> tests/ui/identity_assign_op.rs:38:5 | LL | x *= ONE; - | ^^^^^^^^ help: consider replacing it with: `x` + | ^^^^^^^^^ help: remove it error: aborting due to 10 previous errors From 9771085687aeadeed3f2b3715533fdccdd0d77e1 Mon Sep 17 00:00:00 2001 From: JafarTano <81587305+JaafarTanoukhi@users.noreply.github.com> Date: Thu, 23 Jul 2026 14:08:45 +0300 Subject: [PATCH 05/11] supported other than integer types --- .../src/operators/identity_assign_op.rs | 26 ++++- tests/ui/identity_assign_op.fixed | 48 ++++++++++ tests/ui/identity_assign_op.rs | 48 ++++++++++ tests/ui/identity_assign_op.stderr | 94 ++++++++++++++++--- 4 files changed, 201 insertions(+), 15 deletions(-) diff --git a/clippy_lints/src/operators/identity_assign_op.rs b/clippy_lints/src/operators/identity_assign_op.rs index 406701341688..4fdc030c5078 100644 --- a/clippy_lints/src/operators/identity_assign_op.rs +++ b/clippy_lints/src/operators/identity_assign_op.rs @@ -16,12 +16,10 @@ pub(super) fn check<'tcx>( ) { if match op { BinOpKind::Add | BinOpKind::Sub | BinOpKind::BitOr | BinOpKind::BitXor | BinOpKind::Shl | BinOpKind::Shr => { - matches!(ConstEvalCtxt::new(cx).eval(right), Some(Constant::Int(0))) + is_zero_or_one(cx, right, 0) }, - BinOpKind::Mul | BinOpKind::Div => { - matches!(ConstEvalCtxt::new(cx).eval(right), Some(Constant::Int(1))) - }, + BinOpKind::Mul | BinOpKind::Div => is_zero_or_one(cx, right, 1), _ => false, } { @@ -36,3 +34,23 @@ pub(super) fn check<'tcx>( ); } } + +fn is_zero_or_one(cx: &LateContext<'_>, expr: &Expr<'_>, expected: u128) -> bool { + const F16_ZERO: u16 = 0.0_f16.to_bits(); + const F16_ONE: u16 = 1.0_f16.to_bits(); + const F128_ZERO: u128 = 0.0_f128.to_bits(); + const F128_ONE: u128 = 1.0_f128.to_bits(); + + let Some(value) = ConstEvalCtxt::new(cx).eval(expr).map(Constant::peel_refs) else { + return false; + }; + + match value { + Constant::Int(value) => value == expected, + Constant::F16(value) => value == if expected == 0 { F16_ZERO } else { F16_ONE }, + Constant::F32(value) => value == if expected == 0 { 0.0 } else { 1.0 }, + Constant::F64(value) => value == if expected == 0 { 0.0 } else { 1.0 }, + Constant::F128(value) => value == if expected == 0 { F128_ZERO } else { F128_ONE }, + _ => false, + } +} diff --git a/tests/ui/identity_assign_op.fixed b/tests/ui/identity_assign_op.fixed index 203d2ced64b8..224704a6accb 100644 --- a/tests/ui/identity_assign_op.fixed +++ b/tests/ui/identity_assign_op.fixed @@ -3,6 +3,12 @@ const ONE: i64 = 1; const ZERO: i64 = 0; +const ONE_U64: u64 = 1; +const ZERO_U64: u64 = 0; +const ONE_F32: f32 = 1.0; +const ZERO_F32: f32 = 0.0; +const ONE_F64: f64 = 1.0; +const ZERO_F64: f64 = 0.0; #[rustfmt::skip] fn main() { @@ -36,6 +42,48 @@ fn main() { //~^ identity_assign_op + //~^ identity_assign_op + + let mut y = 1u64; + + + //~^ identity_assign_op + + + //~^ identity_assign_op + + + //~^ identity_assign_op + + + //~^ identity_assign_op + + let mut z = 1.0f32; + + + //~^ identity_assign_op + + + //~^ identity_assign_op + + + //~^ identity_assign_op + + + //~^ identity_assign_op + + let mut w = 1.0f64; + + + //~^ identity_assign_op + + + //~^ identity_assign_op + + + //~^ identity_assign_op + + //~^ identity_assign_op x += 1; // no error diff --git a/tests/ui/identity_assign_op.rs b/tests/ui/identity_assign_op.rs index 387979034a95..cefb5930e7e3 100644 --- a/tests/ui/identity_assign_op.rs +++ b/tests/ui/identity_assign_op.rs @@ -3,6 +3,12 @@ const ONE: i64 = 1; const ZERO: i64 = 0; +const ONE_U64: u64 = 1; +const ZERO_U64: u64 = 0; +const ONE_F32: f32 = 1.0; +const ZERO_F32: f32 = 0.0; +const ONE_F64: f64 = 1.0; +const ZERO_F64: f64 = 0.0; #[rustfmt::skip] fn main() { @@ -38,6 +44,48 @@ fn main() { x *= ONE; //~^ identity_assign_op + let mut y = 1u64; + + y += 0u64; + //~^ identity_assign_op + + y *= 1u64; + //~^ identity_assign_op + + y += ZERO_U64; + //~^ identity_assign_op + + y *= ONE_U64; + //~^ identity_assign_op + + let mut z = 1.0f32; + + z += 0.0f32; + //~^ identity_assign_op + + z *= 1.0f32; + //~^ identity_assign_op + + z += ZERO_F32; + //~^ identity_assign_op + + z *= ONE_F32; + //~^ identity_assign_op + + let mut w = 1.0f64; + + w += 0.0; + //~^ identity_assign_op + + w *= 1.0; + //~^ identity_assign_op + + w += ZERO_F64; + //~^ identity_assign_op + + w *= ONE_F64; + //~^ identity_assign_op + x += 1; // no error x *= 2; // no error x -= 1; // no error diff --git a/tests/ui/identity_assign_op.stderr b/tests/ui/identity_assign_op.stderr index c664898b61ef..2f7a6f2297b6 100644 --- a/tests/ui/identity_assign_op.stderr +++ b/tests/ui/identity_assign_op.stderr @@ -1,5 +1,5 @@ error: this operation has no effect - --> tests/ui/identity_assign_op.rs:11:5 + --> tests/ui/identity_assign_op.rs:17:5 | LL | x += 0; | ^^^^^^^ help: remove it @@ -8,58 +8,130 @@ LL | x += 0; = help: to override `-D warnings` add `#[allow(clippy::identity_assign_op)]` error: this operation has no effect - --> tests/ui/identity_assign_op.rs:14:5 + --> tests/ui/identity_assign_op.rs:20:5 | LL | x -= 0; | ^^^^^^^ help: remove it error: this operation has no effect - --> tests/ui/identity_assign_op.rs:17:5 + --> tests/ui/identity_assign_op.rs:23:5 | LL | x |= 0; | ^^^^^^^ help: remove it error: this operation has no effect - --> tests/ui/identity_assign_op.rs:20:5 + --> tests/ui/identity_assign_op.rs:26:5 | LL | x ^= 0; | ^^^^^^^ help: remove it error: this operation has no effect - --> tests/ui/identity_assign_op.rs:23:5 + --> tests/ui/identity_assign_op.rs:29:5 | LL | x <<= 0; | ^^^^^^^^ help: remove it error: this operation has no effect - --> tests/ui/identity_assign_op.rs:26:5 + --> tests/ui/identity_assign_op.rs:32:5 | LL | x >>= 0; | ^^^^^^^^ help: remove it error: this operation has no effect - --> tests/ui/identity_assign_op.rs:29:5 + --> tests/ui/identity_assign_op.rs:35:5 | LL | x *= 1; | ^^^^^^^ help: remove it error: this operation has no effect - --> tests/ui/identity_assign_op.rs:32:5 + --> tests/ui/identity_assign_op.rs:38:5 | LL | x /= 1; | ^^^^^^^ help: remove it error: this operation has no effect - --> tests/ui/identity_assign_op.rs:35:5 + --> tests/ui/identity_assign_op.rs:41:5 | LL | x += ZERO; | ^^^^^^^^^^ help: remove it error: this operation has no effect - --> tests/ui/identity_assign_op.rs:38:5 + --> tests/ui/identity_assign_op.rs:44:5 | LL | x *= ONE; | ^^^^^^^^^ help: remove it -error: aborting due to 10 previous errors +error: this operation has no effect + --> tests/ui/identity_assign_op.rs:49:5 + | +LL | y += 0u64; + | ^^^^^^^^^^ help: remove it + +error: this operation has no effect + --> tests/ui/identity_assign_op.rs:52:5 + | +LL | y *= 1u64; + | ^^^^^^^^^^ help: remove it + +error: this operation has no effect + --> tests/ui/identity_assign_op.rs:55:5 + | +LL | y += ZERO_U64; + | ^^^^^^^^^^^^^^ help: remove it + +error: this operation has no effect + --> tests/ui/identity_assign_op.rs:58:5 + | +LL | y *= ONE_U64; + | ^^^^^^^^^^^^^ help: remove it + +error: this operation has no effect + --> tests/ui/identity_assign_op.rs:63:5 + | +LL | z += 0.0f32; + | ^^^^^^^^^^^^ help: remove it + +error: this operation has no effect + --> tests/ui/identity_assign_op.rs:66:5 + | +LL | z *= 1.0f32; + | ^^^^^^^^^^^^ help: remove it + +error: this operation has no effect + --> tests/ui/identity_assign_op.rs:69:5 + | +LL | z += ZERO_F32; + | ^^^^^^^^^^^^^^ help: remove it + +error: this operation has no effect + --> tests/ui/identity_assign_op.rs:72:5 + | +LL | z *= ONE_F32; + | ^^^^^^^^^^^^^ help: remove it + +error: this operation has no effect + --> tests/ui/identity_assign_op.rs:77:5 + | +LL | w += 0.0; + | ^^^^^^^^^ help: remove it + +error: this operation has no effect + --> tests/ui/identity_assign_op.rs:80:5 + | +LL | w *= 1.0; + | ^^^^^^^^^ help: remove it + +error: this operation has no effect + --> tests/ui/identity_assign_op.rs:83:5 + | +LL | w += ZERO_F64; + | ^^^^^^^^^^^^^^ help: remove it + +error: this operation has no effect + --> tests/ui/identity_assign_op.rs:86:5 + | +LL | w *= ONE_F64; + | ^^^^^^^^^^^^^ help: remove it + +error: aborting due to 22 previous errors From 87fe20ce1ff478cd0a5d0fac8f4238a120dde373 Mon Sep 17 00:00:00 2001 From: JafarTano <81587305+JaafarTanoukhi@users.noreply.github.com> Date: Sat, 25 Jul 2026 10:09:57 +0300 Subject: [PATCH 06/11] avoid linting identity operations in assignment series --- .../src/operators/identity_assign_op.rs | 39 ++++++++++++++++--- clippy_lints/src/operators/mod.rs | 2 +- tests/ui/identity_assign_op.fixed | 12 ++++++ tests/ui/identity_assign_op.rs | 12 ++++++ 4 files changed, 59 insertions(+), 6 deletions(-) diff --git a/clippy_lints/src/operators/identity_assign_op.rs b/clippy_lints/src/operators/identity_assign_op.rs index 4fdc030c5078..695606083532 100644 --- a/clippy_lints/src/operators/identity_assign_op.rs +++ b/clippy_lints/src/operators/identity_assign_op.rs @@ -1,7 +1,8 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::eq_expr_value; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Expr, Stmt}; +use rustc_hir::{BinOpKind, Expr, ExprKind, Node, Stmt, StmtKind}; use rustc_lint::LateContext; use super::IDENTITY_ASSIGN_OP; @@ -9,12 +10,11 @@ use super::IDENTITY_ASSIGN_OP; pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>, - _expr: &'tcx Expr<'_>, op: BinOpKind, - _left: &'tcx Expr<'_>, + left: &'tcx Expr<'_>, right: &'tcx Expr<'_>, ) { - if match op { + let is_identity = match op { BinOpKind::Add | BinOpKind::Sub | BinOpKind::BitOr | BinOpKind::BitXor | BinOpKind::Shl | BinOpKind::Shr => { is_zero_or_one(cx, right, 0) }, @@ -22,7 +22,9 @@ pub(super) fn check<'tcx>( BinOpKind::Mul | BinOpKind::Div => is_zero_or_one(cx, right, 1), _ => false, - } { + }; + + if is_identity && !is_part_of_series(cx, stmt, op, left) { span_lint_and_sugg( cx, IDENTITY_ASSIGN_OP, @@ -35,6 +37,33 @@ pub(super) fn check<'tcx>( } } +fn is_part_of_series(cx: &LateContext<'_>, stmt: &Stmt<'_>, op: BinOpKind, left: &Expr<'_>) -> bool { + let Node::Block(block) = cx.tcx.parent_hir_node(stmt.hir_id) else { + return false; + }; + let Some(index) = block.stmts.iter().position(|other| other.hir_id == stmt.hir_id) else { + return false; + }; + + (index > 0 && is_matching_assign_op(cx, &block.stmts[index - 1], op, left)) + || block + .stmts + .get(index + 1) + .is_some_and(|stmt| is_matching_assign_op(cx, stmt, op, left)) +} + +fn is_matching_assign_op(cx: &LateContext<'_>, stmt: &Stmt<'_>, op: BinOpKind, left: &Expr<'_>) -> bool { + let StmtKind::Semi(expr) = stmt.kind else { + return false; + }; + let ExprKind::AssignOp(other_op, other_left, _) = expr.kind else { + return false; + }; + let other_op: BinOpKind = other_op.node.into(); + + other_op == op && eq_expr_value(cx, left.span.ctxt(), left, other_left) +} + fn is_zero_or_one(cx: &LateContext<'_>, expr: &Expr<'_>, expected: u128) -> bool { const F16_ZERO: u16 = 0.0_f16.to_bits(); const F16_ONE: u16 = 1.0_f16.to_bits(); diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index c05a77d2c36d..0f02cc4da4a7 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -1141,7 +1141,7 @@ impl<'tcx> LateLintPass<'tcx> for Operators { StmtKind::Semi(e) => match e.kind { ExprKind::AssignOp(op, lhs, rhs) => { let bin_op = op.node.into(); - identity_assign_op::check(cx, stmt, e, bin_op, lhs, rhs); + identity_assign_op::check(cx, stmt, bin_op, lhs, rhs); }, _ => (), }, diff --git a/tests/ui/identity_assign_op.fixed b/tests/ui/identity_assign_op.fixed index 224704a6accb..5ed45405430b 100644 --- a/tests/ui/identity_assign_op.fixed +++ b/tests/ui/identity_assign_op.fixed @@ -90,4 +90,16 @@ fn main() { x *= 2; // no error x -= 1; // no error x <<= 1; // no error + + let mut series = 1i64; + + series += 0; // no error: part of a series + series += 1; + series += 2; + + series <<= 0; // no error: part of a two-statement series + series <<= 1; + + series *= 2; + series *= 1; // no error: at the end of a series } diff --git a/tests/ui/identity_assign_op.rs b/tests/ui/identity_assign_op.rs index cefb5930e7e3..4286f29d1b56 100644 --- a/tests/ui/identity_assign_op.rs +++ b/tests/ui/identity_assign_op.rs @@ -90,4 +90,16 @@ fn main() { x *= 2; // no error x -= 1; // no error x <<= 1; // no error + + let mut series = 1i64; + + series += 0; // no error: part of a series + series += 1; + series += 2; + + series <<= 0; // no error: part of a two-statement series + series <<= 1; + + series *= 2; + series *= 1; // no error: at the end of a series } From 8fb834c08156693872859b802f8812c6dfd8a945 Mon Sep 17 00:00:00 2001 From: JafarTano <81587305+JaafarTanoukhi@users.noreply.github.com> Date: Sat, 25 Jul 2026 22:50:24 +0300 Subject: [PATCH 07/11] simplified --- clippy_lints/src/operators/identity_assign_op.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/operators/identity_assign_op.rs b/clippy_lints/src/operators/identity_assign_op.rs index 695606083532..2d6364181595 100644 --- a/clippy_lints/src/operators/identity_assign_op.rs +++ b/clippy_lints/src/operators/identity_assign_op.rs @@ -46,10 +46,8 @@ fn is_part_of_series(cx: &LateContext<'_>, stmt: &Stmt<'_>, op: BinOpKind, left: }; (index > 0 && is_matching_assign_op(cx, &block.stmts[index - 1], op, left)) - || block - .stmts - .get(index + 1) - .is_some_and(|stmt| is_matching_assign_op(cx, stmt, op, left)) + || + (index < block.stmts.len() - 1 && is_matching_assign_op(cx, &block.stmts[index + 1], op, left)) } fn is_matching_assign_op(cx: &LateContext<'_>, stmt: &Stmt<'_>, op: BinOpKind, left: &Expr<'_>) -> bool { From 8344edb4a6cb3c98676999fc2dbc1ae947ea68c7 Mon Sep 17 00:00:00 2001 From: JafarTano <81587305+JaafarTanoukhi@users.noreply.github.com> Date: Sat, 25 Jul 2026 23:22:04 +0300 Subject: [PATCH 08/11] handled user operators --- .../src/operators/identity_assign_op.rs | 8 ++++++-- clippy_lints/src/operators/mod.rs | 2 +- tests/ui/identity_assign_op.fixed | 18 ++++++++++++++++++ tests/ui/identity_assign_op.rs | 18 ++++++++++++++++++ 4 files changed, 43 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/operators/identity_assign_op.rs b/clippy_lints/src/operators/identity_assign_op.rs index 2d6364181595..4c5d48db72bc 100644 --- a/clippy_lints/src/operators/identity_assign_op.rs +++ b/clippy_lints/src/operators/identity_assign_op.rs @@ -10,10 +10,15 @@ use super::IDENTITY_ASSIGN_OP; pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>, + expr: &'tcx Expr<'_>, op: BinOpKind, left: &'tcx Expr<'_>, right: &'tcx Expr<'_>, ) { + if cx.typeck_results().type_dependent_def_id(expr.hir_id).is_some() { + return; + } + let is_identity = match op { BinOpKind::Add | BinOpKind::Sub | BinOpKind::BitOr | BinOpKind::BitXor | BinOpKind::Shl | BinOpKind::Shr => { is_zero_or_one(cx, right, 0) @@ -46,8 +51,7 @@ fn is_part_of_series(cx: &LateContext<'_>, stmt: &Stmt<'_>, op: BinOpKind, left: }; (index > 0 && is_matching_assign_op(cx, &block.stmts[index - 1], op, left)) - || - (index < block.stmts.len() - 1 && is_matching_assign_op(cx, &block.stmts[index + 1], op, left)) + || (index < block.stmts.len() - 1 && is_matching_assign_op(cx, &block.stmts[index + 1], op, left)) } fn is_matching_assign_op(cx: &LateContext<'_>, stmt: &Stmt<'_>, op: BinOpKind, left: &Expr<'_>) -> bool { diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index 0f02cc4da4a7..c05a77d2c36d 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -1141,7 +1141,7 @@ impl<'tcx> LateLintPass<'tcx> for Operators { StmtKind::Semi(e) => match e.kind { ExprKind::AssignOp(op, lhs, rhs) => { let bin_op = op.node.into(); - identity_assign_op::check(cx, stmt, bin_op, lhs, rhs); + identity_assign_op::check(cx, stmt, e, bin_op, lhs, rhs); }, _ => (), }, diff --git a/tests/ui/identity_assign_op.fixed b/tests/ui/identity_assign_op.fixed index 5ed45405430b..7aa54f61f2c4 100644 --- a/tests/ui/identity_assign_op.fixed +++ b/tests/ui/identity_assign_op.fixed @@ -102,4 +102,22 @@ fn main() { series *= 2; series *= 1; // no error: at the end of a series + + let mut custom = Custom(1); + custom += 0; // no error: user-defined operator + custom *= 1; // no error: user-defined operator +} + +struct Custom(i64); + +impl std::ops::AddAssign for Custom { + fn add_assign(&mut self, rhs: i64) { + self.0 += rhs + 1; + } +} + +impl std::ops::MulAssign for Custom { + fn mul_assign(&mut self, rhs: i64) { + self.0 *= rhs + 1; + } } diff --git a/tests/ui/identity_assign_op.rs b/tests/ui/identity_assign_op.rs index 4286f29d1b56..3fc576063db7 100644 --- a/tests/ui/identity_assign_op.rs +++ b/tests/ui/identity_assign_op.rs @@ -102,4 +102,22 @@ fn main() { series *= 2; series *= 1; // no error: at the end of a series + + let mut custom = Custom(1); + custom += 0; // no error: user-defined operator + custom *= 1; // no error: user-defined operator +} + +struct Custom(i64); + +impl std::ops::AddAssign for Custom { + fn add_assign(&mut self, rhs: i64) { + self.0 += rhs + 1; + } +} + +impl std::ops::MulAssign for Custom { + fn mul_assign(&mut self, rhs: i64) { + self.0 *= rhs + 1; + } } From d340d414d4b16f0ee2b5af7299cb889c9ce45195 Mon Sep 17 00:00:00 2001 From: JafarTano <81587305+JaafarTanoukhi@users.noreply.github.com> Date: Sat, 25 Jul 2026 23:27:07 +0300 Subject: [PATCH 09/11] test involving macro --- tests/ui/identity_assign_op.fixed | 14 ++++++++++++++ tests/ui/identity_assign_op.rs | 14 ++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/tests/ui/identity_assign_op.fixed b/tests/ui/identity_assign_op.fixed index 7aa54f61f2c4..9b0b5e67e494 100644 --- a/tests/ui/identity_assign_op.fixed +++ b/tests/ui/identity_assign_op.fixed @@ -106,6 +106,8 @@ fn main() { let mut custom = Custom(1); custom += 0; // no error: user-defined operator custom *= 1; // no error: user-defined operator + + custom -= 0; // no error: macro-generated user-defined operator } struct Custom(i64); @@ -121,3 +123,15 @@ impl std::ops::MulAssign for Custom { self.0 *= rhs + 1; } } + +macro_rules! impl_sub_assign { + () => { + impl std::ops::SubAssign for Custom { + fn sub_assign(&mut self, rhs: i64) { + self.0 -= rhs + 1; + } + } + }; +} + +impl_sub_assign!(); diff --git a/tests/ui/identity_assign_op.rs b/tests/ui/identity_assign_op.rs index 3fc576063db7..f52ecec36ff5 100644 --- a/tests/ui/identity_assign_op.rs +++ b/tests/ui/identity_assign_op.rs @@ -106,6 +106,8 @@ fn main() { let mut custom = Custom(1); custom += 0; // no error: user-defined operator custom *= 1; // no error: user-defined operator + + custom -= 0; // no error: macro-generated user-defined operator } struct Custom(i64); @@ -121,3 +123,15 @@ impl std::ops::MulAssign for Custom { self.0 *= rhs + 1; } } + +macro_rules! impl_sub_assign { + () => { + impl std::ops::SubAssign for Custom { + fn sub_assign(&mut self, rhs: i64) { + self.0 -= rhs + 1; + } + } + }; +} + +impl_sub_assign!(); From 00a086edb84c35eeaae729d76e48785deea678f0 Mon Sep 17 00:00:00 2001 From: JafarTano <81587305+JaafarTanoukhi@users.noreply.github.com> Date: Sun, 26 Jul 2026 00:10:37 +0300 Subject: [PATCH 10/11] fixd cicd errors --- .../src/operators/identity_assign_op.rs | 18 ++++++++++++++---- clippy_lints/src/operators/mod.rs | 17 +++++++---------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/operators/identity_assign_op.rs b/clippy_lints/src/operators/identity_assign_op.rs index 4c5d48db72bc..21bd01e691d6 100644 --- a/clippy_lints/src/operators/identity_assign_op.rs +++ b/clippy_lints/src/operators/identity_assign_op.rs @@ -69,19 +69,29 @@ fn is_matching_assign_op(cx: &LateContext<'_>, stmt: &Stmt<'_>, op: BinOpKind, l fn is_zero_or_one(cx: &LateContext<'_>, expr: &Expr<'_>, expected: u128) -> bool { const F16_ZERO: u16 = 0.0_f16.to_bits(); const F16_ONE: u16 = 1.0_f16.to_bits(); + const F32_ZERO: u32 = 0.0_f32.to_bits(); + const F32_ONE: u32 = 1.0_f32.to_bits(); + const F64_ZERO: u64 = 0.0_f64.to_bits(); + const F64_ONE: u64 = 1.0_f64.to_bits(); const F128_ZERO: u128 = 0.0_f128.to_bits(); const F128_ONE: u128 = 1.0_f128.to_bits(); + let (expected_f16, expected_f32, expected_f64, expected_f128) = match expected { + 0 => (F16_ZERO, F32_ZERO, F64_ZERO, F128_ZERO), + 1 => (F16_ONE, F32_ONE, F64_ONE, F128_ONE), + _ => return false, + }; + let Some(value) = ConstEvalCtxt::new(cx).eval(expr).map(Constant::peel_refs) else { return false; }; match value { Constant::Int(value) => value == expected, - Constant::F16(value) => value == if expected == 0 { F16_ZERO } else { F16_ONE }, - Constant::F32(value) => value == if expected == 0 { 0.0 } else { 1.0 }, - Constant::F64(value) => value == if expected == 0 { 0.0 } else { 1.0 }, - Constant::F128(value) => value == if expected == 0 { F128_ZERO } else { F128_ONE }, + Constant::F16(value) => value == expected_f16, + Constant::F32(value) => value.to_bits() == expected_f32, + Constant::F64(value) => value.to_bits() == expected_f64, + Constant::F128(value) => value == expected_f128, _ => false, } } diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs index c05a77d2c36d..9ac6e54a269a 100644 --- a/clippy_lints/src/operators/mod.rs +++ b/clippy_lints/src/operators/mod.rs @@ -1137,16 +1137,13 @@ impl<'tcx> LateLintPass<'tcx> for Operators { } fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { - match stmt.kind { - StmtKind::Semi(e) => match e.kind { - ExprKind::AssignOp(op, lhs, rhs) => { - let bin_op = op.node.into(); - identity_assign_op::check(cx, stmt, e, bin_op, lhs, rhs); - }, - _ => (), - }, - _ => (), - } + let StmtKind::Semi(e) = stmt.kind else { return }; + let ExprKind::AssignOp(op, lhs, rhs) = e.kind else { + return; + }; + + let bin_op = op.node.into(); + identity_assign_op::check(cx, stmt, e, bin_op, lhs, rhs); } fn check_expr_post(&mut self, _: &LateContext<'_>, e: &Expr<'_>) { From 77169055880f5355e25edff2d518c429b44072b7 Mon Sep 17 00:00:00 2001 From: JafarTano <81587305+JaafarTanoukhi@users.noreply.github.com> Date: Sun, 26 Jul 2026 03:40:01 +0300 Subject: [PATCH 11/11] 0.0 now is not an additive identity and handled -0.0, fixed tests --- .../src/operators/identity_assign_op.rs | 20 ++- tests/ui/identity_assign_op.fixed | 72 +++++++--- tests/ui/identity_assign_op.rs | 116 ++++++++++----- tests/ui/identity_assign_op.stderr | 136 ++++++++++-------- 4 files changed, 224 insertions(+), 120 deletions(-) diff --git a/clippy_lints/src/operators/identity_assign_op.rs b/clippy_lints/src/operators/identity_assign_op.rs index 21bd01e691d6..1875760a3c0b 100644 --- a/clippy_lints/src/operators/identity_assign_op.rs +++ b/clippy_lints/src/operators/identity_assign_op.rs @@ -20,11 +20,13 @@ pub(super) fn check<'tcx>( } let is_identity = match op { - BinOpKind::Add | BinOpKind::Sub | BinOpKind::BitOr | BinOpKind::BitXor | BinOpKind::Shl | BinOpKind::Shr => { - is_zero_or_one(cx, right, 0) + BinOpKind::Add => is_zero_or_one(cx, right, 0, true), + + BinOpKind::Sub | BinOpKind::BitOr | BinOpKind::BitXor | BinOpKind::Shl | BinOpKind::Shr => { + is_zero_or_one(cx, right, 0, false) }, - BinOpKind::Mul | BinOpKind::Div => is_zero_or_one(cx, right, 1), + BinOpKind::Mul | BinOpKind::Div => is_zero_or_one(cx, right, 1, false), _ => false, }; @@ -66,17 +68,27 @@ fn is_matching_assign_op(cx: &LateContext<'_>, stmt: &Stmt<'_>, op: BinOpKind, l other_op == op && eq_expr_value(cx, left.span.ctxt(), left, other_left) } -fn is_zero_or_one(cx: &LateContext<'_>, expr: &Expr<'_>, expected: u128) -> bool { +fn is_zero_or_one(cx: &LateContext<'_>, expr: &Expr<'_>, expected: u128, negative_float_zero: bool) -> bool { const F16_ZERO: u16 = 0.0_f16.to_bits(); + const F16_NEGATIVE_ZERO: u16 = (-0.0_f16).to_bits(); const F16_ONE: u16 = 1.0_f16.to_bits(); const F32_ZERO: u32 = 0.0_f32.to_bits(); + const F32_NEGATIVE_ZERO: u32 = (-0.0_f32).to_bits(); const F32_ONE: u32 = 1.0_f32.to_bits(); const F64_ZERO: u64 = 0.0_f64.to_bits(); + const F64_NEGATIVE_ZERO: u64 = (-0.0_f64).to_bits(); const F64_ONE: u64 = 1.0_f64.to_bits(); const F128_ZERO: u128 = 0.0_f128.to_bits(); + const F128_NEGATIVE_ZERO: u128 = (-0.0_f128).to_bits(); const F128_ONE: u128 = 1.0_f128.to_bits(); let (expected_f16, expected_f32, expected_f64, expected_f128) = match expected { + 0 if negative_float_zero => ( + F16_NEGATIVE_ZERO, + F32_NEGATIVE_ZERO, + F64_NEGATIVE_ZERO, + F128_NEGATIVE_ZERO, + ), 0 => (F16_ZERO, F32_ZERO, F64_ZERO, F128_ZERO), 1 => (F16_ONE, F32_ONE, F64_ONE, F128_ONE), _ => return false, diff --git a/tests/ui/identity_assign_op.fixed b/tests/ui/identity_assign_op.fixed index 9b0b5e67e494..2d05c9589076 100644 --- a/tests/ui/identity_assign_op.fixed +++ b/tests/ui/identity_assign_op.fixed @@ -1,18 +1,21 @@ #![warn(clippy::identity_assign_op)] #![allow(unused)] -const ONE: i64 = 1; -const ZERO: i64 = 0; -const ONE_U64: u64 = 1; +const ZERO_I64: i64 = 0; +const ONE_I64: i64 = 1; const ZERO_U64: u64 = 0; -const ONE_F32: f32 = 1.0; +const ONE_U64: u64 = 1; const ZERO_F32: f32 = 0.0; -const ONE_F64: f64 = 1.0; +const NEGATIVE_ZERO_F32: f32 = -0.0; +const ONE_F32: f32 = 1.0; const ZERO_F64: f64 = 0.0; +const NEGATIVE_ZERO_F64: f64 = -0.0; +const ONE_F64: f64 = 1.0; #[rustfmt::skip] -fn main() { - let mut x = 1i64; +fn test_identity_values() { + // Literals + let mut signed = 1_i64; //~^ identity_assign_op @@ -38,13 +41,17 @@ fn main() { //~^ identity_assign_op + let mut unsigned = 1_u64; + //~^ identity_assign_op //~^ identity_assign_op - let mut y = 1u64; + let mut float32 = 1.0_f32; + + float32 += 0.0; // no error //~^ identity_assign_op @@ -55,24 +62,42 @@ fn main() { //~^ identity_assign_op + let mut float64 = 1.0_f64; + + float64 += 0.0; // no error + //~^ identity_assign_op - let mut z = 1.0f32; + + //~^ identity_assign_op //~^ identity_assign_op + let mut subtraction = 1.0_f32; + subtraction -= -0.0; // no error + + // Constants + let mut signed = 1_i64; + //~^ identity_assign_op //~^ identity_assign_op + let mut unsigned = 1_u64; + //~^ identity_assign_op - let mut w = 1.0f64; + + //~^ identity_assign_op + + let mut float32 = 1.0_f32; + + float32 += ZERO_F32; // no error //~^ identity_assign_op @@ -80,18 +105,28 @@ fn main() { //~^ identity_assign_op + let mut float64 = 1.0_f64; + + float64 += ZERO_F64; // no error + //~^ identity_assign_op //~^ identity_assign_op +} - x += 1; // no error - x *= 2; // no error - x -= 1; // no error - x <<= 1; // no error +fn test_non_identity_values() { + let mut value = 1_i64; - let mut series = 1i64; + value += 1; + value *= 2; + value -= 1; + value <<= 1; +} + +fn test_series() { + let mut series = 1_i64; series += 0; // no error: part of a series series += 1; @@ -102,11 +137,16 @@ fn main() { series *= 2; series *= 1; // no error: at the end of a series +} +fn test_user_defined_operators() { let mut custom = Custom(1); custom += 0; // no error: user-defined operator custom *= 1; // no error: user-defined operator - +} + +fn test_macros() { + let mut custom = Custom(1); custom -= 0; // no error: macro-generated user-defined operator } diff --git a/tests/ui/identity_assign_op.rs b/tests/ui/identity_assign_op.rs index f52ecec36ff5..bb6bd1f269f8 100644 --- a/tests/ui/identity_assign_op.rs +++ b/tests/ui/identity_assign_op.rs @@ -1,97 +1,132 @@ #![warn(clippy::identity_assign_op)] #![allow(unused)] -const ONE: i64 = 1; -const ZERO: i64 = 0; -const ONE_U64: u64 = 1; +const ZERO_I64: i64 = 0; +const ONE_I64: i64 = 1; const ZERO_U64: u64 = 0; -const ONE_F32: f32 = 1.0; +const ONE_U64: u64 = 1; const ZERO_F32: f32 = 0.0; -const ONE_F64: f64 = 1.0; +const NEGATIVE_ZERO_F32: f32 = -0.0; +const ONE_F32: f32 = 1.0; const ZERO_F64: f64 = 0.0; +const NEGATIVE_ZERO_F64: f64 = -0.0; +const ONE_F64: f64 = 1.0; #[rustfmt::skip] -fn main() { - let mut x = 1i64; +fn test_identity_values() { + // Literals + let mut signed = 1_i64; - x += 0; + signed += 0; //~^ identity_assign_op - x -= 0; + signed -= 0; //~^ identity_assign_op - x |= 0; + signed |= 0; //~^ identity_assign_op - x ^= 0; + signed ^= 0; //~^ identity_assign_op - x <<= 0; + signed <<= 0; //~^ identity_assign_op - x >>= 0; + signed >>= 0; //~^ identity_assign_op - x *= 1; + signed *= 1; //~^ identity_assign_op - x /= 1; + signed /= 1; //~^ identity_assign_op - x += ZERO; + let mut unsigned = 1_u64; + + unsigned += 0; //~^ identity_assign_op - x *= ONE; + unsigned *= 1; //~^ identity_assign_op - let mut y = 1u64; + let mut float32 = 1.0_f32; - y += 0u64; + float32 += 0.0; // no error + + float32 *= 1.0; + //~^ identity_assign_op + + float32 += -0.0; //~^ identity_assign_op - y *= 1u64; + float32 -= 0.0; + //~^ identity_assign_op + + let mut float64 = 1.0_f64; + + float64 += 0.0; // no error + + float64 *= 1.0; //~^ identity_assign_op - y += ZERO_U64; + float64 += -0.0; //~^ identity_assign_op - y *= ONE_U64; + float64 -= 0.0; //~^ identity_assign_op - let mut z = 1.0f32; + let mut subtraction = 1.0_f32; + subtraction -= -0.0; // no error + + // Constants + let mut signed = 1_i64; - z += 0.0f32; + signed += ZERO_I64; //~^ identity_assign_op - z *= 1.0f32; + signed *= ONE_I64; //~^ identity_assign_op - z += ZERO_F32; + let mut unsigned = 1_u64; + + unsigned += ZERO_U64; //~^ identity_assign_op - z *= ONE_F32; + unsigned *= ONE_U64; //~^ identity_assign_op - let mut w = 1.0f64; + let mut float32 = 1.0_f32; + + float32 += ZERO_F32; // no error - w += 0.0; + float32 *= ONE_F32; //~^ identity_assign_op - w *= 1.0; + float32 += NEGATIVE_ZERO_F32; //~^ identity_assign_op - w += ZERO_F64; + let mut float64 = 1.0_f64; + + float64 += ZERO_F64; // no error + + float64 *= ONE_F64; //~^ identity_assign_op - w *= ONE_F64; + float64 += NEGATIVE_ZERO_F64; //~^ identity_assign_op +} - x += 1; // no error - x *= 2; // no error - x -= 1; // no error - x <<= 1; // no error +fn test_non_identity_values() { + let mut value = 1_i64; - let mut series = 1i64; + value += 1; + value *= 2; + value -= 1; + value <<= 1; +} + +fn test_series() { + let mut series = 1_i64; series += 0; // no error: part of a series series += 1; @@ -102,11 +137,16 @@ fn main() { series *= 2; series *= 1; // no error: at the end of a series +} +fn test_user_defined_operators() { let mut custom = Custom(1); custom += 0; // no error: user-defined operator custom *= 1; // no error: user-defined operator - +} + +fn test_macros() { + let mut custom = Custom(1); custom -= 0; // no error: macro-generated user-defined operator } diff --git a/tests/ui/identity_assign_op.stderr b/tests/ui/identity_assign_op.stderr index 2f7a6f2297b6..8dfc64c86433 100644 --- a/tests/ui/identity_assign_op.stderr +++ b/tests/ui/identity_assign_op.stderr @@ -1,137 +1,149 @@ error: this operation has no effect - --> tests/ui/identity_assign_op.rs:17:5 + --> tests/ui/identity_assign_op.rs:20:5 | -LL | x += 0; - | ^^^^^^^ help: remove it +LL | signed += 0; + | ^^^^^^^^^^^^ help: remove it | = note: `-D clippy::identity-assign-op` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::identity_assign_op)]` -error: this operation has no effect - --> tests/ui/identity_assign_op.rs:20:5 - | -LL | x -= 0; - | ^^^^^^^ help: remove it - error: this operation has no effect --> tests/ui/identity_assign_op.rs:23:5 | -LL | x |= 0; - | ^^^^^^^ help: remove it +LL | signed -= 0; + | ^^^^^^^^^^^^ help: remove it error: this operation has no effect --> tests/ui/identity_assign_op.rs:26:5 | -LL | x ^= 0; - | ^^^^^^^ help: remove it +LL | signed |= 0; + | ^^^^^^^^^^^^ help: remove it error: this operation has no effect --> tests/ui/identity_assign_op.rs:29:5 | -LL | x <<= 0; - | ^^^^^^^^ help: remove it +LL | signed ^= 0; + | ^^^^^^^^^^^^ help: remove it error: this operation has no effect --> tests/ui/identity_assign_op.rs:32:5 | -LL | x >>= 0; - | ^^^^^^^^ help: remove it +LL | signed <<= 0; + | ^^^^^^^^^^^^^ help: remove it error: this operation has no effect --> tests/ui/identity_assign_op.rs:35:5 | -LL | x *= 1; - | ^^^^^^^ help: remove it +LL | signed >>= 0; + | ^^^^^^^^^^^^^ help: remove it error: this operation has no effect --> tests/ui/identity_assign_op.rs:38:5 | -LL | x /= 1; - | ^^^^^^^ help: remove it +LL | signed *= 1; + | ^^^^^^^^^^^^ help: remove it error: this operation has no effect --> tests/ui/identity_assign_op.rs:41:5 | -LL | x += ZERO; - | ^^^^^^^^^^ help: remove it +LL | signed /= 1; + | ^^^^^^^^^^^^ help: remove it error: this operation has no effect - --> tests/ui/identity_assign_op.rs:44:5 + --> tests/ui/identity_assign_op.rs:46:5 | -LL | x *= ONE; - | ^^^^^^^^^ help: remove it +LL | unsigned += 0; + | ^^^^^^^^^^^^^^ help: remove it error: this operation has no effect --> tests/ui/identity_assign_op.rs:49:5 | -LL | y += 0u64; - | ^^^^^^^^^^ help: remove it +LL | unsigned *= 1; + | ^^^^^^^^^^^^^^ help: remove it error: this operation has no effect - --> tests/ui/identity_assign_op.rs:52:5 + --> tests/ui/identity_assign_op.rs:56:5 | -LL | y *= 1u64; - | ^^^^^^^^^^ help: remove it +LL | float32 *= 1.0; + | ^^^^^^^^^^^^^^^ help: remove it error: this operation has no effect - --> tests/ui/identity_assign_op.rs:55:5 + --> tests/ui/identity_assign_op.rs:59:5 | -LL | y += ZERO_U64; - | ^^^^^^^^^^^^^^ help: remove it +LL | float32 += -0.0; + | ^^^^^^^^^^^^^^^^ help: remove it error: this operation has no effect - --> tests/ui/identity_assign_op.rs:58:5 + --> tests/ui/identity_assign_op.rs:62:5 | -LL | y *= ONE_U64; - | ^^^^^^^^^^^^^ help: remove it +LL | float32 -= 0.0; + | ^^^^^^^^^^^^^^^ help: remove it + +error: this operation has no effect + --> tests/ui/identity_assign_op.rs:69:5 + | +LL | float64 *= 1.0; + | ^^^^^^^^^^^^^^^ help: remove it error: this operation has no effect - --> tests/ui/identity_assign_op.rs:63:5 + --> tests/ui/identity_assign_op.rs:72:5 | -LL | z += 0.0f32; - | ^^^^^^^^^^^^ help: remove it +LL | float64 += -0.0; + | ^^^^^^^^^^^^^^^^ help: remove it error: this operation has no effect - --> tests/ui/identity_assign_op.rs:66:5 + --> tests/ui/identity_assign_op.rs:75:5 | -LL | z *= 1.0f32; - | ^^^^^^^^^^^^ help: remove it +LL | float64 -= 0.0; + | ^^^^^^^^^^^^^^^ help: remove it error: this operation has no effect - --> tests/ui/identity_assign_op.rs:69:5 + --> tests/ui/identity_assign_op.rs:84:5 | -LL | z += ZERO_F32; - | ^^^^^^^^^^^^^^ help: remove it +LL | signed += ZERO_I64; + | ^^^^^^^^^^^^^^^^^^^ help: remove it error: this operation has no effect - --> tests/ui/identity_assign_op.rs:72:5 + --> tests/ui/identity_assign_op.rs:87:5 | -LL | z *= ONE_F32; - | ^^^^^^^^^^^^^ help: remove it +LL | signed *= ONE_I64; + | ^^^^^^^^^^^^^^^^^^ help: remove it error: this operation has no effect - --> tests/ui/identity_assign_op.rs:77:5 + --> tests/ui/identity_assign_op.rs:92:5 | -LL | w += 0.0; - | ^^^^^^^^^ help: remove it +LL | unsigned += ZERO_U64; + | ^^^^^^^^^^^^^^^^^^^^^ help: remove it error: this operation has no effect - --> tests/ui/identity_assign_op.rs:80:5 + --> tests/ui/identity_assign_op.rs:95:5 | -LL | w *= 1.0; - | ^^^^^^^^^ help: remove it +LL | unsigned *= ONE_U64; + | ^^^^^^^^^^^^^^^^^^^^ help: remove it error: this operation has no effect - --> tests/ui/identity_assign_op.rs:83:5 + --> tests/ui/identity_assign_op.rs:102:5 | -LL | w += ZERO_F64; - | ^^^^^^^^^^^^^^ help: remove it +LL | float32 *= ONE_F32; + | ^^^^^^^^^^^^^^^^^^^ help: remove it error: this operation has no effect - --> tests/ui/identity_assign_op.rs:86:5 + --> tests/ui/identity_assign_op.rs:105:5 | -LL | w *= ONE_F64; - | ^^^^^^^^^^^^^ help: remove it +LL | float32 += NEGATIVE_ZERO_F32; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove it + +error: this operation has no effect + --> tests/ui/identity_assign_op.rs:112:5 + | +LL | float64 *= ONE_F64; + | ^^^^^^^^^^^^^^^^^^^ help: remove it + +error: this operation has no effect + --> tests/ui/identity_assign_op.rs:115:5 + | +LL | float64 += NEGATIVE_ZERO_F64; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove it -error: aborting due to 22 previous errors +error: aborting due to 24 previous errors