diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 33c20602dfd23..97e6879c33e99 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -981,6 +981,75 @@ impl BinOpKind { pub type BinOp = Spanned; +// Sometimes `BinOpKind` and `AssignOpKind` need the same treatment. The +// operations covered by `AssignOpKind` are a subset of those covered by +// `BinOpKind`, so it makes sense to convert `AssignOpKind` to `BinOpKind`. +impl From for BinOpKind { + fn from(op: AssignOpKind) -> BinOpKind { + match op { + AssignOpKind::AddAssign => BinOpKind::Add, + AssignOpKind::SubAssign => BinOpKind::Sub, + AssignOpKind::MulAssign => BinOpKind::Mul, + AssignOpKind::DivAssign => BinOpKind::Div, + AssignOpKind::RemAssign => BinOpKind::Rem, + AssignOpKind::BitXorAssign => BinOpKind::BitXor, + AssignOpKind::BitAndAssign => BinOpKind::BitAnd, + AssignOpKind::BitOrAssign => BinOpKind::BitOr, + AssignOpKind::ShlAssign => BinOpKind::Shl, + AssignOpKind::ShrAssign => BinOpKind::Shr, + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Encodable, Decodable, HashStable_Generic)] +pub enum AssignOpKind { + /// The `+=` operator (addition) + AddAssign, + /// The `-=` operator (subtraction) + SubAssign, + /// The `*=` operator (multiplication) + MulAssign, + /// The `/=` operator (division) + DivAssign, + /// The `%=` operator (modulus) + RemAssign, + /// The `^=` operator (bitwise xor) + BitXorAssign, + /// The `&=` operator (bitwise and) + BitAndAssign, + /// The `|=` operator (bitwise or) + BitOrAssign, + /// The `<<=` operator (shift left) + ShlAssign, + /// The `>>=` operator (shift right) + ShrAssign, +} + +impl AssignOpKind { + pub fn as_str(&self) -> &'static str { + use AssignOpKind::*; + match self { + AddAssign => "+=", + SubAssign => "-=", + MulAssign => "*=", + DivAssign => "/=", + RemAssign => "%=", + BitXorAssign => "^=", + BitAndAssign => "&=", + BitOrAssign => "|=", + ShlAssign => "<<=", + ShrAssign => ">>=", + } + } + + /// AssignOps are always by value. + pub fn is_by_value(self) -> bool { + true + } +} + +pub type AssignOp = Spanned; + /// Unary operator. /// /// Note that `&data` is not an operator, it's an `AddrOf` expression. @@ -1593,7 +1662,7 @@ pub enum ExprKind { /// An assignment with an operator. /// /// E.g., `a += 1`. - AssignOp(BinOp, P, P), + AssignOp(AssignOp, P, P), /// Access of a named (e.g., `obj.foo`) or unnamed (e.g., `obj.0`) struct field. Field(P, Ident), /// An indexing operation (e.g., `foo[2]`). diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs index 98b1fc52ed747..1e5f414fae1c7 100644 --- a/compiler/rustc_ast/src/util/parser.rs +++ b/compiler/rustc_ast/src/util/parser.rs @@ -1,6 +1,6 @@ use rustc_span::kw; -use crate::ast::{self, BinOpKind, RangeLimits}; +use crate::ast::{self, AssignOpKind, BinOpKind, RangeLimits}; use crate::token::{self, Token}; /// Associative operator. @@ -9,7 +9,7 @@ pub enum AssocOp { /// A binary op. Binary(BinOpKind), /// `?=` where ? is one of the assignable BinOps - AssignOp(BinOpKind), + AssignOp(AssignOpKind), /// `=` Assign, /// `as` @@ -44,16 +44,16 @@ impl AssocOp { token::Or => Some(Binary(BinOpKind::BitOr)), token::Shl => Some(Binary(BinOpKind::Shl)), token::Shr => Some(Binary(BinOpKind::Shr)), - token::PlusEq => Some(AssignOp(BinOpKind::Add)), - token::MinusEq => Some(AssignOp(BinOpKind::Sub)), - token::StarEq => Some(AssignOp(BinOpKind::Mul)), - token::SlashEq => Some(AssignOp(BinOpKind::Div)), - token::PercentEq => Some(AssignOp(BinOpKind::Rem)), - token::CaretEq => Some(AssignOp(BinOpKind::BitXor)), - token::AndEq => Some(AssignOp(BinOpKind::BitAnd)), - token::OrEq => Some(AssignOp(BinOpKind::BitOr)), - token::ShlEq => Some(AssignOp(BinOpKind::Shl)), - token::ShrEq => Some(AssignOp(BinOpKind::Shr)), + token::PlusEq => Some(AssignOp(AssignOpKind::AddAssign)), + token::MinusEq => Some(AssignOp(AssignOpKind::SubAssign)), + token::StarEq => Some(AssignOp(AssignOpKind::MulAssign)), + token::SlashEq => Some(AssignOp(AssignOpKind::DivAssign)), + token::PercentEq => Some(AssignOp(AssignOpKind::RemAssign)), + token::CaretEq => Some(AssignOp(AssignOpKind::BitXorAssign)), + token::AndEq => Some(AssignOp(AssignOpKind::BitAndAssign)), + token::OrEq => Some(AssignOp(AssignOpKind::BitOrAssign)), + token::ShlEq => Some(AssignOp(AssignOpKind::ShlAssign)), + token::ShrEq => Some(AssignOp(AssignOpKind::ShrAssign)), token::Lt => Some(Binary(BinOpKind::Lt)), token::Le => Some(Binary(BinOpKind::Le)), token::Ge => Some(Binary(BinOpKind::Ge)), diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 52291fdfb3029..80bb1e8fc4144 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -274,7 +274,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } ExprKind::Assign(el, er, span) => self.lower_expr_assign(el, er, *span, e.span), ExprKind::AssignOp(op, el, er) => hir::ExprKind::AssignOp( - self.lower_binop(*op), + self.lower_assign_op(*op), self.lower_expr(el), self.lower_expr(er), ), @@ -443,6 +443,10 @@ impl<'hir> LoweringContext<'_, 'hir> { Spanned { node: b.node, span: self.lower_span(b.span) } } + fn lower_assign_op(&mut self, a: AssignOp) -> AssignOp { + Spanned { node: a.node, span: self.lower_span(a.span) } + } + fn lower_legacy_const_generics( &mut self, mut f: Expr, diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 7d9dc89bd7567..df848a26d390a 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -274,22 +274,22 @@ impl<'a> State<'a> { fn print_expr_binary( &mut self, - op: ast::BinOp, + op: ast::BinOpKind, lhs: &ast::Expr, rhs: &ast::Expr, fixup: FixupContext, ) { - let binop_prec = op.node.precedence(); + let binop_prec = op.precedence(); let left_prec = lhs.precedence(); let right_prec = rhs.precedence(); - let (mut left_needs_paren, right_needs_paren) = match op.node.fixity() { + let (mut left_needs_paren, right_needs_paren) = match op.fixity() { Fixity::Left => (left_prec < binop_prec, right_prec <= binop_prec), Fixity::Right => (left_prec <= binop_prec, right_prec < binop_prec), Fixity::None => (left_prec <= binop_prec, right_prec <= binop_prec), }; - match (&lhs.kind, op.node) { + match (&lhs.kind, op) { // These cases need parens: `x as i32 < y` has the parser thinking that `i32 < y` is // the beginning of a path type. It starts trying to parse `x as (i32 < y ...` instead // of `(x as i32) < ...`. We need to convince it _not_ to do that. @@ -312,7 +312,7 @@ impl<'a> State<'a> { self.print_expr_cond_paren(lhs, left_needs_paren, fixup.leftmost_subexpression()); self.space(); - self.word_space(op.node.as_str()); + self.word_space(op.as_str()); self.print_expr_cond_paren(rhs, right_needs_paren, fixup.subsequent_subexpression()); } @@ -410,7 +410,7 @@ impl<'a> State<'a> { self.print_expr_method_call(seg, receiver, args, fixup); } ast::ExprKind::Binary(op, lhs, rhs) => { - self.print_expr_binary(*op, lhs, rhs, fixup); + self.print_expr_binary(op.node, lhs, rhs, fixup); } ast::ExprKind::Unary(op, expr) => { self.print_expr_unary(*op, expr, fixup); @@ -605,8 +605,7 @@ impl<'a> State<'a> { fixup.leftmost_subexpression(), ); self.space(); - self.word(op.node.as_str()); - self.word_space("="); + self.word_space(op.node.as_str()); self.print_expr_cond_paren( rhs, rhs.precedence() < ExprPrecedence::Assign, diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index f8af1b81ca9be..1a6c15b66a45f 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -10,9 +10,9 @@ use rustc_ast::{ LitKind, TraitObjectSyntax, UintTy, UnsafeBinderCastKind, }; pub use rustc_ast::{ - AttrId, AttrStyle, BinOp, BinOpKind, BindingMode, BorrowKind, BoundConstness, BoundPolarity, - ByRef, CaptureBy, DelimArgs, ImplPolarity, IsAuto, MetaItemInner, MetaItemLit, Movability, - Mutability, UnOp, + AssignOp, AssignOpKind, AttrId, AttrStyle, BinOp, BinOpKind, BindingMode, BorrowKind, + BoundConstness, BoundPolarity, ByRef, CaptureBy, DelimArgs, ImplPolarity, IsAuto, + MetaItemInner, MetaItemLit, Movability, Mutability, UnOp, }; use rustc_attr_data_structures::AttributeKind; use rustc_data_structures::fingerprint::Fingerprint; @@ -2648,7 +2648,7 @@ pub enum ExprKind<'hir> { /// An assignment with an operator. /// /// E.g., `a += 1`. - AssignOp(BinOp, &'hir Expr<'hir>, &'hir Expr<'hir>), + AssignOp(AssignOp, &'hir Expr<'hir>, &'hir Expr<'hir>), /// Access of a named (e.g., `obj.foo`) or unnamed (e.g., `obj.0`) struct or tuple field. Field(&'hir Expr<'hir>, Ident), /// An indexing operation (`foo[2]`). diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 1c23761b2e5bf..8c0c17f7a7d6a 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -1271,18 +1271,18 @@ impl<'a> State<'a> { self.print_call_post(base_args) } - fn print_expr_binary(&mut self, op: hir::BinOp, lhs: &hir::Expr<'_>, rhs: &hir::Expr<'_>) { - let binop_prec = op.node.precedence(); + fn print_expr_binary(&mut self, op: hir::BinOpKind, lhs: &hir::Expr<'_>, rhs: &hir::Expr<'_>) { + let binop_prec = op.precedence(); let left_prec = lhs.precedence(); let right_prec = rhs.precedence(); - let (mut left_needs_paren, right_needs_paren) = match op.node.fixity() { + let (mut left_needs_paren, right_needs_paren) = match op.fixity() { Fixity::Left => (left_prec < binop_prec, right_prec <= binop_prec), Fixity::Right => (left_prec <= binop_prec, right_prec < binop_prec), Fixity::None => (left_prec <= binop_prec, right_prec <= binop_prec), }; - match (&lhs.kind, op.node) { + match (&lhs.kind, op) { // These cases need parens: `x as i32 < y` has the parser thinking that `i32 < y` is // the beginning of a path type. It starts trying to parse `x as (i32 < y ...` instead // of `(x as i32) < ...`. We need to convince it _not_ to do that. @@ -1297,7 +1297,7 @@ impl<'a> State<'a> { self.print_expr_cond_paren(lhs, left_needs_paren); self.space(); - self.word_space(op.node.as_str()); + self.word_space(op.as_str()); self.print_expr_cond_paren(rhs, right_needs_paren); } @@ -1451,7 +1451,7 @@ impl<'a> State<'a> { self.word(".use"); } hir::ExprKind::Binary(op, lhs, rhs) => { - self.print_expr_binary(op, lhs, rhs); + self.print_expr_binary(op.node, lhs, rhs); } hir::ExprKind::Unary(op, expr) => { self.print_expr_unary(op, expr); @@ -1572,8 +1572,7 @@ impl<'a> State<'a> { hir::ExprKind::AssignOp(op, lhs, rhs) => { self.print_expr_cond_paren(lhs, lhs.precedence() <= ExprPrecedence::Assign); self.space(); - self.word(op.node.as_str()); - self.word_space("="); + self.word_space(op.node.as_str()); self.print_expr_cond_paren(rhs, rhs.precedence() < ExprPrecedence::Assign); } hir::ExprKind::Field(expr, ident) => { diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index a882ea27af6bb..45ab8e03db578 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -512,7 +512,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.check_expr_assign(expr, expected, lhs, rhs, span) } ExprKind::AssignOp(op, lhs, rhs) => { - self.check_expr_binop_assign(expr, op, lhs, rhs, expected) + self.check_expr_assign_op(expr, op, lhs, rhs, expected) } ExprKind::Unary(unop, oprnd) => self.check_expr_unop(unop, oprnd, expected, expr), ExprKind::AddrOf(kind, mutbl, oprnd) => { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 0097b6ea1231c..912098c4e2d65 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -3477,30 +3477,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lhs_ty: Ty<'tcx>, rhs_expr: &'tcx hir::Expr<'tcx>, lhs_expr: &'tcx hir::Expr<'tcx>, - op: hir::BinOp, ) { - match op.node { - hir::BinOpKind::Eq => { - if let Some(partial_eq_def_id) = self.infcx.tcx.lang_items().eq_trait() - && self - .infcx - .type_implements_trait(partial_eq_def_id, [rhs_ty, lhs_ty], self.param_env) - .must_apply_modulo_regions() - { - let sm = self.tcx.sess.source_map(); - if let Ok(rhs_snippet) = sm.span_to_snippet(rhs_expr.span) - && let Ok(lhs_snippet) = sm.span_to_snippet(lhs_expr.span) - { - err.note(format!("`{rhs_ty}` implements `PartialEq<{lhs_ty}>`")); - err.multipart_suggestion( - "consider swapping the equality", - vec![(lhs_expr.span, rhs_snippet), (rhs_expr.span, lhs_snippet)], - Applicability::MaybeIncorrect, - ); - } - } + if let Some(partial_eq_def_id) = self.infcx.tcx.lang_items().eq_trait() + && self + .infcx + .type_implements_trait(partial_eq_def_id, [rhs_ty, lhs_ty], self.param_env) + .must_apply_modulo_regions() + { + let sm = self.tcx.sess.source_map(); + if let Ok(rhs_snippet) = sm.span_to_snippet(rhs_expr.span) + && let Ok(lhs_snippet) = sm.span_to_snippet(lhs_expr.span) + { + err.note(format!("`{rhs_ty}` implements `PartialEq<{lhs_ty}>`")); + err.multipart_suggestion( + "consider swapping the equality", + vec![(lhs_expr.span, rhs_snippet), (rhs_expr.span, lhs_snippet)], + Applicability::MaybeIncorrect, + ); } - _ => {} } } } diff --git a/compiler/rustc_hir_typeck/src/op.rs b/compiler/rustc_hir_typeck/src/op.rs index a473e14b24445..93f77b8409f00 100644 --- a/compiler/rustc_hir_typeck/src/op.rs +++ b/compiler/rustc_hir_typeck/src/op.rs @@ -4,15 +4,15 @@ use rustc_data_structures::packed::Pu128; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, struct_span_code_err}; use rustc_infer::traits::ObligationCauseCode; +use rustc_middle::bug; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, }; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt, TypeVisitableExt}; -use rustc_middle::{bug, span_bug}; use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::source_map::Spanned; -use rustc_span::{Ident, Span, sym}; +use rustc_span::{Ident, Span, Symbol, sym}; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits::{FulfillmentError, Obligation, ObligationCtxt}; use tracing::debug; @@ -24,24 +24,27 @@ use crate::Expectation; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Checks a `a = b` - pub(crate) fn check_expr_binop_assign( + pub(crate) fn check_expr_assign_op( &self, expr: &'tcx hir::Expr<'tcx>, - op: hir::BinOp, + op: hir::AssignOp, lhs: &'tcx hir::Expr<'tcx>, rhs: &'tcx hir::Expr<'tcx>, expected: Expectation<'tcx>, ) -> Ty<'tcx> { let (lhs_ty, rhs_ty, return_ty) = - self.check_overloaded_binop(expr, lhs, rhs, op, IsAssign::Yes, expected); - - let ty = - if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) { - self.enforce_builtin_binop_types(lhs.span, lhs_ty, rhs.span, rhs_ty, op); - self.tcx.types.unit - } else { - return_ty - }; + self.check_overloaded_binop(expr, lhs, rhs, Op::AssignOp(op), expected); + + let category = BinOpCategory::from(op.node); + let ty = if !lhs_ty.is_ty_var() + && !rhs_ty.is_ty_var() + && is_builtin_binop(lhs_ty, rhs_ty, category) + { + self.enforce_builtin_binop_types(lhs.span, lhs_ty, rhs.span, rhs_ty, category); + self.tcx.types.unit + } else { + return_ty + }; self.check_lhs_assignable(lhs, E0067, op.span, |err| { if let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) { @@ -49,7 +52,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .lookup_op_method( (lhs, lhs_deref_ty), Some((rhs, rhs_ty)), - Op::Binary(op, IsAssign::Yes), + lang_item_for_binop(self.tcx, Op::AssignOp(op)), + op.span, expected, ) .is_ok() @@ -60,7 +64,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .lookup_op_method( (lhs, lhs_ty), Some((rhs, rhs_ty)), - Op::Binary(op, IsAssign::Yes), + lang_item_for_binop(self.tcx, Op::AssignOp(op)), + op.span, expected, ) .is_err() @@ -98,7 +103,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr.hir_id, expr, op, lhs_expr, rhs_expr ); - match BinOpCategory::from(op) { + match BinOpCategory::from(op.node) { BinOpCategory::Shortcircuit => { // && and || are a simple case. self.check_expr_coercible_to_type(lhs_expr, tcx.types.bool, None); @@ -114,14 +119,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Otherwise, we always treat operators as if they are // overloaded. This is the way to be most flexible w/r/t // types that get inferred. - let (lhs_ty, rhs_ty, return_ty) = self.check_overloaded_binop( - expr, - lhs_expr, - rhs_expr, - op, - IsAssign::No, - expected, - ); + let (lhs_ty, rhs_ty, return_ty) = + self.check_overloaded_binop(expr, lhs_expr, rhs_expr, Op::BinOp(op), expected); // Supply type inference hints if relevant. Probably these // hints should be enforced during select as part of the @@ -135,16 +134,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // deduce that the result type should be `u32`, even // though we don't know yet what type 2 has and hence // can't pin this down to a specific impl. + let category = BinOpCategory::from(op.node); if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() - && is_builtin_binop(lhs_ty, rhs_ty, op) + && is_builtin_binop(lhs_ty, rhs_ty, category) { let builtin_return_ty = self.enforce_builtin_binop_types( lhs_expr.span, lhs_ty, rhs_expr.span, rhs_ty, - op, + category, ); self.demand_eqtype(expr.span, builtin_return_ty, return_ty); builtin_return_ty @@ -161,16 +161,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lhs_ty: Ty<'tcx>, rhs_span: Span, rhs_ty: Ty<'tcx>, - op: hir::BinOp, + category: BinOpCategory, ) -> Ty<'tcx> { - debug_assert!(is_builtin_binop(lhs_ty, rhs_ty, op)); + debug_assert!(is_builtin_binop(lhs_ty, rhs_ty, category)); // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work. // (See https://github.com/rust-lang/rust/issues/57447.) let (lhs_ty, rhs_ty) = (deref_ty_if_possible(lhs_ty), deref_ty_if_possible(rhs_ty)); let tcx = self.tcx; - match BinOpCategory::from(op) { + match category { BinOpCategory::Shortcircuit => { self.demand_suptype(lhs_span, tcx.types.bool, lhs_ty); self.demand_suptype(rhs_span, tcx.types.bool, rhs_ty); @@ -201,17 +201,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expr: &'tcx hir::Expr<'tcx>, lhs_expr: &'tcx hir::Expr<'tcx>, rhs_expr: &'tcx hir::Expr<'tcx>, - op: hir::BinOp, - is_assign: IsAssign, + op: Op, expected: Expectation<'tcx>, ) -> (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>) { - debug!( - "check_overloaded_binop(expr.hir_id={}, op={:?}, is_assign={:?})", - expr.hir_id, op, is_assign - ); + debug!("check_overloaded_binop(expr.hir_id={}, op={:?})", expr.hir_id, op); - let lhs_ty = match is_assign { - IsAssign::No => { + let lhs_ty = match op { + Op::BinOp(_) => { // Find a suitable supertype of the LHS expression's type, by coercing to // a type variable, to pass as the `Self` to the trait, avoiding invariant // trait matching creating lifetime constraints that are too strict. @@ -221,7 +217,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let fresh_var = self.next_ty_var(lhs_expr.span); self.demand_coerce(lhs_expr, lhs_ty, fresh_var, Some(rhs_expr), AllowTwoPhase::No) } - IsAssign::Yes => { + Op::AssignOp(_) => { // rust-lang/rust#52126: We have to use strict // equivalence on the LHS of an assign-op like `+=`; // overwritten or mutably-borrowed places cannot be @@ -242,7 +238,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let result = self.lookup_op_method( (lhs_expr, lhs_ty), Some((rhs_expr, rhs_ty_var)), - Op::Binary(op, is_assign), + lang_item_for_binop(self.tcx, op), + op.span(), expected, ); @@ -252,15 +249,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { rhs_ty_var, Some(lhs_expr), |err, ty| { - self.suggest_swapping_lhs_and_rhs(err, ty, lhs_ty, rhs_expr, lhs_expr, op); + if let Op::BinOp(binop) = op + && binop.node == hir::BinOpKind::Eq + { + self.suggest_swapping_lhs_and_rhs(err, ty, lhs_ty, rhs_expr, lhs_expr); + } }, ); let rhs_ty = self.resolve_vars_with_obligations(rhs_ty); let return_ty = match result { Ok(method) => { - let by_ref_binop = !op.node.is_by_value(); - if is_assign == IsAssign::Yes || by_ref_binop { + let by_ref_binop = !op.is_by_value(); + if matches!(op, Op::AssignOp(_)) || by_ref_binop { if let ty::Ref(_, _, mutbl) = method.sig.inputs()[0].kind() { let mutbl = AutoBorrowMutability::new(*mutbl, AllowTwoPhase::Yes); let autoref = Adjustment { @@ -301,32 +302,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Ty::new_misc_error(self.tcx) } Err(errors) => { - let (_, trait_def_id) = - lang_item_for_op(self.tcx, Op::Binary(op, is_assign), op.span); + let (_, trait_def_id) = lang_item_for_binop(self.tcx, op); let missing_trait = trait_def_id .map(|def_id| with_no_trimmed_paths!(self.tcx.def_path_str(def_id))); let mut path = None; let lhs_ty_str = self.tcx.short_string(lhs_ty, &mut path); let rhs_ty_str = self.tcx.short_string(rhs_ty, &mut path); - let (mut err, output_def_id) = match is_assign { - IsAssign::Yes => { + let (mut err, output_def_id) = match op { + Op::AssignOp(assign_op) => { + let s = assign_op.node.as_str(); let mut err = struct_span_code_err!( self.dcx(), expr.span, E0368, - "binary assignment operation `{}=` cannot be applied to type `{}`", - op.node.as_str(), + "binary assignment operation `{}` cannot be applied to type `{}`", + s, lhs_ty_str, ); err.span_label( lhs_expr.span, - format!("cannot use `{}=` on type `{}`", op.node.as_str(), lhs_ty_str), + format!("cannot use `{}` on type `{}`", s, lhs_ty_str), ); self.note_unmet_impls_on_type(&mut err, errors, false); (err, None) } - IsAssign::No => { - let message = match op.node { + Op::BinOp(bin_op) => { + let message = match bin_op.node { hir::BinOpKind::Add => { format!("cannot add `{rhs_ty_str}` to `{lhs_ty_str}`") } @@ -362,8 +363,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } _ => format!( "binary operation `{}` cannot be applied to type `{}`", - op.node.as_str(), - lhs_ty_str, + bin_op.node.as_str(), + lhs_ty_str ), }; let output_def_id = trait_def_id.and_then(|def_id| { @@ -376,7 +377,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .cloned() }); let mut err = - struct_span_code_err!(self.dcx(), op.span, E0369, "{message}"); + struct_span_code_err!(self.dcx(), bin_op.span, E0369, "{message}"); if !lhs_expr.span.eq(&rhs_expr.span) { err.span_label(lhs_expr.span, lhs_ty_str.clone()); err.span_label(rhs_expr.span, rhs_ty_str); @@ -409,18 +410,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .lookup_op_method( (lhs_expr, lhs_deref_ty), Some((rhs_expr, rhs_ty)), - Op::Binary(op, is_assign), + lang_item_for_binop(self.tcx, op), + op.span(), expected, ) .is_ok() { let msg = format!( - "`{}{}` can be used on `{}` if you dereference the left-hand side", - op.node.as_str(), - match is_assign { - IsAssign::Yes => "=", - IsAssign::No => "", - }, + "`{}` can be used on `{}` if you dereference the left-hand side", + op.as_str(), self.tcx.short_string(lhs_deref_ty, err.long_ty_path()), ); err.span_suggestion_verbose( @@ -442,14 +440,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .lookup_op_method( (lhs_expr, lhs_adjusted_ty), Some((rhs_expr, rhs_adjusted_ty)), - Op::Binary(op, is_assign), + lang_item_for_binop(self.tcx, op), + op.span(), expected, ) .is_ok() { let lhs = self.tcx.short_string(lhs_adjusted_ty, err.long_ty_path()); let rhs = self.tcx.short_string(rhs_adjusted_ty, err.long_ty_path()); - let op = op.node.as_str(); + let op = op.as_str(); err.note(format!("an implementation for `{lhs} {op} {rhs}` exists")); if let Some(lhs_new_mutbl) = lhs_new_mutbl @@ -499,7 +498,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.lookup_op_method( (lhs_expr, lhs_ty), Some((rhs_expr, rhs_ty)), - Op::Binary(op, is_assign), + lang_item_for_binop(self.tcx, op), + op.span(), expected, ) .is_ok() @@ -511,13 +511,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We should suggest `a + b` => `*a + b` if `a` is copy, and suggest // `a += b` => `*a += b` if a is a mut ref. - if !op.span.can_be_used_for_suggestions() { + if !op.span().can_be_used_for_suggestions() { // Suppress suggestions when lhs and rhs are not in the same span as the error - } else if is_assign == IsAssign::Yes + } else if let Op::AssignOp(_) = op && let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) { suggest_deref_binop(&mut err, lhs_deref_ty); - } else if is_assign == IsAssign::No + } else if let Op::BinOp(_) = op && let ty::Ref(region, lhs_deref_ty, mutbl) = lhs_ty.kind() { if self.type_is_copy_modulo_regions(self.param_env, *lhs_deref_ty) { @@ -572,10 +572,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } if let Some(missing_trait) = missing_trait { - if op.node == hir::BinOpKind::Add - && self.check_str_addition( - lhs_expr, rhs_expr, lhs_ty, rhs_ty, &mut err, is_assign, op, - ) + if matches!( + op, + Op::BinOp(Spanned { node: hir::BinOpKind::Add, .. }) + | Op::AssignOp(Spanned { node: hir::AssignOpKind::AddAssign, .. }) + ) && self + .check_str_addition(lhs_expr, rhs_expr, lhs_ty, rhs_ty, &mut err, op) { // This has nothing here because it means we did string // concatenation (e.g., "Hello " + "World!"). This means @@ -592,7 +594,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .lookup_op_method( (lhs_expr, lhs_ty), Some((rhs_expr, rhs_ty)), - Op::Binary(op, is_assign), + lang_item_for_binop(self.tcx, op), + op.span(), expected, ) .unwrap_err(); @@ -642,9 +645,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // Suggest using `add`, `offset` or `offset_from` for pointer - {integer}, // pointer + {integer} or pointer - pointer. - if op.span.can_be_used_for_suggestions() { - match op.node { - hir::BinOpKind::Add if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() => { + if op.span().can_be_used_for_suggestions() { + match op { + Op::BinOp(Spanned { node: hir::BinOpKind::Add, .. }) + if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() => + { err.multipart_suggestion( "consider using `wrapping_add` or `add` for pointer + {integer}", vec![ @@ -657,7 +662,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MaybeIncorrect, ); } - hir::BinOpKind::Sub => { + Op::BinOp(Spanned { node: hir::BinOpKind::Sub, .. }) => { if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() { err.multipart_suggestion( "consider using `wrapping_sub` or `sub` for \ @@ -713,8 +718,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { lhs_ty: Ty<'tcx>, rhs_ty: Ty<'tcx>, err: &mut Diag<'_>, - is_assign: IsAssign, - op: hir::BinOp, + op: Op, ) -> bool { let str_concat_note = "string concatenation requires an owned `String` on the left"; let rm_borrow_msg = "remove the borrow to obtain an owned `String`"; @@ -733,8 +737,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { r_ty.kind(), ty::Ref(_, inner_ty, _) if *inner_ty.kind() == ty::Str )) => { - if let IsAssign::No = is_assign { // Do not supply this message if `&str += &str` - err.span_label(op.span, "`+` cannot be used to concatenate two `&str` strings"); + if let Op::BinOp(_) = op { // Do not supply this message if `&str += &str` + err.span_label( + op.span(), + "`+` cannot be used to concatenate two `&str` strings" + ); err.note(str_concat_note); if let hir::ExprKind::AddrOf(_, _, lhs_inner_expr) = lhs_expr.kind { err.span_suggestion_verbose( @@ -758,11 +765,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if (*l_ty.kind() == ty::Str || is_std_string(l_ty)) && is_std_string(rhs_ty) => { err.span_label( - op.span, + op.span(), "`+` cannot be used to concatenate a `&str` with a `String`", ); - match is_assign { - IsAssign::No => { + match op { + Op::BinOp(_) => { let sugg_msg; let lhs_sugg = if let hir::ExprKind::AddrOf(_, _, lhs_inner_expr) = lhs_expr.kind { sugg_msg = "remove the borrow on the left and add one on the right"; @@ -781,7 +788,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Applicability::MachineApplicable, ); } - IsAssign::Yes => { + Op::AssignOp(_) => { err.note(str_concat_note); } } @@ -799,7 +806,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected: Expectation<'tcx>, ) -> Ty<'tcx> { assert!(op.is_by_value()); - match self.lookup_op_method((ex, operand_ty), None, Op::Unary(op, ex.span), expected) { + match self.lookup_op_method( + (ex, operand_ty), + None, + lang_item_for_unop(self.tcx, op), + ex.span, + expected, + ) { Ok(method) => { self.write_method_call_and_enforce_effects(ex.hir_id, ex.span, method); method.sig.output() @@ -898,21 +911,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { &self, (lhs_expr, lhs_ty): (&'tcx hir::Expr<'tcx>, Ty<'tcx>), opt_rhs: Option<(&'tcx hir::Expr<'tcx>, Ty<'tcx>)>, - op: Op, + (opname, trait_did): (Symbol, Option), + span: Span, expected: Expectation<'tcx>, ) -> Result, Vec>> { - let span = match op { - Op::Binary(op, _) => op.span, - Op::Unary(_, span) => span, - }; - let (opname, Some(trait_did)) = lang_item_for_op(self.tcx, op, span) else { + let Some(trait_did) = trait_did else { // Bail if the operator trait is not defined. return Err(vec![]); }; debug!( - "lookup_op_method(lhs_ty={:?}, op={:?}, opname={:?}, trait_did={:?})", - lhs_ty, op, opname, trait_did + "lookup_op_method(lhs_ty={:?}, opname={:?}, trait_did={:?})", + lhs_ty, opname, trait_did ); let opname = Ident::with_dummy_span(opname); @@ -980,37 +990,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } -fn lang_item_for_op( - tcx: TyCtxt<'_>, - op: Op, - span: Span, -) -> (rustc_span::Symbol, Option) { +fn lang_item_for_binop(tcx: TyCtxt<'_>, op: Op) -> (Symbol, Option) { let lang = tcx.lang_items(); - if let Op::Binary(op, IsAssign::Yes) = op { - match op.node { - hir::BinOpKind::Add => (sym::add_assign, lang.add_assign_trait()), - hir::BinOpKind::Sub => (sym::sub_assign, lang.sub_assign_trait()), - hir::BinOpKind::Mul => (sym::mul_assign, lang.mul_assign_trait()), - hir::BinOpKind::Div => (sym::div_assign, lang.div_assign_trait()), - hir::BinOpKind::Rem => (sym::rem_assign, lang.rem_assign_trait()), - hir::BinOpKind::BitXor => (sym::bitxor_assign, lang.bitxor_assign_trait()), - hir::BinOpKind::BitAnd => (sym::bitand_assign, lang.bitand_assign_trait()), - hir::BinOpKind::BitOr => (sym::bitor_assign, lang.bitor_assign_trait()), - hir::BinOpKind::Shl => (sym::shl_assign, lang.shl_assign_trait()), - hir::BinOpKind::Shr => (sym::shr_assign, lang.shr_assign_trait()), - hir::BinOpKind::Lt - | hir::BinOpKind::Le - | hir::BinOpKind::Ge - | hir::BinOpKind::Gt - | hir::BinOpKind::Eq - | hir::BinOpKind::Ne - | hir::BinOpKind::And - | hir::BinOpKind::Or => { - span_bug!(span, "impossible assignment operation: {}=", op.node.as_str()) - } - } - } else if let Op::Binary(op, IsAssign::No) = op { - match op.node { + match op { + Op::AssignOp(op) => match op.node { + hir::AssignOpKind::AddAssign => (sym::add_assign, lang.add_assign_trait()), + hir::AssignOpKind::SubAssign => (sym::sub_assign, lang.sub_assign_trait()), + hir::AssignOpKind::MulAssign => (sym::mul_assign, lang.mul_assign_trait()), + hir::AssignOpKind::DivAssign => (sym::div_assign, lang.div_assign_trait()), + hir::AssignOpKind::RemAssign => (sym::rem_assign, lang.rem_assign_trait()), + hir::AssignOpKind::BitXorAssign => (sym::bitxor_assign, lang.bitxor_assign_trait()), + hir::AssignOpKind::BitAndAssign => (sym::bitand_assign, lang.bitand_assign_trait()), + hir::AssignOpKind::BitOrAssign => (sym::bitor_assign, lang.bitor_assign_trait()), + hir::AssignOpKind::ShlAssign => (sym::shl_assign, lang.shl_assign_trait()), + hir::AssignOpKind::ShrAssign => (sym::shr_assign, lang.shr_assign_trait()), + }, + Op::BinOp(op) => match op.node { hir::BinOpKind::Add => (sym::add, lang.add_trait()), hir::BinOpKind::Sub => (sym::sub, lang.sub_trait()), hir::BinOpKind::Mul => (sym::mul, lang.mul_trait()), @@ -1028,20 +1023,24 @@ fn lang_item_for_op( hir::BinOpKind::Eq => (sym::eq, lang.eq_trait()), hir::BinOpKind::Ne => (sym::ne, lang.eq_trait()), hir::BinOpKind::And | hir::BinOpKind::Or => { - span_bug!(span, "&& and || are not overloadable") + bug!("&& and || are not overloadable") } - } - } else if let Op::Unary(hir::UnOp::Not, _) = op { - (sym::not, lang.not_trait()) - } else if let Op::Unary(hir::UnOp::Neg, _) = op { - (sym::neg, lang.neg_trait()) - } else { - bug!("lookup_op_method: op not supported: {:?}", op) + }, + } +} + +fn lang_item_for_unop(tcx: TyCtxt<'_>, op: hir::UnOp) -> (Symbol, Option) { + let lang = tcx.lang_items(); + match op { + hir::UnOp::Not => (sym::not, lang.not_trait()), + hir::UnOp::Neg => (sym::neg, lang.neg_trait()), + hir::UnOp::Deref => bug!("Deref is not overloadable"), } } // Binary operator categories. These categories summarize the behavior // with respect to the builtin operations supported. +#[derive(Clone, Copy)] enum BinOpCategory { /// &&, || -- cannot be overridden Shortcircuit, @@ -1063,44 +1062,58 @@ enum BinOpCategory { Comparison, } -impl BinOpCategory { - fn from(op: hir::BinOp) -> BinOpCategory { - match op.node { - hir::BinOpKind::Shl | hir::BinOpKind::Shr => BinOpCategory::Shift, - - hir::BinOpKind::Add - | hir::BinOpKind::Sub - | hir::BinOpKind::Mul - | hir::BinOpKind::Div - | hir::BinOpKind::Rem => BinOpCategory::Math, - - hir::BinOpKind::BitXor | hir::BinOpKind::BitAnd | hir::BinOpKind::BitOr => { - BinOpCategory::Bitwise - } - - hir::BinOpKind::Eq - | hir::BinOpKind::Ne - | hir::BinOpKind::Lt - | hir::BinOpKind::Le - | hir::BinOpKind::Ge - | hir::BinOpKind::Gt => BinOpCategory::Comparison, +impl From for BinOpCategory { + fn from(op: hir::BinOpKind) -> BinOpCategory { + use hir::BinOpKind::*; + match op { + Shl | Shr => BinOpCategory::Shift, + Add | Sub | Mul | Div | Rem => BinOpCategory::Math, + BitXor | BitAnd | BitOr => BinOpCategory::Bitwise, + Eq | Ne | Lt | Le | Ge | Gt => BinOpCategory::Comparison, + And | Or => BinOpCategory::Shortcircuit, + } + } +} - hir::BinOpKind::And | hir::BinOpKind::Or => BinOpCategory::Shortcircuit, +impl From for BinOpCategory { + fn from(op: hir::AssignOpKind) -> BinOpCategory { + use hir::AssignOpKind::*; + match op { + ShlAssign | ShrAssign => BinOpCategory::Shift, + AddAssign | SubAssign | MulAssign | DivAssign | RemAssign => BinOpCategory::Math, + BitXorAssign | BitAndAssign | BitOrAssign => BinOpCategory::Bitwise, } } } -/// Whether the binary operation is an assignment (`a += b`), or not (`a + b`) +/// An assignment op (e.g. `a += b`), or a binary op (e.g. `a + b`). #[derive(Clone, Copy, Debug, PartialEq)] -enum IsAssign { - No, - Yes, +enum Op { + BinOp(hir::BinOp), + AssignOp(hir::AssignOp), } -#[derive(Clone, Copy, Debug)] -enum Op { - Binary(hir::BinOp, IsAssign), - Unary(hir::UnOp, Span), +impl Op { + fn span(&self) -> Span { + match self { + Op::BinOp(op) => op.span, + Op::AssignOp(op) => op.span, + } + } + + fn as_str(&self) -> &'static str { + match self { + Op::BinOp(op) => op.node.as_str(), + Op::AssignOp(op) => op.node.as_str(), + } + } + + fn is_by_value(&self) -> bool { + match self { + Op::BinOp(op) => op.node.is_by_value(), + Op::AssignOp(op) => op.node.is_by_value(), + } + } } /// Dereferences a single level of immutable referencing. @@ -1127,27 +1140,24 @@ fn deref_ty_if_possible(ty: Ty<'_>) -> Ty<'_> { /// Reason #2 is the killer. I tried for a while to always use /// overloaded logic and just check the types in constants/codegen after /// the fact, and it worked fine, except for SIMD types. -nmatsakis -fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, op: hir::BinOp) -> bool { +fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, category: BinOpCategory) -> bool { // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work. // (See https://github.com/rust-lang/rust/issues/57447.) let (lhs, rhs) = (deref_ty_if_possible(lhs), deref_ty_if_possible(rhs)); - match BinOpCategory::from(op) { + match category.into() { BinOpCategory::Shortcircuit => true, - BinOpCategory::Shift => { lhs.references_error() || rhs.references_error() || lhs.is_integral() && rhs.is_integral() } - BinOpCategory::Math => { lhs.references_error() || rhs.references_error() || lhs.is_integral() && rhs.is_integral() || lhs.is_floating_point() && rhs.is_floating_point() } - BinOpCategory::Bitwise => { lhs.references_error() || rhs.references_error() @@ -1155,7 +1165,6 @@ fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, op: hir::BinOp) -> bool || lhs.is_floating_point() && rhs.is_floating_point() || lhs.is_bool() && rhs.is_bool() } - BinOpCategory::Comparison => { lhs.references_error() || rhs.references_error() || lhs.is_scalar() && rhs.is_scalar() } diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index d457133c703fd..06f82b361b0db 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -160,7 +160,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { self.typeck_results.node_args_mut().remove(e.hir_id); } } - hir::ExprKind::Binary(ref op, lhs, rhs) | hir::ExprKind::AssignOp(ref op, lhs, rhs) => { + hir::ExprKind::Binary(ref op, lhs, rhs) => { let lhs_ty = self.typeck_results.node_type(lhs.hir_id); let rhs_ty = self.typeck_results.node_type(rhs.hir_id); @@ -168,25 +168,27 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { self.typeck_results.type_dependent_defs_mut().remove(e.hir_id); self.typeck_results.node_args_mut().remove(e.hir_id); - match e.kind { - hir::ExprKind::Binary(..) => { - if !op.node.is_by_value() { - let mut adjustments = self.typeck_results.adjustments_mut(); - if let Some(a) = adjustments.get_mut(lhs.hir_id) { - a.pop(); - } - if let Some(a) = adjustments.get_mut(rhs.hir_id) { - a.pop(); - } - } + if !op.node.is_by_value() { + let mut adjustments = self.typeck_results.adjustments_mut(); + if let Some(a) = adjustments.get_mut(lhs.hir_id) { + a.pop(); } - hir::ExprKind::AssignOp(..) - if let Some(a) = - self.typeck_results.adjustments_mut().get_mut(lhs.hir_id) => - { + if let Some(a) = adjustments.get_mut(rhs.hir_id) { a.pop(); } - _ => {} + } + } + } + hir::ExprKind::AssignOp(_, lhs, rhs) => { + let lhs_ty = self.typeck_results.node_type(lhs.hir_id); + let rhs_ty = self.typeck_results.node_type(rhs.hir_id); + + if lhs_ty.is_scalar() && rhs_ty.is_scalar() { + self.typeck_results.type_dependent_defs_mut().remove(e.hir_id); + self.typeck_results.node_args_mut().remove(e.hir_id); + + if let Some(a) = self.typeck_results.adjustments_mut().get_mut(lhs.hir_id) { + a.pop(); } } } diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index fec5db64bf28e..a6c82f574a1c7 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -14,7 +14,7 @@ use rustc_middle::ty::{ }; use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass}; use rustc_span::def_id::LocalDefId; -use rustc_span::{Span, Symbol, source_map, sym}; +use rustc_span::{Span, Symbol, sym}; use tracing::debug; use {rustc_ast as ast, rustc_hir as hir}; @@ -223,7 +223,7 @@ impl TypeLimits { fn lint_nan<'tcx>( cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>, - binop: hir::BinOp, + binop: hir::BinOpKind, l: &'tcx hir::Expr<'tcx>, r: &'tcx hir::Expr<'tcx>, ) { @@ -262,19 +262,19 @@ fn lint_nan<'tcx>( InvalidNanComparisons::EqNe { suggestion } } - let lint = match binop.node { + let lint = match binop { hir::BinOpKind::Eq | hir::BinOpKind::Ne if is_nan(cx, l) => { eq_ne(e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful { nan_plus_binop: l_span.until(r_span), float: r_span.shrink_to_hi(), - neg: (binop.node == hir::BinOpKind::Ne).then(|| r_span.shrink_to_lo()), + neg: (binop == hir::BinOpKind::Ne).then(|| r_span.shrink_to_lo()), }) } hir::BinOpKind::Eq | hir::BinOpKind::Ne if is_nan(cx, r) => { eq_ne(e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful { nan_plus_binop: l_span.shrink_to_hi().to(r_span), float: l_span.shrink_to_hi(), - neg: (binop.node == hir::BinOpKind::Ne).then(|| l_span.shrink_to_lo()), + neg: (binop == hir::BinOpKind::Ne).then(|| l_span.shrink_to_lo()), }) } hir::BinOpKind::Lt | hir::BinOpKind::Le | hir::BinOpKind::Gt | hir::BinOpKind::Ge @@ -560,11 +560,11 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { } } hir::ExprKind::Binary(binop, ref l, ref r) => { - if is_comparison(binop) { - if !check_limits(cx, binop, l, r) { + if is_comparison(binop.node) { + if !check_limits(cx, binop.node, l, r) { cx.emit_span_lint(UNUSED_COMPARISONS, e.span, UnusedComparisons); } else { - lint_nan(cx, e, binop, l, r); + lint_nan(cx, e, binop.node, l, r); let cmpop = ComparisonOp::BinOp(binop.node); lint_wide_pointer(cx, e, cmpop, l, r); lint_fn_pointer(cx, e, cmpop, l, r); @@ -591,8 +591,8 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { _ => {} }; - fn is_valid(binop: hir::BinOp, v: T, min: T, max: T) -> bool { - match binop.node { + fn is_valid(binop: hir::BinOpKind, v: T, min: T, max: T) -> bool { + match binop { hir::BinOpKind::Lt => v > min && v <= max, hir::BinOpKind::Le => v >= min && v < max, hir::BinOpKind::Gt => v >= min && v < max, @@ -602,22 +602,19 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { } } - fn rev_binop(binop: hir::BinOp) -> hir::BinOp { - source_map::respan( - binop.span, - match binop.node { - hir::BinOpKind::Lt => hir::BinOpKind::Gt, - hir::BinOpKind::Le => hir::BinOpKind::Ge, - hir::BinOpKind::Gt => hir::BinOpKind::Lt, - hir::BinOpKind::Ge => hir::BinOpKind::Le, - _ => return binop, - }, - ) + fn rev_binop(binop: hir::BinOpKind) -> hir::BinOpKind { + match binop { + hir::BinOpKind::Lt => hir::BinOpKind::Gt, + hir::BinOpKind::Le => hir::BinOpKind::Ge, + hir::BinOpKind::Gt => hir::BinOpKind::Lt, + hir::BinOpKind::Ge => hir::BinOpKind::Le, + _ => binop, + } } fn check_limits( cx: &LateContext<'_>, - binop: hir::BinOp, + binop: hir::BinOpKind, l: &hir::Expr<'_>, r: &hir::Expr<'_>, ) -> bool { @@ -659,9 +656,9 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { } } - fn is_comparison(binop: hir::BinOp) -> bool { + fn is_comparison(binop: hir::BinOpKind) -> bool { matches!( - binop.node, + binop, hir::BinOpKind::Eq | hir::BinOpKind::Lt | hir::BinOpKind::Le diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 6d6e6a1f185b5..707c8d04d557f 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1668,6 +1668,42 @@ pub enum BinOp { Offset, } +// Assignment operators, e.g. `+=`. See comments on the corresponding variants +// in `BinOp` for details. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, HashStable)] +pub enum AssignOp { + AddAssign, + SubAssign, + MulAssign, + DivAssign, + RemAssign, + BitXorAssign, + BitAndAssign, + BitOrAssign, + ShlAssign, + ShrAssign, +} + +// Sometimes `BinOp` and `AssignOp` need the same treatment. The operations +// covered by `AssignOp` are a subset of those covered by `BinOp`, so it makes +// sense to convert `AssignOp` to `BinOp`. +impl From for BinOp { + fn from(op: AssignOp) -> BinOp { + match op { + AssignOp::AddAssign => BinOp::Add, + AssignOp::SubAssign => BinOp::Sub, + AssignOp::MulAssign => BinOp::Mul, + AssignOp::DivAssign => BinOp::Div, + AssignOp::RemAssign => BinOp::Rem, + AssignOp::BitXorAssign => BinOp::BitXor, + AssignOp::BitAndAssign => BinOp::BitAnd, + AssignOp::BitOrAssign => BinOp::BitOr, + AssignOp::ShlAssign => BinOp::Shl, + AssignOp::ShrAssign => BinOp::Shr, + } + } +} + // Some nodes are used a lot. Make sure they don't unintentionally get bigger. #[cfg(target_pointer_width = "64")] mod size_asserts { diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index 6783bbf8bf42f..8d373cb3b3091 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -27,7 +27,7 @@ use tracing::instrument; use crate::middle::region; use crate::mir::interpret::AllocId; -use crate::mir::{self, BinOp, BorrowKind, FakeReadCause, UnOp}; +use crate::mir::{self, AssignOp, BinOp, BorrowKind, FakeReadCause, UnOp}; use crate::thir::visit::for_each_immediate_subpat; use crate::ty::adjustment::PointerCoercion; use crate::ty::layout::IntegerExt; @@ -403,7 +403,7 @@ pub enum ExprKind<'tcx> { }, /// A *non-overloaded* operation assignment, e.g. `lhs += rhs`. AssignOp { - op: BinOp, + op: AssignOp, lhs: ExprId, rhs: ExprId, }, diff --git a/compiler/rustc_mir_build/src/builder/expr/stmt.rs b/compiler/rustc_mir_build/src/builder/expr/stmt.rs index 7f8a0a34c3123..2dff26f02f3a1 100644 --- a/compiler/rustc_mir_build/src/builder/expr/stmt.rs +++ b/compiler/rustc_mir_build/src/builder/expr/stmt.rs @@ -78,8 +78,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { // because AssignOp is only legal for Copy types // (overloaded ops should be desugared into a call). let result = unpack!( - block = - this.build_binary_op(block, op, expr_span, lhs_ty, Operand::Copy(lhs), rhs) + block = this.build_binary_op( + block, + op.into(), + expr_span, + lhs_ty, + Operand::Copy(lhs), + rhs + ) ); this.cfg.push_assign(block, source_info, lhs, result); diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index b8af77245f25d..31e22e69111be 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -9,7 +9,7 @@ use rustc_middle::hir::place::{ Place as HirPlace, PlaceBase as HirPlaceBase, ProjectionKind as HirProjectionKind, }; use rustc_middle::middle::region; -use rustc_middle::mir::{self, BinOp, BorrowKind, UnOp}; +use rustc_middle::mir::{self, AssignOp, BinOp, BorrowKind, UnOp}; use rustc_middle::thir::*; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, PointerCoercion, @@ -489,7 +489,7 @@ impl<'tcx> ThirBuildCx<'tcx> { self.overloaded_operator(expr, Box::new([lhs, rhs])) } else { ExprKind::AssignOp { - op: bin_op(op.node), + op: assign_op(op.node), lhs: self.mirror_expr(lhs), rhs: self.mirror_expr(rhs), } @@ -1347,3 +1347,18 @@ fn bin_op(op: hir::BinOpKind) -> BinOp { _ => bug!("no equivalent for ast binop {:?}", op), } } + +fn assign_op(op: hir::AssignOpKind) -> AssignOp { + match op { + hir::AssignOpKind::AddAssign => AssignOp::AddAssign, + hir::AssignOpKind::SubAssign => AssignOp::SubAssign, + hir::AssignOpKind::MulAssign => AssignOp::MulAssign, + hir::AssignOpKind::DivAssign => AssignOp::DivAssign, + hir::AssignOpKind::RemAssign => AssignOp::RemAssign, + hir::AssignOpKind::BitXorAssign => AssignOp::BitXorAssign, + hir::AssignOpKind::BitAndAssign => AssignOp::BitAndAssign, + hir::AssignOpKind::BitOrAssign => AssignOp::BitOrAssign, + hir::AssignOpKind::ShlAssign => AssignOp::ShlAssign, + hir::AssignOpKind::ShrAssign => AssignOp::ShrAssign, + } +} diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index e1e6b93abf354..841d967d934be 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -14,10 +14,10 @@ use rustc_ast::util::classify; use rustc_ast::util::parser::{AssocOp, ExprPrecedence, Fixity, prec_let_scrutinee_needs_par}; use rustc_ast::visit::{Visitor, walk_expr}; use rustc_ast::{ - self as ast, AnonConst, Arm, AttrStyle, AttrVec, BinOp, BinOpKind, BlockCheckMode, CaptureBy, - ClosureBinder, DUMMY_NODE_ID, Expr, ExprField, ExprKind, FnDecl, FnRetTy, Label, MacCall, - MetaItemLit, Movability, Param, RangeLimits, StmtKind, Ty, TyKind, UnOp, UnsafeBinderCastKind, - YieldKind, + self as ast, AnonConst, Arm, AssignOp, AssignOpKind, AttrStyle, AttrVec, BinOp, BinOpKind, + BlockCheckMode, CaptureBy, ClosureBinder, DUMMY_NODE_ID, Expr, ExprField, ExprKind, FnDecl, + FnRetTy, Label, MacCall, MetaItemLit, Movability, Param, RangeLimits, StmtKind, Ty, TyKind, + UnOp, UnsafeBinderCastKind, YieldKind, }; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{Applicability, Diag, PResult, StashKey, Subdiagnostic}; @@ -359,7 +359,7 @@ impl<'a> Parser<'a> { ( Some( AssocOp::Binary(BinOpKind::Shr | BinOpKind::Gt | BinOpKind::Ge) - | AssocOp::AssignOp(BinOpKind::Shr), + | AssocOp::AssignOp(AssignOpKind::ShrAssign), ), _, ) if self.restrictions.contains(Restrictions::CONST_EXPR) => { @@ -3914,8 +3914,8 @@ impl<'a> Parser<'a> { self.dcx().emit_err(errors::LeftArrowOperator { span }); } - fn mk_assign_op(&self, binop: BinOp, lhs: P, rhs: P) -> ExprKind { - ExprKind::AssignOp(binop, lhs, rhs) + fn mk_assign_op(&self, assign_op: AssignOp, lhs: P, rhs: P) -> ExprKind { + ExprKind::AssignOp(assign_op, lhs, rhs) } fn mk_range( diff --git a/src/tools/clippy/clippy_lints/src/format_push_string.rs b/src/tools/clippy/clippy_lints/src/format_push_string.rs index 68cc50f39391f..b64d608c0c709 100644 --- a/src/tools/clippy/clippy_lints/src/format_push_string.rs +++ b/src/tools/clippy/clippy_lints/src/format_push_string.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher; use clippy_utils::ty::is_type_lang_item; -use rustc_hir::{BinOpKind, Expr, ExprKind, LangItem, MatchSource}; +use rustc_hir::{AssignOpKind, Expr, ExprKind, LangItem, MatchSource}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::sym; @@ -77,7 +77,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatPushString { return; } }, - ExprKind::AssignOp(op, left, arg) if op.node == BinOpKind::Add && is_string(cx, left) => arg, + ExprKind::AssignOp(op, left, arg) if op.node == AssignOpKind::AddAssign && is_string(cx, left) => arg, _ => return, }; if is_format(cx, arg) { diff --git a/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs b/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs index 41d2b18803d95..185fc2aa2d4ac 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs @@ -5,7 +5,7 @@ use clippy_utils::source::snippet_with_context; use rustc_ast::ast::{LitIntType, LitKind}; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Block, Expr, ExprKind, Stmt, StmtKind}; +use rustc_hir::{AssignOpKind, BinOpKind, Block, Expr, ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::{IntTy, Ty, UintTy}; use rustc_session::declare_lint_pass; @@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd { && ex.span.ctxt() == ctxt && expr1.span.ctxt() == ctxt && clippy_utils::SpanlessEq::new(cx).eq_expr(l, target) - && BinOpKind::Add == op1.node + && AssignOpKind::AddAssign == op1.node && let ExprKind::Lit(lit) = value.kind && let LitKind::Int(Pu128(1), LitIntType::Unsuffixed) = lit.node && block.expr.is_none() diff --git a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs index cbc3e2ccd5b87..5d7d3ae3f24b7 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs @@ -8,7 +8,7 @@ use clippy_utils::{ use rustc_ast::ast::LitKind; use rustc_data_structures::packed::Pu128; use rustc_errors::Applicability; -use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath}; +use rustc_hir::{AssignOpKind, BinOp, BinOpKind, Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::Span; @@ -366,7 +366,7 @@ fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a Exp match peel_blocks_with_stmt(expr).kind { ExprKind::AssignOp(ref op1, target, value) => { // Check if literal being subtracted is one - (BinOpKind::Sub == op1.node && is_integer_literal(value, 1)).then_some(target) + (AssignOpKind::SubAssign == op1.node && is_integer_literal(value, 1)).then_some(target) }, ExprKind::Assign(target, value, _) => { if let ExprKind::Binary(ref op1, left1, right1) = value.kind diff --git a/src/tools/clippy/clippy_lints/src/loops/utils.rs b/src/tools/clippy/clippy_lints/src/loops/utils.rs index a5185d38e7c33..3c6fbef2bda8c 100644 --- a/src/tools/clippy/clippy_lints/src/loops/utils.rs +++ b/src/tools/clippy/clippy_lints/src/loops/utils.rs @@ -3,7 +3,9 @@ use clippy_utils::{get_parent_expr, is_integer_const, path_to_local, path_to_loc use rustc_ast::ast::{LitIntType, LitKind}; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_expr, walk_local}; -use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, HirIdMap, LetStmt, Mutability, PatKind}; +use rustc_hir::{ + AssignOpKind, BorrowKind, Expr, ExprKind, HirId, HirIdMap, LetStmt, Mutability, PatKind +}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, Ty}; @@ -58,7 +60,7 @@ impl<'tcx> Visitor<'tcx> for IncrementVisitor<'_, 'tcx> { match parent.kind { ExprKind::AssignOp(op, lhs, rhs) => { if lhs.hir_id == expr.hir_id { - *state = if op.node == BinOpKind::Add + *state = if op.node == AssignOpKind::AddAssign && is_integer_const(self.cx, rhs, 1) && *state == IncrementVisitorVarState::Initial && self.depth == 0 diff --git a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs index d78299fe08be8..cdd6f4e5b033a 100644 --- a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs +++ b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs @@ -10,7 +10,7 @@ use rustc_ast::{LitKind, RangeLimits}; use rustc_data_structures::packed::Pu128; use rustc_data_structures::unhash::UnindexMap; use rustc_errors::{Applicability, Diag}; -use rustc_hir::{BinOp, Block, Body, Expr, ExprKind, UnOp}; +use rustc_hir::{BinOpKind, Block, Body, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; use rustc_span::source_map::Spanned; @@ -97,7 +97,7 @@ enum LengthComparison { /// /// E.g. for `v.len() > 5` this returns `Some((LengthComparison::IntLessThanLength, 5, v.len()))` fn len_comparison<'hir>( - bin_op: BinOp, + bin_op: BinOpKind, left: &'hir Expr<'hir>, right: &'hir Expr<'hir>, ) -> Option<(LengthComparison, usize, &'hir Expr<'hir>)> { @@ -112,7 +112,7 @@ fn len_comparison<'hir>( // normalize comparison, `v.len() > 4` becomes `4 < v.len()` // this simplifies the logic a bit - let (op, left, right) = normalize_comparison(bin_op.node, left, right)?; + let (op, left, right) = normalize_comparison(bin_op, left, right)?; match (op, left.kind, right.kind) { (Rel::Lt, int_lit_pat!(left), _) => Some((LengthComparison::IntLessThanLength, left as usize, right)), (Rel::Lt, _, int_lit_pat!(right)) => Some((LengthComparison::LengthLessThanInt, right as usize, left)), @@ -138,7 +138,7 @@ fn assert_len_expr<'hir>( && let ExprKind::Unary(UnOp::Not, condition) = &cond.kind && let ExprKind::Binary(bin_op, left, right) = &condition.kind - && let Some((cmp, asserted_len, slice_len)) = len_comparison(*bin_op, left, right) + && let Some((cmp, asserted_len, slice_len)) = len_comparison(bin_op.node, left, right) && let ExprKind::MethodCall(method, recv, [], _) = &slice_len.kind && cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_slice() && method.ident.name == sym::len diff --git a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs index be728e6c8b74b..fbd287f528544 100644 --- a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs +++ b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs @@ -261,10 +261,11 @@ fn check_expr<'tcx>(vis: &mut ReadVisitor<'_, 'tcx>, expr: &'tcx Expr<'_>) -> St | ExprKind::Assign(..) | ExprKind::Index(..) | ExprKind::Repeat(_, _) - | ExprKind::Struct(_, _, _) => { + | ExprKind::Struct(_, _, _) + | ExprKind::AssignOp(_, _, _) => { walk_expr(vis, expr); }, - ExprKind::Binary(op, _, _) | ExprKind::AssignOp(op, _, _) => { + ExprKind::Binary(op, _, _) => { if op.node == BinOpKind::And || op.node == BinOpKind::Or { // x && y and x || y always evaluate x first, so these are // strictly sequenced. 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 index 08fcd17e55558..a78a342d4fe39 100644 --- a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -335,9 +335,12 @@ impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects { return; } match &expr.kind { - hir::ExprKind::AssignOp(op, lhs, rhs) | hir::ExprKind::Binary(op, lhs, rhs) => { + hir::ExprKind::Binary(op, lhs, rhs) => { self.manage_bin_ops(cx, expr, op.node, lhs, rhs); }, + hir::ExprKind::AssignOp(op, lhs, rhs) => { + self.manage_bin_ops(cx, expr, op.node.into(), lhs, rhs); + }, hir::ExprKind::MethodCall(ps, receiver, args, _) => { self.manage_method_call(args, cx, expr, ps, receiver); }, diff --git a/src/tools/clippy/clippy_lints/src/operators/mod.rs b/src/tools/clippy/clippy_lints/src/operators/mod.rs index f758d08d36633..d32c062cf56a7 100644 --- a/src/tools/clippy/clippy_lints/src/operators/mod.rs +++ b/src/tools/clippy/clippy_lints/src/operators/mod.rs @@ -913,9 +913,10 @@ impl<'tcx> LateLintPass<'tcx> for Operators { ); }, ExprKind::AssignOp(op, lhs, rhs) => { - self.arithmetic_context.check_binary(cx, e, op.node, lhs, rhs); - misrefactored_assign_op::check(cx, e, op.node, lhs, rhs); - modulo_arithmetic::check(cx, e, op.node, lhs, rhs, false); + let bin_op = op.node.into(); + 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); }, ExprKind::Assign(lhs, rhs, _) => { assign_op_pattern::check(cx, e, lhs, rhs); diff --git a/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs b/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs index fb426e91bf01d..56bd8fefdb459 100644 --- a/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs +++ b/src/tools/clippy/clippy_lints/src/suspicious_trait_impl.rs @@ -5,6 +5,7 @@ use core::ops::ControlFlow; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -56,8 +57,27 @@ declare_lint_pass!(SuspiciousImpl => [SUSPICIOUS_ARITHMETIC_IMPL, SUSPICIOUS_OP_ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if let hir::ExprKind::Binary(binop, _, _) | hir::ExprKind::AssignOp(binop, ..) = expr.kind - && let Some((binop_trait_lang, op_assign_trait_lang)) = binop_traits(binop.node) + match expr.kind { + hir::ExprKind::Binary(op, _, _) => { + self.check_expr_inner(cx, expr, op.node, op.span); + } + hir::ExprKind::AssignOp(op, _, _) => { + self.check_expr_inner(cx, expr, op.node.into(), op.span); + } + _ => {} + } + } +} + +impl<'tcx> SuspiciousImpl { + fn check_expr_inner( + &mut self, + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'_>, + binop: hir::BinOpKind, + span: Span, + ) { + if let Some((binop_trait_lang, op_assign_trait_lang)) = binop_traits(binop) && let Some(binop_trait_id) = cx.tcx.lang_items().get(binop_trait_lang) && let Some(op_assign_trait_id) = cx.tcx.lang_items().get(op_assign_trait_lang) @@ -82,10 +102,10 @@ impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl { span_lint( cx, lint, - binop.span, + span, format!( "suspicious use of `{}` in `{}` impl", - binop.node.as_str(), + binop.as_str(), cx.tcx.item_name(trait_id) ), ); diff --git a/src/tools/clippy/clippy_lints/src/swap.rs b/src/tools/clippy/clippy_lints/src/swap.rs index 7176d533b6164..0337b74b4b124 100644 --- a/src/tools/clippy/clippy_lints/src/swap.rs +++ b/src/tools/clippy/clippy_lints/src/swap.rs @@ -10,7 +10,7 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, Block, Expr, ExprKind, LetStmt, PatKind, QPath, Stmt, StmtKind}; +use rustc_hir::{AssignOpKind, Block, Expr, ExprKind, LetStmt, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty; use rustc_session::declare_lint_pass; @@ -307,7 +307,7 @@ fn extract_sides_of_xor_assign<'a, 'hir>( if let StmtKind::Semi(expr) = stmt.kind && let ExprKind::AssignOp( Spanned { - node: BinOpKind::BitXor, + node: AssignOpKind::BitXorAssign, .. }, lhs, diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index dd149c4a29b9f..17751e824c021 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -15,7 +15,7 @@ use rustc_apfloat::ieee::{Half, Quad}; use rustc_ast::ast::{self, LitFloatType, LitKind}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{ - BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, PatExpr, PatExprKind, QPath, UnOp, + BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, PatExpr, PatExprKind, QPath, UnOp, }; use rustc_lexer::tokenize; use rustc_lint::LateContext; @@ -506,7 +506,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { UnOp::Deref => Some(if let Constant::Ref(r) = o { *r } else { o }), }), ExprKind::If(cond, then, ref otherwise) => self.ifthenelse(cond, then, *otherwise), - ExprKind::Binary(op, left, right) => self.binop(op, left, right), + ExprKind::Binary(op, left, right) => self.binop(op.node, left, right), ExprKind::Call(callee, []) => { // We only handle a few const functions for now. if let ExprKind::Path(qpath) = &callee.kind @@ -744,7 +744,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } } - fn binop(&self, op: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> Option> { + fn binop(&self, op: BinOpKind, left: &Expr<'_>, right: &Expr<'_>) -> Option> { let l = self.expr(left)?; let r = self.expr(right); match (l, r) { @@ -757,7 +757,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { // Using / or %, where the left-hand argument is the smallest integer of a signed integer type and // the right-hand argument is -1 always panics, even with overflow-checks disabled - if let BinOpKind::Div | BinOpKind::Rem = op.node + if let BinOpKind::Div | BinOpKind::Rem = op && l == ty_min_value && r == -1 { @@ -765,7 +765,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { } let zext = |n: i128| Constant::Int(unsext(self.tcx, n, ity)); - match op.node { + match op { // When +, * or binary - create a value greater than the maximum value, or less than // the minimum value that can be stored, it panics. BinOpKind::Add => l.checked_add(r).and_then(|n| ity.ensure_fits(n)).map(zext), @@ -792,7 +792,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { ty::Uint(ity) => { let bits = ity.bits(); - match op.node { + match op { BinOpKind::Add => l.checked_add(r).and_then(|n| ity.ensure_fits(n)).map(Constant::Int), BinOpKind::Sub => l.checked_sub(r).and_then(|n| ity.ensure_fits(n)).map(Constant::Int), BinOpKind::Mul => l.checked_mul(r).and_then(|n| ity.ensure_fits(n)).map(Constant::Int), @@ -815,7 +815,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { _ => None, }, // FIXME(f16_f128): add these types when binary operations are available on all platforms - (Constant::F32(l), Some(Constant::F32(r))) => match op.node { + (Constant::F32(l), Some(Constant::F32(r))) => match op { BinOpKind::Add => Some(Constant::F32(l + r)), BinOpKind::Sub => Some(Constant::F32(l - r)), BinOpKind::Mul => Some(Constant::F32(l * r)), @@ -829,7 +829,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { BinOpKind::Gt => Some(Constant::Bool(l > r)), _ => None, }, - (Constant::F64(l), Some(Constant::F64(r))) => match op.node { + (Constant::F64(l), Some(Constant::F64(r))) => match op { BinOpKind::Add => Some(Constant::F64(l + r)), BinOpKind::Sub => Some(Constant::F64(l - r)), BinOpKind::Mul => Some(Constant::F64(l * r)), @@ -843,7 +843,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { BinOpKind::Gt => Some(Constant::Bool(l > r)), _ => None, }, - (l, r) => match (op.node, l, r) { + (l, r) => match (op, l, r) { (BinOpKind::And, Constant::Bool(false), _) => Some(Constant::Bool(false)), (BinOpKind::Or, Constant::Bool(true), _) => Some(Constant::Bool(true)), (BinOpKind::And, Constant::Bool(true), Some(r)) | (BinOpKind::Or, Constant::Bool(false), Some(r)) => { diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index 065d48119a24f..e92c0c79b2556 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -357,7 +357,7 @@ fn binop_to_string(op: AssocOp, lhs: &str, rhs: &str) -> String { match op { AssocOp::Binary(op) => format!("{lhs} {} {rhs}", op.as_str()), AssocOp::Assign => format!("{lhs} = {rhs}"), - AssocOp::AssignOp(op) => format!("{lhs} {}= {rhs}", op.as_str()), + AssocOp::AssignOp(op) => format!("{lhs} {} {rhs}", op.as_str()), AssocOp::Cast => format!("{lhs} as {rhs}"), AssocOp::Range(limits) => format!("{lhs}{}{rhs}", limits.as_str()), } diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs index 65120770edd61..be6b483bfff1f 100644 --- a/src/tools/rustfmt/src/expr.rs +++ b/src/tools/rustfmt/src/expr.rs @@ -2058,7 +2058,7 @@ fn rewrite_assignment( context: &RewriteContext<'_>, lhs: &ast::Expr, rhs: &ast::Expr, - op: Option<&ast::BinOp>, + op: Option<&ast::AssignOp>, shape: Shape, ) -> RewriteResult { let operator_str = match op {