diff options
Diffstat (limited to 'src/tools/clippy/clippy_lints/src/operators')
17 files changed, 296 insertions, 257 deletions
diff --git a/src/tools/clippy/clippy_lints/src/operators/absurd_extreme_comparisons.rs b/src/tools/clippy/clippy_lints/src/operators/absurd_extreme_comparisons.rs index 1ec4240af..d29ca37ea 100644 --- a/src/tools/clippy/clippy_lints/src/operators/absurd_extreme_comparisons.rs +++ b/src/tools/clippy/clippy_lints/src/operators/absurd_extreme_comparisons.rs @@ -34,13 +34,12 @@ pub(super) fn check<'tcx>( }; let help = format!( - "because `{}` is the {} value for this type, {}", + "because `{}` is the {} value for this type, {conclusion}", snippet(cx, culprit.expr.span, "x"), match culprit.which { ExtremeType::Minimum => "minimum", ExtremeType::Maximum => "maximum", - }, - conclusion + } ); span_lint_and_help(cx, ABSURD_EXTREME_COMPARISONS, expr.span, msg, None, &help); diff --git a/src/tools/clippy/clippy_lints/src/operators/arithmetic.rs b/src/tools/clippy/clippy_lints/src/operators/arithmetic.rs deleted file mode 100644 index 800cf249f..000000000 --- a/src/tools/clippy/clippy_lints/src/operators/arithmetic.rs +++ /dev/null @@ -1,119 +0,0 @@ -#![allow( - // False positive - clippy::match_same_arms -)] - -use super::ARITHMETIC; -use clippy_utils::{consts::constant_simple, diagnostics::span_lint}; -use rustc_data_structures::fx::FxHashSet; -use rustc_hir as hir; -use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::impl_lint_pass; -use rustc_span::source_map::Span; - -const HARD_CODED_ALLOWED: &[&str] = &["std::num::Saturating", "std::string::String", "std::num::Wrapping"]; - -#[derive(Debug)] -pub struct Arithmetic { - allowed: FxHashSet<String>, - // Used to check whether expressions are constants, such as in enum discriminants and consts - const_span: Option<Span>, - expr_span: Option<Span>, -} - -impl_lint_pass!(Arithmetic => [ARITHMETIC]); - -impl Arithmetic { - #[must_use] - pub fn new(mut allowed: FxHashSet<String>) -> Self { - allowed.extend(HARD_CODED_ALLOWED.iter().copied().map(String::from)); - Self { - allowed, - const_span: None, - expr_span: None, - } - } - - /// Checks if the given `expr` has any of the inner `allowed` elements. - fn is_allowed_ty(&self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool { - self.allowed.contains( - cx.typeck_results() - .expr_ty(expr) - .to_string() - .split('<') - .next() - .unwrap_or_default(), - ) - } - - fn issue_lint(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) { - span_lint(cx, ARITHMETIC, expr.span, "arithmetic detected"); - self.expr_span = Some(expr.span); - } -} - -impl<'tcx> LateLintPass<'tcx> for Arithmetic { - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if self.expr_span.is_some() { - return; - } - if let Some(span) = self.const_span && span.contains(expr.span) { - return; - } - match &expr.kind { - hir::ExprKind::Binary(op, lhs, rhs) | hir::ExprKind::AssignOp(op, lhs, rhs) => { - let ( - hir::BinOpKind::Add - | hir::BinOpKind::Sub - | hir::BinOpKind::Mul - | hir::BinOpKind::Div - | hir::BinOpKind::Rem - | hir::BinOpKind::Shl - | hir::BinOpKind::Shr - ) = op.node else { - return; - }; - if self.is_allowed_ty(cx, lhs) || self.is_allowed_ty(cx, rhs) { - return; - } - self.issue_lint(cx, expr); - }, - hir::ExprKind::Unary(hir::UnOp::Neg, _) => { - // CTFE already takes care of things like `-1` that do not overflow. - if constant_simple(cx, cx.typeck_results(), expr).is_none() { - self.issue_lint(cx, expr); - } - }, - _ => {}, - } - } - - fn check_body(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) { - let body_owner = cx.tcx.hir().body_owner_def_id(body.id()); - match cx.tcx.hir().body_owner_kind(body_owner) { - hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => { - let body_span = cx.tcx.def_span(body_owner); - if let Some(span) = self.const_span && span.contains(body_span) { - return; - } - self.const_span = Some(body_span); - }, - hir::BodyOwnerKind::Closure | hir::BodyOwnerKind::Fn => {}, - } - } - - fn check_body_post(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) { - let body_owner = cx.tcx.hir().body_owner(body.id()); - let body_span = cx.tcx.hir().span(body_owner); - if let Some(span) = self.const_span && span.contains(body_span) { - return; - } - self.const_span = None; - } - - fn check_expr_post(&mut self, _: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if Some(expr.span) == self.expr_span { - self.expr_span = None; - } - } -} diff --git a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs new file mode 100644 index 000000000..8827daaa3 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -0,0 +1,198 @@ +use super::ARITHMETIC_SIDE_EFFECTS; +use clippy_utils::{consts::constant_simple, diagnostics::span_lint}; +use rustc_ast as ast; +use rustc_data_structures::fx::FxHashSet; +use rustc_hir as hir; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::Ty; +use rustc_session::impl_lint_pass; +use rustc_span::source_map::{Span, Spanned}; + +const HARD_CODED_ALLOWED: &[&str] = &[ + "&str", + "f32", + "f64", + "std::num::Saturating", + "std::num::Wrapping", + "std::string::String", +]; + +#[derive(Debug)] +pub struct ArithmeticSideEffects { + allowed: FxHashSet<String>, + // Used to check whether expressions are constants, such as in enum discriminants and consts + const_span: Option<Span>, + expr_span: Option<Span>, +} + +impl_lint_pass!(ArithmeticSideEffects => [ARITHMETIC_SIDE_EFFECTS]); + +impl ArithmeticSideEffects { + #[must_use] + pub fn new(mut allowed: FxHashSet<String>) -> Self { + allowed.extend(HARD_CODED_ALLOWED.iter().copied().map(String::from)); + Self { + allowed, + const_span: None, + expr_span: None, + } + } + + /// Assuming that `expr` is a literal integer, checks operators (+=, -=, *, /) in a + /// non-constant environment that won't overflow. + fn has_valid_op(op: &Spanned<hir::BinOpKind>, expr: &hir::Expr<'_>) -> bool { + if let hir::ExprKind::Lit(ref lit) = expr.kind && + let ast::LitKind::Int(value, _) = lit.node + { + match (&op.node, value) { + (hir::BinOpKind::Div | hir::BinOpKind::Rem, 0) => false, + (hir::BinOpKind::Add | hir::BinOpKind::Sub, 0) + | (hir::BinOpKind::Div | hir::BinOpKind::Rem, _) + | (hir::BinOpKind::Mul, 0 | 1) => true, + _ => false, + } + } else { + false + } + } + + /// Checks if the given `expr` has any of the inner `allowed` elements. + fn is_allowed_ty(&self, ty: Ty<'_>) -> bool { + self.allowed + .contains(ty.to_string().split('<').next().unwrap_or_default()) + } + + // For example, 8i32 or &i64::MAX. + fn is_integral(ty: Ty<'_>) -> bool { + ty.peel_refs().is_integral() + } + + // Common entry-point to avoid code duplication. + fn issue_lint(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) { + let msg = "arithmetic operation that can potentially result in unexpected side-effects"; + span_lint(cx, ARITHMETIC_SIDE_EFFECTS, expr.span, msg); + self.expr_span = Some(expr.span); + } + + /// If `expr` does not match any variant of `LiteralIntegerTy`, returns `None`. + fn literal_integer<'expr, 'tcx>(expr: &'expr hir::Expr<'tcx>) -> Option<LiteralIntegerTy<'expr, 'tcx>> { + if matches!(expr.kind, hir::ExprKind::Lit(_)) { + return Some(LiteralIntegerTy::Value(expr)); + } + if let hir::ExprKind::AddrOf(.., inn) = expr.kind && let hir::ExprKind::Lit(_) = inn.kind { + return Some(LiteralIntegerTy::Ref(inn)); + } + None + } + + /// Manages when the lint should be triggered. Operations in constant environments, hard coded + /// types, custom allowed types and non-constant operations that won't overflow are ignored. + fn manage_bin_ops<'tcx>( + &mut self, + cx: &LateContext<'tcx>, + expr: &hir::Expr<'tcx>, + op: &Spanned<hir::BinOpKind>, + lhs: &hir::Expr<'tcx>, + rhs: &hir::Expr<'tcx>, + ) { + if constant_simple(cx, cx.typeck_results(), expr).is_some() { + return; + } + if !matches!( + op.node, + hir::BinOpKind::Add + | hir::BinOpKind::Sub + | hir::BinOpKind::Mul + | hir::BinOpKind::Div + | hir::BinOpKind::Rem + | hir::BinOpKind::Shl + | hir::BinOpKind::Shr + ) { + return; + }; + let lhs_ty = cx.typeck_results().expr_ty(lhs); + let rhs_ty = cx.typeck_results().expr_ty(rhs); + let lhs_and_rhs_have_the_same_ty = lhs_ty == rhs_ty; + if lhs_and_rhs_have_the_same_ty && self.is_allowed_ty(lhs_ty) && self.is_allowed_ty(rhs_ty) { + return; + } + let has_valid_op = if Self::is_integral(lhs_ty) && Self::is_integral(rhs_ty) { + match (Self::literal_integer(lhs), Self::literal_integer(rhs)) { + (None, Some(lit_int_ty)) | (Some(lit_int_ty), None) => Self::has_valid_op(op, lit_int_ty.into()), + (Some(LiteralIntegerTy::Value(_)), Some(LiteralIntegerTy::Value(_))) => true, + (None, None) | (Some(_), Some(_)) => false, + } + } else { + false + }; + if !has_valid_op { + self.issue_lint(cx, expr); + } + } +} + +impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'tcx>) { + if self.expr_span.is_some() || self.const_span.map_or(false, |sp| sp.contains(expr.span)) { + return; + } + match &expr.kind { + hir::ExprKind::Binary(op, lhs, rhs) | hir::ExprKind::AssignOp(op, lhs, rhs) => { + self.manage_bin_ops(cx, expr, op, lhs, rhs); + }, + hir::ExprKind::Unary(hir::UnOp::Neg, _) => { + if constant_simple(cx, cx.typeck_results(), expr).is_none() { + self.issue_lint(cx, expr); + } + }, + _ => {}, + } + } + + fn check_body(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) { + let body_owner = cx.tcx.hir().body_owner(body.id()); + let body_owner_def_id = cx.tcx.hir().local_def_id(body_owner); + let body_owner_kind = cx.tcx.hir().body_owner_kind(body_owner_def_id); + if let hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) = body_owner_kind { + let body_span = cx.tcx.hir().span_with_body(body_owner); + if let Some(span) = self.const_span && span.contains(body_span) { + return; + } + self.const_span = Some(body_span); + } + } + + fn check_body_post(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) { + let body_owner = cx.tcx.hir().body_owner(body.id()); + let body_span = cx.tcx.hir().span(body_owner); + if let Some(span) = self.const_span && span.contains(body_span) { + return; + } + self.const_span = None; + } + + fn check_expr_post(&mut self, _: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + if Some(expr.span) == self.expr_span { + self.expr_span = None; + } + } +} + +/// Tells if an expression is a integer declared by value or by reference. +/// +/// If `LiteralIntegerTy::Ref`, then the contained value will be `hir::ExprKind::Lit` rather +/// than `hirExprKind::Addr`. +enum LiteralIntegerTy<'expr, 'tcx> { + /// For example, `&199` + Ref(&'expr hir::Expr<'tcx>), + /// For example, `1` or `i32::MAX` + Value(&'expr hir::Expr<'tcx>), +} + +impl<'expr, 'tcx> From<LiteralIntegerTy<'expr, 'tcx>> for &'expr hir::Expr<'tcx> { + fn from(from: LiteralIntegerTy<'expr, 'tcx>) -> Self { + match from { + LiteralIntegerTy::Ref(elem) | LiteralIntegerTy::Value(elem) => elem, + } + } +} diff --git a/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs b/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs index 945a09a64..ee9fd9406 100644 --- a/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs +++ b/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs @@ -2,16 +2,17 @@ use clippy_utils::binop_traits; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet_opt; use clippy_utils::ty::implements_trait; +use clippy_utils::visitors::for_each_expr; use clippy_utils::{eq_expr_value, trait_ref_of_method}; +use core::ops::ControlFlow; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_lint::LateContext; use rustc_middle::mir::FakeReadCause; use rustc_middle::ty::BorrowKind; use rustc_trait_selection::infer::TyCtxtInferExt; -use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use super::ASSIGN_OP_PATTERN; @@ -28,7 +29,7 @@ pub(super) fn check<'tcx>( if_chain! { if let Some((_, lang_item)) = binop_traits(op.node); if let Ok(trait_id) = cx.tcx.lang_items().require(lang_item); - let parent_fn = cx.tcx.hir().get_parent_item(e.hir_id); + let parent_fn = cx.tcx.hir().get_parent_item(e.hir_id).def_id; if trait_ref_of_method(cx, parent_fn) .map_or(true, |t| t.path.res.def_id() != trait_id); if implements_trait(cx, ty, trait_id, &[rty.into()]); @@ -55,7 +56,7 @@ pub(super) fn check<'tcx>( diag.span_suggestion( expr.span, "replace it with", - format!("{} {}= {}", snip_a, op.node.as_str(), snip_r), + format!("{snip_a} {}= {snip_r}", op.node.as_str()), Applicability::MachineApplicable, ); } @@ -65,15 +66,19 @@ pub(super) fn check<'tcx>( } }; - let mut visitor = ExprVisitor { - assignee, - counter: 0, - cx, - }; - - walk_expr(&mut visitor, e); + let mut found = false; + let found_multiple = for_each_expr(e, |e| { + if eq_expr_value(cx, assignee, e) { + if found { + return ControlFlow::Break(()); + } + found = true; + } + ControlFlow::Continue(()) + }) + .is_some(); - if visitor.counter == 1 { + if found && !found_multiple { // a = a op b if eq_expr_value(cx, assignee, l) { lint(assignee, r); @@ -98,22 +103,6 @@ pub(super) fn check<'tcx>( } } -struct ExprVisitor<'a, 'tcx> { - assignee: &'a hir::Expr<'a>, - counter: u8, - cx: &'a LateContext<'tcx>, -} - -impl<'a, 'tcx> Visitor<'tcx> for ExprVisitor<'a, 'tcx> { - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) { - if eq_expr_value(self.cx, self.assignee, expr) { - self.counter += 1; - } - - walk_expr(self, expr); - } -} - fn imm_borrows_in_expr(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> hir::HirIdSet { struct S(hir::HirIdSet); impl Delegate<'_> for S { @@ -134,16 +123,15 @@ fn imm_borrows_in_expr(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> hir::HirIdSet } let mut s = S(hir::HirIdSet::default()); - cx.tcx.infer_ctxt().enter(|infcx| { - let mut v = ExprUseVisitor::new( - &mut s, - &infcx, - cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()), - cx.param_env, - cx.typeck_results(), - ); - v.consume_expr(e); - }); + let infcx = cx.tcx.infer_ctxt().build(); + let mut v = ExprUseVisitor::new( + &mut s, + &infcx, + cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()), + cx.param_env, + cx.typeck_results(), + ); + v.consume_expr(e); s.0 } @@ -167,15 +155,14 @@ fn mut_borrows_in_expr(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> hir::HirIdSet } let mut s = S(hir::HirIdSet::default()); - cx.tcx.infer_ctxt().enter(|infcx| { - let mut v = ExprUseVisitor::new( - &mut s, - &infcx, - cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()), - cx.param_env, - cx.typeck_results(), - ); - v.consume_expr(e); - }); + let infcx = cx.tcx.infer_ctxt().build(); + let mut v = ExprUseVisitor::new( + &mut s, + &infcx, + cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()), + cx.param_env, + cx.typeck_results(), + ); + v.consume_expr(e); s.0 } diff --git a/src/tools/clippy/clippy_lints/src/operators/bit_mask.rs b/src/tools/clippy/clippy_lints/src/operators/bit_mask.rs index 74387fbc8..1369b3e74 100644 --- a/src/tools/clippy/clippy_lints/src/operators/bit_mask.rs +++ b/src/tools/clippy/clippy_lints/src/operators/bit_mask.rs @@ -64,10 +64,7 @@ fn check_bit_mask( cx, BAD_BIT_MASK, span, - &format!( - "incompatible bit mask: `_ & {}` can never be equal to `{}`", - mask_value, cmp_value - ), + &format!("incompatible bit mask: `_ & {mask_value}` can never be equal to `{cmp_value}`"), ); } } else if mask_value == 0 { @@ -80,10 +77,7 @@ fn check_bit_mask( cx, BAD_BIT_MASK, span, - &format!( - "incompatible bit mask: `_ | {}` can never be equal to `{}`", - mask_value, cmp_value - ), + &format!("incompatible bit mask: `_ | {mask_value}` can never be equal to `{cmp_value}`"), ); } }, @@ -96,10 +90,7 @@ fn check_bit_mask( cx, BAD_BIT_MASK, span, - &format!( - "incompatible bit mask: `_ & {}` will always be lower than `{}`", - mask_value, cmp_value - ), + &format!("incompatible bit mask: `_ & {mask_value}` will always be lower than `{cmp_value}`"), ); } else if mask_value == 0 { span_lint(cx, BAD_BIT_MASK, span, "&-masking with zero"); @@ -111,10 +102,7 @@ fn check_bit_mask( cx, BAD_BIT_MASK, span, - &format!( - "incompatible bit mask: `_ | {}` will never be lower than `{}`", - mask_value, cmp_value - ), + &format!("incompatible bit mask: `_ | {mask_value}` will never be lower than `{cmp_value}`"), ); } else { check_ineffective_lt(cx, span, mask_value, cmp_value, "|"); @@ -130,10 +118,7 @@ fn check_bit_mask( cx, BAD_BIT_MASK, span, - &format!( - "incompatible bit mask: `_ & {}` will never be higher than `{}`", - mask_value, cmp_value - ), + &format!("incompatible bit mask: `_ & {mask_value}` will never be higher than `{cmp_value}`"), ); } else if mask_value == 0 { span_lint(cx, BAD_BIT_MASK, span, "&-masking with zero"); @@ -145,10 +130,7 @@ fn check_bit_mask( cx, BAD_BIT_MASK, span, - &format!( - "incompatible bit mask: `_ | {}` will always be higher than `{}`", - mask_value, cmp_value - ), + &format!("incompatible bit mask: `_ | {mask_value}` will always be higher than `{cmp_value}`"), ); } else { check_ineffective_gt(cx, span, mask_value, cmp_value, "|"); @@ -167,10 +149,7 @@ fn check_ineffective_lt(cx: &LateContext<'_>, span: Span, m: u128, c: u128, op: cx, INEFFECTIVE_BIT_MASK, span, - &format!( - "ineffective bit mask: `x {} {}` compared to `{}`, is the same as x compared directly", - op, m, c - ), + &format!("ineffective bit mask: `x {op} {m}` compared to `{c}`, is the same as x compared directly"), ); } } @@ -181,10 +160,7 @@ fn check_ineffective_gt(cx: &LateContext<'_>, span: Span, m: u128, c: u128, op: cx, INEFFECTIVE_BIT_MASK, span, - &format!( - "ineffective bit mask: `x {} {}` compared to `{}`, is the same as x compared directly", - op, m, c - ), + &format!("ineffective bit mask: `x {op} {m}` compared to `{c}`, is the same as x compared directly"), ); } } diff --git a/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs b/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs index e1f9b5906..24aeb82a3 100644 --- a/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs +++ b/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use clippy_utils::ty::{implements_trait, is_copy}; -use clippy_utils::{match_any_def_paths, path_def_id, paths}; +use clippy_utils::{match_def_path, path_def_id, paths}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::LateContext; @@ -38,7 +38,7 @@ fn symmetric_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: Ty<'t fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) { let typeck = cx.typeck_results(); let (arg, arg_span) = match expr.kind { - ExprKind::MethodCall(.., [arg], _) + ExprKind::MethodCall(_, arg, [], _) if typeck .type_dependent_def_id(expr.hir_id) .and_then(|id| cx.tcx.trait_of_item(id)) @@ -49,13 +49,15 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) (arg, arg.span) }, ExprKind::Call(path, [arg]) - if path_def_id(cx, path) - .and_then(|id| match_any_def_paths(cx, id, &[&paths::FROM_STR_METHOD, &paths::FROM_FROM])) - .map_or(false, |idx| match idx { - 0 => true, - 1 => !is_copy(cx, typeck.expr_ty(expr)), - _ => false, - }) => + if path_def_id(cx, path).map_or(false, |id| { + if match_def_path(cx, id, &paths::FROM_STR_METHOD) { + true + } else if cx.tcx.lang_items().from_fn() == Some(id) { + !is_copy(cx, typeck.expr_ty(expr)) + } else { + false + } + }) => { (arg, arg.span) }, @@ -99,7 +101,7 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) let expr_snip; let eq_impl; if with_deref.is_implemented() { - expr_snip = format!("*{}", arg_snip); + expr_snip = format!("*{arg_snip}"); eq_impl = with_deref; } else { expr_snip = arg_snip.to_string(); @@ -121,17 +123,15 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) }; if eq_impl.ty_eq_other { hint = format!( - "{}{}{}", - expr_snip, + "{expr_snip}{}{}", snippet(cx, cmp_span, ".."), snippet(cx, other.span, "..") ); } else { hint = format!( - "{}{}{}", + "{}{}{expr_snip}", snippet(cx, other.span, ".."), - snippet(cx, cmp_span, ".."), - expr_snip + snippet(cx, cmp_span, "..") ); } } diff --git a/src/tools/clippy/clippy_lints/src/operators/duration_subsec.rs b/src/tools/clippy/clippy_lints/src/operators/duration_subsec.rs index 0d067d1e1..49e662cac 100644 --- a/src/tools/clippy/clippy_lints/src/operators/duration_subsec.rs +++ b/src/tools/clippy/clippy_lints/src/operators/duration_subsec.rs @@ -17,7 +17,7 @@ pub(crate) fn check<'tcx>( right: &'tcx Expr<'_>, ) { if op == BinOpKind::Div - && let ExprKind::MethodCall(method_path, [self_arg], _) = left.kind + && let ExprKind::MethodCall(method_path, self_arg, [], _) = left.kind && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_arg).peel_refs(), sym::Duration) && let Some((Constant::Int(divisor), _)) = constant(cx, cx.typeck_results(), right) { @@ -31,12 +31,11 @@ pub(crate) fn check<'tcx>( cx, DURATION_SUBSEC, expr.span, - &format!("calling `{}()` is more concise than this calculation", suggested_fn), + &format!("calling `{suggested_fn}()` is more concise than this calculation"), "try", format!( - "{}.{}()", - snippet_with_applicability(cx, self_arg.span, "_", &mut applicability), - suggested_fn + "{}.{suggested_fn}()", + snippet_with_applicability(cx, self_arg.span, "_", &mut applicability) ), applicability, ); diff --git a/src/tools/clippy/clippy_lints/src/operators/eq_op.rs b/src/tools/clippy/clippy_lints/src/operators/eq_op.rs index 44cf0bb06..67913f739 100644 --- a/src/tools/clippy/clippy_lints/src/operators/eq_op.rs +++ b/src/tools/clippy/clippy_lints/src/operators/eq_op.rs @@ -22,7 +22,7 @@ pub(crate) fn check_assert<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { cx, EQ_OP, lhs.span.to(rhs.span), - &format!("identical args used in this `{}!` macro call", macro_name), + &format!("identical args used in this `{macro_name}!` macro call"), ); } } diff --git a/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs b/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs index 0ef793443..97ddcdb24 100644 --- a/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs +++ b/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs @@ -113,7 +113,7 @@ fn is_signum(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { } if_chain! { - if let ExprKind::MethodCall(method_name, [ref self_arg, ..], _) = expr.kind; + if let ExprKind::MethodCall(method_name, self_arg, ..) = expr.kind; if sym!(signum) == method_name.ident.name; // Check that the receiver of the signum() is a float (expressions[0] is the receiver of // the method call) diff --git a/src/tools/clippy/clippy_lints/src/operators/misrefactored_assign_op.rs b/src/tools/clippy/clippy_lints/src/operators/misrefactored_assign_op.rs index 0024384d9..ae805147f 100644 --- a/src/tools/clippy/clippy_lints/src/operators/misrefactored_assign_op.rs +++ b/src/tools/clippy/clippy_lints/src/operators/misrefactored_assign_op.rs @@ -47,18 +47,14 @@ fn lint_misrefactored_assign_op( if let (Some(snip_a), Some(snip_r)) = (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs_other.span)) { let a = &sugg::Sugg::hir(cx, assignee, ".."); let r = &sugg::Sugg::hir(cx, rhs, ".."); - let long = format!("{} = {}", snip_a, sugg::make_binop(op.into(), a, r)); + let long = format!("{snip_a} = {}", sugg::make_binop(op.into(), a, r)); diag.span_suggestion( expr.span, &format!( - "did you mean `{} = {} {} {}` or `{}`? Consider replacing it with", - snip_a, - snip_a, - op.as_str(), - snip_r, - long + "did you mean `{snip_a} = {snip_a} {} {snip_r}` or `{long}`? Consider replacing it with", + op.as_str() ), - format!("{} {}= {}", snip_a, op.as_str(), snip_r), + format!("{snip_a} {}= {snip_r}", op.as_str()), Applicability::MaybeIncorrect, ); diag.span_suggestion( diff --git a/src/tools/clippy/clippy_lints/src/operators/mod.rs b/src/tools/clippy/clippy_lints/src/operators/mod.rs index bb6d99406..b8a20d5eb 100644 --- a/src/tools/clippy/clippy_lints/src/operators/mod.rs +++ b/src/tools/clippy/clippy_lints/src/operators/mod.rs @@ -21,7 +21,7 @@ mod ptr_eq; mod self_assignment; mod verbose_bit_mask; -pub(crate) mod arithmetic; +pub(crate) mod arithmetic_side_effects; use rustc_hir::{Body, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; @@ -61,25 +61,29 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does - /// Checks for any kind of arithmetic operation of any type. + /// Checks any kind of arithmetic operation of any type. /// /// Operators like `+`, `-`, `*` or `<<` are usually capable of overflowing according to the [Rust /// Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow), - /// or can panic (`/`, `%`). Known safe built-in types like `Wrapping` or `Saturing` are filtered - /// away. + /// or can panic (`/`, `%`). + /// + /// Known safe built-in types like `Wrapping` or `Saturating`, floats, operations in constant + /// environments, allowed types and non-constant operations that won't overflow are ignored. /// /// ### Why is this bad? - /// Integer overflow will trigger a panic in debug builds or will wrap in - /// release mode. Division by zero will cause a panic in either mode. In some applications one - /// wants explicitly checked, wrapping or saturating arithmetic. + /// For integers, overflow will trigger a panic in debug builds or wrap the result in + /// release mode; division by zero will cause a panic in either mode. As a result, it is + /// desirable to explicitly call checked, wrapping or saturating arithmetic methods. /// /// #### Example /// ```rust - /// # let a = 0; - /// a + 1; + /// // `n` can be any number, including `i32::MAX`. + /// fn foo(n: i32) -> i32 { + /// n + 1 + /// } /// ``` /// - /// Third-party types also tend to overflow. + /// Third-party types can also overflow or present unwanted side-effects. /// /// #### Example /// ```ignore,rust @@ -88,11 +92,11 @@ declare_clippy_lint! { /// ``` /// /// ### Allowed types - /// Custom allowed types can be specified through the "arithmetic-allowed" filter. + /// Custom allowed types can be specified through the "arithmetic-side-effects-allowed" filter. #[clippy::version = "1.64.0"] - pub ARITHMETIC, + pub ARITHMETIC_SIDE_EFFECTS, restriction, - "any arithmetic expression that could overflow or panic" + "any arithmetic expression that can cause side effects like overflows or panics" } declare_clippy_lint! { @@ -785,7 +789,7 @@ pub struct Operators { } impl_lint_pass!(Operators => [ ABSURD_EXTREME_COMPARISONS, - ARITHMETIC, + ARITHMETIC_SIDE_EFFECTS, INTEGER_ARITHMETIC, FLOAT_ARITHMETIC, ASSIGN_OP_PATTERN, diff --git a/src/tools/clippy/clippy_lints/src/operators/needless_bitwise_bool.rs b/src/tools/clippy/clippy_lints/src/operators/needless_bitwise_bool.rs index e902235a0..ab5fb1787 100644 --- a/src/tools/clippy/clippy_lints/src/operators/needless_bitwise_bool.rs +++ b/src/tools/clippy/clippy_lints/src/operators/needless_bitwise_bool.rs @@ -27,7 +27,7 @@ pub(super) fn check(cx: &LateContext<'_>, e: &Expr<'_>, op: BinOpKind, lhs: &Exp if let Some(lhs_snip) = snippet_opt(cx, lhs.span) && let Some(rhs_snip) = snippet_opt(cx, rhs.span) { - let sugg = format!("{} {} {}", lhs_snip, op_str, rhs_snip); + let sugg = format!("{lhs_snip} {op_str} {rhs_snip}"); diag.span_suggestion(e.span, "try", sugg, Applicability::MachineApplicable); } }, diff --git a/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs b/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs index b6097710d..0830a106f 100644 --- a/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs +++ b/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs @@ -1,5 +1,6 @@ use clippy_utils::consts::constant_simple; use clippy_utils::diagnostics::span_lint; +use clippy_utils::is_integer_literal; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_span::source_map::Span; @@ -50,11 +51,9 @@ impl Context { hir::BinOpKind::Div | hir::BinOpKind::Rem => match &r.kind { hir::ExprKind::Lit(_lit) => (), hir::ExprKind::Unary(hir::UnOp::Neg, expr) => { - if let hir::ExprKind::Lit(lit) = &expr.kind { - if let rustc_ast::ast::LitKind::Int(1, _) = lit.node { - span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); - self.expr_id = Some(expr.hir_id); - } + if is_integer_literal(expr, 1) { + span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected"); + self.expr_id = Some(expr.hir_id); } }, _ => { diff --git a/src/tools/clippy/clippy_lints/src/operators/op_ref.rs b/src/tools/clippy/clippy_lints/src/operators/op_ref.rs index 1805672e3..71b31b5e4 100644 --- a/src/tools/clippy/clippy_lints/src/operators/op_ref.rs +++ b/src/tools/clippy/clippy_lints/src/operators/op_ref.rs @@ -185,7 +185,7 @@ fn in_impl<'tcx>( if let ItemKind::Impl(item) = &item.kind; if let Some(of_trait) = &item.of_trait; if let Some(seg) = of_trait.path.segments.last(); - if let Some(Res::Def(_, trait_id)) = seg.res; + if let Res::Def(_, trait_id) = seg.res; if trait_id == bin_op; if let Some(generic_args) = seg.args; if let Some(GenericArg::Type(other_ty)) = generic_args.args.last(); @@ -204,7 +204,7 @@ fn are_equal<'tcx>(cx: &LateContext<'tcx>, middle_ty: Ty<'_>, hir_ty: &rustc_hir if let ty::Adt(adt_def, _) = middle_ty.kind(); if let Some(local_did) = adt_def.did().as_local(); let item = cx.tcx.hir().expect_item(local_did); - let middle_ty_id = item.def_id.to_def_id(); + let middle_ty_id = item.owner_id.to_def_id(); if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind; if let Res::Def(_, hir_ty_id) = path.res; diff --git a/src/tools/clippy/clippy_lints/src/operators/ptr_eq.rs b/src/tools/clippy/clippy_lints/src/operators/ptr_eq.rs index 1aefc2741..1229c202f 100644 --- a/src/tools/clippy/clippy_lints/src/operators/ptr_eq.rs +++ b/src/tools/clippy/clippy_lints/src/operators/ptr_eq.rs @@ -34,7 +34,7 @@ pub(super) fn check<'tcx>( expr.span, LINT_MSG, "try", - format!("std::ptr::eq({}, {})", left_snip, right_snip), + format!("std::ptr::eq({left_snip}, {right_snip})"), Applicability::MachineApplicable, ); } diff --git a/src/tools/clippy/clippy_lints/src/operators/self_assignment.rs b/src/tools/clippy/clippy_lints/src/operators/self_assignment.rs index 9d6bec05b..7c9d5320a 100644 --- a/src/tools/clippy/clippy_lints/src/operators/self_assignment.rs +++ b/src/tools/clippy/clippy_lints/src/operators/self_assignment.rs @@ -14,7 +14,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, lhs: &'tcx cx, SELF_ASSIGNMENT, e.span, - &format!("self-assignment of `{}` to `{}`", rhs, lhs), + &format!("self-assignment of `{rhs}` to `{lhs}`"), ); } } diff --git a/src/tools/clippy/clippy_lints/src/operators/verbose_bit_mask.rs b/src/tools/clippy/clippy_lints/src/operators/verbose_bit_mask.rs index ff85fd554..fbf65e92b 100644 --- a/src/tools/clippy/clippy_lints/src/operators/verbose_bit_mask.rs +++ b/src/tools/clippy/clippy_lints/src/operators/verbose_bit_mask.rs @@ -35,7 +35,7 @@ pub(super) fn check<'tcx>( diag.span_suggestion( e.span, "try", - format!("{}.trailing_zeros() >= {}", sugg, n.count_ones()), + format!("{sugg}.trailing_zeros() >= {}", n.count_ones()), Applicability::MaybeIncorrect, ); }, |