Rollup merge of #138017 - nnethercote:tighten-assignment-op, r=spastorino
Tighten up assignment operator representations. This is step 3 of [MCP 831](https://github.com/rust-lang/compiler-team/issues/831). r? `@spastorino`
This commit is contained in:
commit
e5c7451a10
29 changed files with 471 additions and 314 deletions
|
@ -981,6 +981,75 @@ impl BinOpKind {
|
||||||
|
|
||||||
pub type BinOp = Spanned<BinOpKind>;
|
pub type BinOp = Spanned<BinOpKind>;
|
||||||
|
|
||||||
|
// 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<AssignOpKind> 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<AssignOpKind>;
|
||||||
|
|
||||||
/// Unary operator.
|
/// Unary operator.
|
||||||
///
|
///
|
||||||
/// Note that `&data` is not an operator, it's an `AddrOf` expression.
|
/// Note that `&data` is not an operator, it's an `AddrOf` expression.
|
||||||
|
@ -1593,7 +1662,7 @@ pub enum ExprKind {
|
||||||
/// An assignment with an operator.
|
/// An assignment with an operator.
|
||||||
///
|
///
|
||||||
/// E.g., `a += 1`.
|
/// E.g., `a += 1`.
|
||||||
AssignOp(BinOp, P<Expr>, P<Expr>),
|
AssignOp(AssignOp, P<Expr>, P<Expr>),
|
||||||
/// Access of a named (e.g., `obj.foo`) or unnamed (e.g., `obj.0`) struct field.
|
/// Access of a named (e.g., `obj.foo`) or unnamed (e.g., `obj.0`) struct field.
|
||||||
Field(P<Expr>, Ident),
|
Field(P<Expr>, Ident),
|
||||||
/// An indexing operation (e.g., `foo[2]`).
|
/// An indexing operation (e.g., `foo[2]`).
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use rustc_span::kw;
|
use rustc_span::kw;
|
||||||
|
|
||||||
use crate::ast::{self, BinOpKind, RangeLimits};
|
use crate::ast::{self, AssignOpKind, BinOpKind, RangeLimits};
|
||||||
use crate::token::{self, Token};
|
use crate::token::{self, Token};
|
||||||
|
|
||||||
/// Associative operator.
|
/// Associative operator.
|
||||||
|
@ -9,7 +9,7 @@ pub enum AssocOp {
|
||||||
/// A binary op.
|
/// A binary op.
|
||||||
Binary(BinOpKind),
|
Binary(BinOpKind),
|
||||||
/// `?=` where ? is one of the assignable BinOps
|
/// `?=` where ? is one of the assignable BinOps
|
||||||
AssignOp(BinOpKind),
|
AssignOp(AssignOpKind),
|
||||||
/// `=`
|
/// `=`
|
||||||
Assign,
|
Assign,
|
||||||
/// `as`
|
/// `as`
|
||||||
|
@ -44,16 +44,16 @@ impl AssocOp {
|
||||||
token::Or => Some(Binary(BinOpKind::BitOr)),
|
token::Or => Some(Binary(BinOpKind::BitOr)),
|
||||||
token::Shl => Some(Binary(BinOpKind::Shl)),
|
token::Shl => Some(Binary(BinOpKind::Shl)),
|
||||||
token::Shr => Some(Binary(BinOpKind::Shr)),
|
token::Shr => Some(Binary(BinOpKind::Shr)),
|
||||||
token::PlusEq => Some(AssignOp(BinOpKind::Add)),
|
token::PlusEq => Some(AssignOp(AssignOpKind::AddAssign)),
|
||||||
token::MinusEq => Some(AssignOp(BinOpKind::Sub)),
|
token::MinusEq => Some(AssignOp(AssignOpKind::SubAssign)),
|
||||||
token::StarEq => Some(AssignOp(BinOpKind::Mul)),
|
token::StarEq => Some(AssignOp(AssignOpKind::MulAssign)),
|
||||||
token::SlashEq => Some(AssignOp(BinOpKind::Div)),
|
token::SlashEq => Some(AssignOp(AssignOpKind::DivAssign)),
|
||||||
token::PercentEq => Some(AssignOp(BinOpKind::Rem)),
|
token::PercentEq => Some(AssignOp(AssignOpKind::RemAssign)),
|
||||||
token::CaretEq => Some(AssignOp(BinOpKind::BitXor)),
|
token::CaretEq => Some(AssignOp(AssignOpKind::BitXorAssign)),
|
||||||
token::AndEq => Some(AssignOp(BinOpKind::BitAnd)),
|
token::AndEq => Some(AssignOp(AssignOpKind::BitAndAssign)),
|
||||||
token::OrEq => Some(AssignOp(BinOpKind::BitOr)),
|
token::OrEq => Some(AssignOp(AssignOpKind::BitOrAssign)),
|
||||||
token::ShlEq => Some(AssignOp(BinOpKind::Shl)),
|
token::ShlEq => Some(AssignOp(AssignOpKind::ShlAssign)),
|
||||||
token::ShrEq => Some(AssignOp(BinOpKind::Shr)),
|
token::ShrEq => Some(AssignOp(AssignOpKind::ShrAssign)),
|
||||||
token::Lt => Some(Binary(BinOpKind::Lt)),
|
token::Lt => Some(Binary(BinOpKind::Lt)),
|
||||||
token::Le => Some(Binary(BinOpKind::Le)),
|
token::Le => Some(Binary(BinOpKind::Le)),
|
||||||
token::Ge => Some(Binary(BinOpKind::Ge)),
|
token::Ge => Some(Binary(BinOpKind::Ge)),
|
||||||
|
|
|
@ -274,7 +274,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||||
}
|
}
|
||||||
ExprKind::Assign(el, er, span) => self.lower_expr_assign(el, er, *span, e.span),
|
ExprKind::Assign(el, er, span) => self.lower_expr_assign(el, er, *span, e.span),
|
||||||
ExprKind::AssignOp(op, el, er) => hir::ExprKind::AssignOp(
|
ExprKind::AssignOp(op, el, er) => hir::ExprKind::AssignOp(
|
||||||
self.lower_binop(*op),
|
self.lower_assign_op(*op),
|
||||||
self.lower_expr(el),
|
self.lower_expr(el),
|
||||||
self.lower_expr(er),
|
self.lower_expr(er),
|
||||||
),
|
),
|
||||||
|
@ -443,6 +443,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||||
Spanned { node: b.node, span: self.lower_span(b.span) }
|
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(
|
fn lower_legacy_const_generics(
|
||||||
&mut self,
|
&mut self,
|
||||||
mut f: Expr,
|
mut f: Expr,
|
||||||
|
|
|
@ -274,22 +274,22 @@ impl<'a> State<'a> {
|
||||||
|
|
||||||
fn print_expr_binary(
|
fn print_expr_binary(
|
||||||
&mut self,
|
&mut self,
|
||||||
op: ast::BinOp,
|
op: ast::BinOpKind,
|
||||||
lhs: &ast::Expr,
|
lhs: &ast::Expr,
|
||||||
rhs: &ast::Expr,
|
rhs: &ast::Expr,
|
||||||
fixup: FixupContext,
|
fixup: FixupContext,
|
||||||
) {
|
) {
|
||||||
let binop_prec = op.node.precedence();
|
let binop_prec = op.precedence();
|
||||||
let left_prec = lhs.precedence();
|
let left_prec = lhs.precedence();
|
||||||
let right_prec = rhs.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::Left => (left_prec < binop_prec, right_prec <= binop_prec),
|
||||||
Fixity::Right => (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),
|
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
|
// 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
|
// 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.
|
// 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.print_expr_cond_paren(lhs, left_needs_paren, fixup.leftmost_subexpression());
|
||||||
self.space();
|
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());
|
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);
|
self.print_expr_method_call(seg, receiver, args, fixup);
|
||||||
}
|
}
|
||||||
ast::ExprKind::Binary(op, lhs, rhs) => {
|
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) => {
|
ast::ExprKind::Unary(op, expr) => {
|
||||||
self.print_expr_unary(*op, expr, fixup);
|
self.print_expr_unary(*op, expr, fixup);
|
||||||
|
@ -605,8 +605,7 @@ impl<'a> State<'a> {
|
||||||
fixup.leftmost_subexpression(),
|
fixup.leftmost_subexpression(),
|
||||||
);
|
);
|
||||||
self.space();
|
self.space();
|
||||||
self.word(op.node.as_str());
|
self.word_space(op.node.as_str());
|
||||||
self.word_space("=");
|
|
||||||
self.print_expr_cond_paren(
|
self.print_expr_cond_paren(
|
||||||
rhs,
|
rhs,
|
||||||
rhs.precedence() < ExprPrecedence::Assign,
|
rhs.precedence() < ExprPrecedence::Assign,
|
||||||
|
|
|
@ -10,9 +10,9 @@ use rustc_ast::{
|
||||||
LitKind, TraitObjectSyntax, UintTy, UnsafeBinderCastKind,
|
LitKind, TraitObjectSyntax, UintTy, UnsafeBinderCastKind,
|
||||||
};
|
};
|
||||||
pub use rustc_ast::{
|
pub use rustc_ast::{
|
||||||
AttrId, AttrStyle, BinOp, BinOpKind, BindingMode, BorrowKind, BoundConstness, BoundPolarity,
|
AssignOp, AssignOpKind, AttrId, AttrStyle, BinOp, BinOpKind, BindingMode, BorrowKind,
|
||||||
ByRef, CaptureBy, DelimArgs, ImplPolarity, IsAuto, MetaItemInner, MetaItemLit, Movability,
|
BoundConstness, BoundPolarity, ByRef, CaptureBy, DelimArgs, ImplPolarity, IsAuto,
|
||||||
Mutability, UnOp,
|
MetaItemInner, MetaItemLit, Movability, Mutability, UnOp,
|
||||||
};
|
};
|
||||||
use rustc_attr_data_structures::AttributeKind;
|
use rustc_attr_data_structures::AttributeKind;
|
||||||
use rustc_data_structures::fingerprint::Fingerprint;
|
use rustc_data_structures::fingerprint::Fingerprint;
|
||||||
|
@ -2648,7 +2648,7 @@ pub enum ExprKind<'hir> {
|
||||||
/// An assignment with an operator.
|
/// An assignment with an operator.
|
||||||
///
|
///
|
||||||
/// E.g., `a += 1`.
|
/// 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.
|
/// Access of a named (e.g., `obj.foo`) or unnamed (e.g., `obj.0`) struct or tuple field.
|
||||||
Field(&'hir Expr<'hir>, Ident),
|
Field(&'hir Expr<'hir>, Ident),
|
||||||
/// An indexing operation (`foo[2]`).
|
/// An indexing operation (`foo[2]`).
|
||||||
|
|
|
@ -1271,18 +1271,18 @@ impl<'a> State<'a> {
|
||||||
self.print_call_post(base_args)
|
self.print_call_post(base_args)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_expr_binary(&mut self, op: hir::BinOp, lhs: &hir::Expr<'_>, rhs: &hir::Expr<'_>) {
|
fn print_expr_binary(&mut self, op: hir::BinOpKind, lhs: &hir::Expr<'_>, rhs: &hir::Expr<'_>) {
|
||||||
let binop_prec = op.node.precedence();
|
let binop_prec = op.precedence();
|
||||||
let left_prec = lhs.precedence();
|
let left_prec = lhs.precedence();
|
||||||
let right_prec = rhs.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::Left => (left_prec < binop_prec, right_prec <= binop_prec),
|
||||||
Fixity::Right => (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),
|
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
|
// 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
|
// 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.
|
// 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.print_expr_cond_paren(lhs, left_needs_paren);
|
||||||
self.space();
|
self.space();
|
||||||
self.word_space(op.node.as_str());
|
self.word_space(op.as_str());
|
||||||
self.print_expr_cond_paren(rhs, right_needs_paren);
|
self.print_expr_cond_paren(rhs, right_needs_paren);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1451,7 +1451,7 @@ impl<'a> State<'a> {
|
||||||
self.word(".use");
|
self.word(".use");
|
||||||
}
|
}
|
||||||
hir::ExprKind::Binary(op, lhs, rhs) => {
|
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) => {
|
hir::ExprKind::Unary(op, expr) => {
|
||||||
self.print_expr_unary(op, expr);
|
self.print_expr_unary(op, expr);
|
||||||
|
@ -1572,8 +1572,7 @@ impl<'a> State<'a> {
|
||||||
hir::ExprKind::AssignOp(op, lhs, rhs) => {
|
hir::ExprKind::AssignOp(op, lhs, rhs) => {
|
||||||
self.print_expr_cond_paren(lhs, lhs.precedence() <= ExprPrecedence::Assign);
|
self.print_expr_cond_paren(lhs, lhs.precedence() <= ExprPrecedence::Assign);
|
||||||
self.space();
|
self.space();
|
||||||
self.word(op.node.as_str());
|
self.word_space(op.node.as_str());
|
||||||
self.word_space("=");
|
|
||||||
self.print_expr_cond_paren(rhs, rhs.precedence() < ExprPrecedence::Assign);
|
self.print_expr_cond_paren(rhs, rhs.precedence() < ExprPrecedence::Assign);
|
||||||
}
|
}
|
||||||
hir::ExprKind::Field(expr, ident) => {
|
hir::ExprKind::Field(expr, ident) => {
|
||||||
|
|
|
@ -512,7 +512,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
self.check_expr_assign(expr, expected, lhs, rhs, span)
|
self.check_expr_assign(expr, expected, lhs, rhs, span)
|
||||||
}
|
}
|
||||||
ExprKind::AssignOp(op, lhs, rhs) => {
|
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::Unary(unop, oprnd) => self.check_expr_unop(unop, oprnd, expected, expr),
|
||||||
ExprKind::AddrOf(kind, mutbl, oprnd) => {
|
ExprKind::AddrOf(kind, mutbl, oprnd) => {
|
||||||
|
|
|
@ -3477,30 +3477,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
lhs_ty: Ty<'tcx>,
|
lhs_ty: Ty<'tcx>,
|
||||||
rhs_expr: &'tcx hir::Expr<'tcx>,
|
rhs_expr: &'tcx hir::Expr<'tcx>,
|
||||||
lhs_expr: &'tcx hir::Expr<'tcx>,
|
lhs_expr: &'tcx hir::Expr<'tcx>,
|
||||||
op: hir::BinOp,
|
|
||||||
) {
|
) {
|
||||||
match op.node {
|
if let Some(partial_eq_def_id) = self.infcx.tcx.lang_items().eq_trait()
|
||||||
hir::BinOpKind::Eq => {
|
&& self
|
||||||
if let Some(partial_eq_def_id) = self.infcx.tcx.lang_items().eq_trait()
|
.infcx
|
||||||
&& self
|
.type_implements_trait(partial_eq_def_id, [rhs_ty, lhs_ty], self.param_env)
|
||||||
.infcx
|
.must_apply_modulo_regions()
|
||||||
.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 sm = self.tcx.sess.source_map();
|
&& let Ok(lhs_snippet) = sm.span_to_snippet(lhs_expr.span)
|
||||||
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(
|
||||||
err.note(format!("`{rhs_ty}` implements `PartialEq<{lhs_ty}>`"));
|
"consider swapping the equality",
|
||||||
err.multipart_suggestion(
|
vec![(lhs_expr.span, rhs_snippet), (rhs_expr.span, lhs_snippet)],
|
||||||
"consider swapping the equality",
|
Applicability::MaybeIncorrect,
|
||||||
vec![(lhs_expr.span, rhs_snippet), (rhs_expr.span, lhs_snippet)],
|
);
|
||||||
Applicability::MaybeIncorrect,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,15 +4,15 @@ use rustc_data_structures::packed::Pu128;
|
||||||
use rustc_errors::codes::*;
|
use rustc_errors::codes::*;
|
||||||
use rustc_errors::{Applicability, Diag, struct_span_code_err};
|
use rustc_errors::{Applicability, Diag, struct_span_code_err};
|
||||||
use rustc_infer::traits::ObligationCauseCode;
|
use rustc_infer::traits::ObligationCauseCode;
|
||||||
|
use rustc_middle::bug;
|
||||||
use rustc_middle::ty::adjustment::{
|
use rustc_middle::ty::adjustment::{
|
||||||
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
|
Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability,
|
||||||
};
|
};
|
||||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||||
use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt, TypeVisitableExt};
|
use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt, TypeVisitableExt};
|
||||||
use rustc_middle::{bug, span_bug};
|
|
||||||
use rustc_session::errors::ExprParenthesesNeeded;
|
use rustc_session::errors::ExprParenthesesNeeded;
|
||||||
use rustc_span::source_map::Spanned;
|
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::infer::InferCtxtExt;
|
||||||
use rustc_trait_selection::traits::{FulfillmentError, Obligation, ObligationCtxt};
|
use rustc_trait_selection::traits::{FulfillmentError, Obligation, ObligationCtxt};
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
@ -24,24 +24,27 @@ use crate::Expectation;
|
||||||
|
|
||||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
/// Checks a `a <op>= b`
|
/// Checks a `a <op>= b`
|
||||||
pub(crate) fn check_expr_binop_assign(
|
pub(crate) fn check_expr_assign_op(
|
||||||
&self,
|
&self,
|
||||||
expr: &'tcx hir::Expr<'tcx>,
|
expr: &'tcx hir::Expr<'tcx>,
|
||||||
op: hir::BinOp,
|
op: hir::AssignOp,
|
||||||
lhs: &'tcx hir::Expr<'tcx>,
|
lhs: &'tcx hir::Expr<'tcx>,
|
||||||
rhs: &'tcx hir::Expr<'tcx>,
|
rhs: &'tcx hir::Expr<'tcx>,
|
||||||
expected: Expectation<'tcx>,
|
expected: Expectation<'tcx>,
|
||||||
) -> Ty<'tcx> {
|
) -> Ty<'tcx> {
|
||||||
let (lhs_ty, rhs_ty, return_ty) =
|
let (lhs_ty, rhs_ty, return_ty) =
|
||||||
self.check_overloaded_binop(expr, lhs, rhs, op, IsAssign::Yes, expected);
|
self.check_overloaded_binop(expr, lhs, rhs, Op::AssignOp(op), expected);
|
||||||
|
|
||||||
let ty =
|
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) {
|
let ty = if !lhs_ty.is_ty_var()
|
||||||
self.enforce_builtin_binop_types(lhs.span, lhs_ty, rhs.span, rhs_ty, op);
|
&& !rhs_ty.is_ty_var()
|
||||||
self.tcx.types.unit
|
&& is_builtin_binop(lhs_ty, rhs_ty, category)
|
||||||
} else {
|
{
|
||||||
return_ty
|
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| {
|
self.check_lhs_assignable(lhs, E0067, op.span, |err| {
|
||||||
if let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) {
|
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(
|
.lookup_op_method(
|
||||||
(lhs, lhs_deref_ty),
|
(lhs, lhs_deref_ty),
|
||||||
Some((rhs, rhs_ty)),
|
Some((rhs, rhs_ty)),
|
||||||
Op::Binary(op, IsAssign::Yes),
|
lang_item_for_binop(self.tcx, Op::AssignOp(op)),
|
||||||
|
op.span,
|
||||||
expected,
|
expected,
|
||||||
)
|
)
|
||||||
.is_ok()
|
.is_ok()
|
||||||
|
@ -60,7 +64,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
.lookup_op_method(
|
.lookup_op_method(
|
||||||
(lhs, lhs_ty),
|
(lhs, lhs_ty),
|
||||||
Some((rhs, rhs_ty)),
|
Some((rhs, rhs_ty)),
|
||||||
Op::Binary(op, IsAssign::Yes),
|
lang_item_for_binop(self.tcx, Op::AssignOp(op)),
|
||||||
|
op.span,
|
||||||
expected,
|
expected,
|
||||||
)
|
)
|
||||||
.is_err()
|
.is_err()
|
||||||
|
@ -98,7 +103,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
expr.hir_id, expr, op, lhs_expr, rhs_expr
|
expr.hir_id, expr, op, lhs_expr, rhs_expr
|
||||||
);
|
);
|
||||||
|
|
||||||
match BinOpCategory::from(op) {
|
match BinOpCategory::from(op.node) {
|
||||||
BinOpCategory::Shortcircuit => {
|
BinOpCategory::Shortcircuit => {
|
||||||
// && and || are a simple case.
|
// && and || are a simple case.
|
||||||
self.check_expr_coercible_to_type(lhs_expr, tcx.types.bool, None);
|
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
|
// Otherwise, we always treat operators as if they are
|
||||||
// overloaded. This is the way to be most flexible w/r/t
|
// overloaded. This is the way to be most flexible w/r/t
|
||||||
// types that get inferred.
|
// types that get inferred.
|
||||||
let (lhs_ty, rhs_ty, return_ty) = self.check_overloaded_binop(
|
let (lhs_ty, rhs_ty, return_ty) =
|
||||||
expr,
|
self.check_overloaded_binop(expr, lhs_expr, rhs_expr, Op::BinOp(op), expected);
|
||||||
lhs_expr,
|
|
||||||
rhs_expr,
|
|
||||||
op,
|
|
||||||
IsAssign::No,
|
|
||||||
expected,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Supply type inference hints if relevant. Probably these
|
// Supply type inference hints if relevant. Probably these
|
||||||
// hints should be enforced during select as part of the
|
// 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
|
// deduce that the result type should be `u32`, even
|
||||||
// though we don't know yet what type 2 has and hence
|
// though we don't know yet what type 2 has and hence
|
||||||
// can't pin this down to a specific impl.
|
// can't pin this down to a specific impl.
|
||||||
|
let category = BinOpCategory::from(op.node);
|
||||||
if !lhs_ty.is_ty_var()
|
if !lhs_ty.is_ty_var()
|
||||||
&& !rhs_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(
|
let builtin_return_ty = self.enforce_builtin_binop_types(
|
||||||
lhs_expr.span,
|
lhs_expr.span,
|
||||||
lhs_ty,
|
lhs_ty,
|
||||||
rhs_expr.span,
|
rhs_expr.span,
|
||||||
rhs_ty,
|
rhs_ty,
|
||||||
op,
|
category,
|
||||||
);
|
);
|
||||||
self.demand_eqtype(expr.span, builtin_return_ty, return_ty);
|
self.demand_eqtype(expr.span, builtin_return_ty, return_ty);
|
||||||
builtin_return_ty
|
builtin_return_ty
|
||||||
|
@ -161,16 +161,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
lhs_ty: Ty<'tcx>,
|
lhs_ty: Ty<'tcx>,
|
||||||
rhs_span: Span,
|
rhs_span: Span,
|
||||||
rhs_ty: Ty<'tcx>,
|
rhs_ty: Ty<'tcx>,
|
||||||
op: hir::BinOp,
|
category: BinOpCategory,
|
||||||
) -> Ty<'tcx> {
|
) -> 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.
|
// 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.)
|
// (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 (lhs_ty, rhs_ty) = (deref_ty_if_possible(lhs_ty), deref_ty_if_possible(rhs_ty));
|
||||||
|
|
||||||
let tcx = self.tcx;
|
let tcx = self.tcx;
|
||||||
match BinOpCategory::from(op) {
|
match category {
|
||||||
BinOpCategory::Shortcircuit => {
|
BinOpCategory::Shortcircuit => {
|
||||||
self.demand_suptype(lhs_span, tcx.types.bool, lhs_ty);
|
self.demand_suptype(lhs_span, tcx.types.bool, lhs_ty);
|
||||||
self.demand_suptype(rhs_span, tcx.types.bool, rhs_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>,
|
expr: &'tcx hir::Expr<'tcx>,
|
||||||
lhs_expr: &'tcx hir::Expr<'tcx>,
|
lhs_expr: &'tcx hir::Expr<'tcx>,
|
||||||
rhs_expr: &'tcx hir::Expr<'tcx>,
|
rhs_expr: &'tcx hir::Expr<'tcx>,
|
||||||
op: hir::BinOp,
|
op: Op,
|
||||||
is_assign: IsAssign,
|
|
||||||
expected: Expectation<'tcx>,
|
expected: Expectation<'tcx>,
|
||||||
) -> (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>) {
|
) -> (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>) {
|
||||||
debug!(
|
debug!("check_overloaded_binop(expr.hir_id={}, op={:?})", expr.hir_id, op);
|
||||||
"check_overloaded_binop(expr.hir_id={}, op={:?}, is_assign={:?})",
|
|
||||||
expr.hir_id, op, is_assign
|
|
||||||
);
|
|
||||||
|
|
||||||
let lhs_ty = match is_assign {
|
let lhs_ty = match op {
|
||||||
IsAssign::No => {
|
Op::BinOp(_) => {
|
||||||
// Find a suitable supertype of the LHS expression's type, by coercing to
|
// 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
|
// a type variable, to pass as the `Self` to the trait, avoiding invariant
|
||||||
// trait matching creating lifetime constraints that are too strict.
|
// 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);
|
let fresh_var = self.next_ty_var(lhs_expr.span);
|
||||||
self.demand_coerce(lhs_expr, lhs_ty, fresh_var, Some(rhs_expr), AllowTwoPhase::No)
|
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
|
// rust-lang/rust#52126: We have to use strict
|
||||||
// equivalence on the LHS of an assign-op like `+=`;
|
// equivalence on the LHS of an assign-op like `+=`;
|
||||||
// overwritten or mutably-borrowed places cannot be
|
// overwritten or mutably-borrowed places cannot be
|
||||||
|
@ -242,7 +238,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
let result = self.lookup_op_method(
|
let result = self.lookup_op_method(
|
||||||
(lhs_expr, lhs_ty),
|
(lhs_expr, lhs_ty),
|
||||||
Some((rhs_expr, rhs_ty_var)),
|
Some((rhs_expr, rhs_ty_var)),
|
||||||
Op::Binary(op, is_assign),
|
lang_item_for_binop(self.tcx, op),
|
||||||
|
op.span(),
|
||||||
expected,
|
expected,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -252,15 +249,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
rhs_ty_var,
|
rhs_ty_var,
|
||||||
Some(lhs_expr),
|
Some(lhs_expr),
|
||||||
|err, ty| {
|
|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 rhs_ty = self.resolve_vars_with_obligations(rhs_ty);
|
||||||
|
|
||||||
let return_ty = match result {
|
let return_ty = match result {
|
||||||
Ok(method) => {
|
Ok(method) => {
|
||||||
let by_ref_binop = !op.node.is_by_value();
|
let by_ref_binop = !op.is_by_value();
|
||||||
if is_assign == IsAssign::Yes || by_ref_binop {
|
if matches!(op, Op::AssignOp(_)) || by_ref_binop {
|
||||||
if let ty::Ref(_, _, mutbl) = method.sig.inputs()[0].kind() {
|
if let ty::Ref(_, _, mutbl) = method.sig.inputs()[0].kind() {
|
||||||
let mutbl = AutoBorrowMutability::new(*mutbl, AllowTwoPhase::Yes);
|
let mutbl = AutoBorrowMutability::new(*mutbl, AllowTwoPhase::Yes);
|
||||||
let autoref = Adjustment {
|
let autoref = Adjustment {
|
||||||
|
@ -301,32 +302,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
Ty::new_misc_error(self.tcx)
|
Ty::new_misc_error(self.tcx)
|
||||||
}
|
}
|
||||||
Err(errors) => {
|
Err(errors) => {
|
||||||
let (_, trait_def_id) =
|
let (_, trait_def_id) = lang_item_for_binop(self.tcx, op);
|
||||||
lang_item_for_op(self.tcx, Op::Binary(op, is_assign), op.span);
|
|
||||||
let missing_trait = trait_def_id
|
let missing_trait = trait_def_id
|
||||||
.map(|def_id| with_no_trimmed_paths!(self.tcx.def_path_str(def_id)));
|
.map(|def_id| with_no_trimmed_paths!(self.tcx.def_path_str(def_id)));
|
||||||
let mut path = None;
|
let mut path = None;
|
||||||
let lhs_ty_str = self.tcx.short_string(lhs_ty, &mut path);
|
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 rhs_ty_str = self.tcx.short_string(rhs_ty, &mut path);
|
||||||
let (mut err, output_def_id) = match is_assign {
|
let (mut err, output_def_id) = match op {
|
||||||
IsAssign::Yes => {
|
Op::AssignOp(assign_op) => {
|
||||||
|
let s = assign_op.node.as_str();
|
||||||
let mut err = struct_span_code_err!(
|
let mut err = struct_span_code_err!(
|
||||||
self.dcx(),
|
self.dcx(),
|
||||||
expr.span,
|
expr.span,
|
||||||
E0368,
|
E0368,
|
||||||
"binary assignment operation `{}=` cannot be applied to type `{}`",
|
"binary assignment operation `{}` cannot be applied to type `{}`",
|
||||||
op.node.as_str(),
|
s,
|
||||||
lhs_ty_str,
|
lhs_ty_str,
|
||||||
);
|
);
|
||||||
err.span_label(
|
err.span_label(
|
||||||
lhs_expr.span,
|
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);
|
self.note_unmet_impls_on_type(&mut err, errors, false);
|
||||||
(err, None)
|
(err, None)
|
||||||
}
|
}
|
||||||
IsAssign::No => {
|
Op::BinOp(bin_op) => {
|
||||||
let message = match op.node {
|
let message = match bin_op.node {
|
||||||
hir::BinOpKind::Add => {
|
hir::BinOpKind::Add => {
|
||||||
format!("cannot add `{rhs_ty_str}` to `{lhs_ty_str}`")
|
format!("cannot add `{rhs_ty_str}` to `{lhs_ty_str}`")
|
||||||
}
|
}
|
||||||
|
@ -362,8 +363,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
_ => format!(
|
_ => format!(
|
||||||
"binary operation `{}` cannot be applied to type `{}`",
|
"binary operation `{}` cannot be applied to type `{}`",
|
||||||
op.node.as_str(),
|
bin_op.node.as_str(),
|
||||||
lhs_ty_str,
|
lhs_ty_str
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
let output_def_id = trait_def_id.and_then(|def_id| {
|
let output_def_id = trait_def_id.and_then(|def_id| {
|
||||||
|
@ -376,7 +377,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
.cloned()
|
.cloned()
|
||||||
});
|
});
|
||||||
let mut err =
|
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) {
|
if !lhs_expr.span.eq(&rhs_expr.span) {
|
||||||
err.span_label(lhs_expr.span, lhs_ty_str.clone());
|
err.span_label(lhs_expr.span, lhs_ty_str.clone());
|
||||||
err.span_label(rhs_expr.span, rhs_ty_str);
|
err.span_label(rhs_expr.span, rhs_ty_str);
|
||||||
|
@ -409,18 +410,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
.lookup_op_method(
|
.lookup_op_method(
|
||||||
(lhs_expr, lhs_deref_ty),
|
(lhs_expr, lhs_deref_ty),
|
||||||
Some((rhs_expr, rhs_ty)),
|
Some((rhs_expr, rhs_ty)),
|
||||||
Op::Binary(op, is_assign),
|
lang_item_for_binop(self.tcx, op),
|
||||||
|
op.span(),
|
||||||
expected,
|
expected,
|
||||||
)
|
)
|
||||||
.is_ok()
|
.is_ok()
|
||||||
{
|
{
|
||||||
let msg = format!(
|
let msg = format!(
|
||||||
"`{}{}` can be used on `{}` if you dereference the left-hand side",
|
"`{}` can be used on `{}` if you dereference the left-hand side",
|
||||||
op.node.as_str(),
|
op.as_str(),
|
||||||
match is_assign {
|
|
||||||
IsAssign::Yes => "=",
|
|
||||||
IsAssign::No => "",
|
|
||||||
},
|
|
||||||
self.tcx.short_string(lhs_deref_ty, err.long_ty_path()),
|
self.tcx.short_string(lhs_deref_ty, err.long_ty_path()),
|
||||||
);
|
);
|
||||||
err.span_suggestion_verbose(
|
err.span_suggestion_verbose(
|
||||||
|
@ -442,14 +440,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
.lookup_op_method(
|
.lookup_op_method(
|
||||||
(lhs_expr, lhs_adjusted_ty),
|
(lhs_expr, lhs_adjusted_ty),
|
||||||
Some((rhs_expr, rhs_adjusted_ty)),
|
Some((rhs_expr, rhs_adjusted_ty)),
|
||||||
Op::Binary(op, is_assign),
|
lang_item_for_binop(self.tcx, op),
|
||||||
|
op.span(),
|
||||||
expected,
|
expected,
|
||||||
)
|
)
|
||||||
.is_ok()
|
.is_ok()
|
||||||
{
|
{
|
||||||
let lhs = self.tcx.short_string(lhs_adjusted_ty, err.long_ty_path());
|
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 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"));
|
err.note(format!("an implementation for `{lhs} {op} {rhs}` exists"));
|
||||||
|
|
||||||
if let Some(lhs_new_mutbl) = lhs_new_mutbl
|
if let Some(lhs_new_mutbl) = lhs_new_mutbl
|
||||||
|
@ -499,7 +498,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
self.lookup_op_method(
|
self.lookup_op_method(
|
||||||
(lhs_expr, lhs_ty),
|
(lhs_expr, lhs_ty),
|
||||||
Some((rhs_expr, rhs_ty)),
|
Some((rhs_expr, rhs_ty)),
|
||||||
Op::Binary(op, is_assign),
|
lang_item_for_binop(self.tcx, op),
|
||||||
|
op.span(),
|
||||||
expected,
|
expected,
|
||||||
)
|
)
|
||||||
.is_ok()
|
.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
|
// We should suggest `a + b` => `*a + b` if `a` is copy, and suggest
|
||||||
// `a += b` => `*a += b` if a is a mut ref.
|
// `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
|
// 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)
|
&& let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty)
|
||||||
{
|
{
|
||||||
suggest_deref_binop(&mut err, lhs_deref_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()
|
&& let ty::Ref(region, lhs_deref_ty, mutbl) = lhs_ty.kind()
|
||||||
{
|
{
|
||||||
if self.type_is_copy_modulo_regions(self.param_env, *lhs_deref_ty) {
|
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 let Some(missing_trait) = missing_trait {
|
||||||
if op.node == hir::BinOpKind::Add
|
if matches!(
|
||||||
&& self.check_str_addition(
|
op,
|
||||||
lhs_expr, rhs_expr, lhs_ty, rhs_ty, &mut err, is_assign, 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
|
// This has nothing here because it means we did string
|
||||||
// concatenation (e.g., "Hello " + "World!"). This means
|
// concatenation (e.g., "Hello " + "World!"). This means
|
||||||
|
@ -592,7 +594,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
.lookup_op_method(
|
.lookup_op_method(
|
||||||
(lhs_expr, lhs_ty),
|
(lhs_expr, lhs_ty),
|
||||||
Some((rhs_expr, rhs_ty)),
|
Some((rhs_expr, rhs_ty)),
|
||||||
Op::Binary(op, is_assign),
|
lang_item_for_binop(self.tcx, op),
|
||||||
|
op.span(),
|
||||||
expected,
|
expected,
|
||||||
)
|
)
|
||||||
.unwrap_err();
|
.unwrap_err();
|
||||||
|
@ -642,9 +645,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
|
|
||||||
// Suggest using `add`, `offset` or `offset_from` for pointer - {integer},
|
// Suggest using `add`, `offset` or `offset_from` for pointer - {integer},
|
||||||
// pointer + {integer} or pointer - pointer.
|
// pointer + {integer} or pointer - pointer.
|
||||||
if op.span.can_be_used_for_suggestions() {
|
if op.span().can_be_used_for_suggestions() {
|
||||||
match op.node {
|
match op {
|
||||||
hir::BinOpKind::Add if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() => {
|
Op::BinOp(Spanned { node: hir::BinOpKind::Add, .. })
|
||||||
|
if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() =>
|
||||||
|
{
|
||||||
err.multipart_suggestion(
|
err.multipart_suggestion(
|
||||||
"consider using `wrapping_add` or `add` for pointer + {integer}",
|
"consider using `wrapping_add` or `add` for pointer + {integer}",
|
||||||
vec![
|
vec![
|
||||||
|
@ -657,7 +662,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
Applicability::MaybeIncorrect,
|
Applicability::MaybeIncorrect,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
hir::BinOpKind::Sub => {
|
Op::BinOp(Spanned { node: hir::BinOpKind::Sub, .. }) => {
|
||||||
if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() {
|
if lhs_ty.is_raw_ptr() && rhs_ty.is_integral() {
|
||||||
err.multipart_suggestion(
|
err.multipart_suggestion(
|
||||||
"consider using `wrapping_sub` or `sub` for \
|
"consider using `wrapping_sub` or `sub` for \
|
||||||
|
@ -713,8 +718,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
lhs_ty: Ty<'tcx>,
|
lhs_ty: Ty<'tcx>,
|
||||||
rhs_ty: Ty<'tcx>,
|
rhs_ty: Ty<'tcx>,
|
||||||
err: &mut Diag<'_>,
|
err: &mut Diag<'_>,
|
||||||
is_assign: IsAssign,
|
op: Op,
|
||||||
op: hir::BinOp,
|
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let str_concat_note = "string concatenation requires an owned `String` on the left";
|
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`";
|
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
|
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`
|
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.span_label(
|
||||||
|
op.span(),
|
||||||
|
"`+` cannot be used to concatenate two `&str` strings"
|
||||||
|
);
|
||||||
err.note(str_concat_note);
|
err.note(str_concat_note);
|
||||||
if let hir::ExprKind::AddrOf(_, _, lhs_inner_expr) = lhs_expr.kind {
|
if let hir::ExprKind::AddrOf(_, _, lhs_inner_expr) = lhs_expr.kind {
|
||||||
err.span_suggestion_verbose(
|
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) =>
|
if (*l_ty.kind() == ty::Str || is_std_string(l_ty)) && is_std_string(rhs_ty) =>
|
||||||
{
|
{
|
||||||
err.span_label(
|
err.span_label(
|
||||||
op.span,
|
op.span(),
|
||||||
"`+` cannot be used to concatenate a `&str` with a `String`",
|
"`+` cannot be used to concatenate a `&str` with a `String`",
|
||||||
);
|
);
|
||||||
match is_assign {
|
match op {
|
||||||
IsAssign::No => {
|
Op::BinOp(_) => {
|
||||||
let sugg_msg;
|
let sugg_msg;
|
||||||
let lhs_sugg = if let hir::ExprKind::AddrOf(_, _, lhs_inner_expr) = lhs_expr.kind {
|
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";
|
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,
|
Applicability::MachineApplicable,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
IsAssign::Yes => {
|
Op::AssignOp(_) => {
|
||||||
err.note(str_concat_note);
|
err.note(str_concat_note);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -799,7 +806,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
expected: Expectation<'tcx>,
|
expected: Expectation<'tcx>,
|
||||||
) -> Ty<'tcx> {
|
) -> Ty<'tcx> {
|
||||||
assert!(op.is_by_value());
|
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) => {
|
Ok(method) => {
|
||||||
self.write_method_call_and_enforce_effects(ex.hir_id, ex.span, method);
|
self.write_method_call_and_enforce_effects(ex.hir_id, ex.span, method);
|
||||||
method.sig.output()
|
method.sig.output()
|
||||||
|
@ -898,21 +911,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
&self,
|
&self,
|
||||||
(lhs_expr, lhs_ty): (&'tcx hir::Expr<'tcx>, Ty<'tcx>),
|
(lhs_expr, lhs_ty): (&'tcx hir::Expr<'tcx>, Ty<'tcx>),
|
||||||
opt_rhs: Option<(&'tcx hir::Expr<'tcx>, Ty<'tcx>)>,
|
opt_rhs: Option<(&'tcx hir::Expr<'tcx>, Ty<'tcx>)>,
|
||||||
op: Op,
|
(opname, trait_did): (Symbol, Option<hir::def_id::DefId>),
|
||||||
|
span: Span,
|
||||||
expected: Expectation<'tcx>,
|
expected: Expectation<'tcx>,
|
||||||
) -> Result<MethodCallee<'tcx>, Vec<FulfillmentError<'tcx>>> {
|
) -> Result<MethodCallee<'tcx>, Vec<FulfillmentError<'tcx>>> {
|
||||||
let span = match op {
|
let Some(trait_did) = trait_did else {
|
||||||
Op::Binary(op, _) => op.span,
|
|
||||||
Op::Unary(_, span) => span,
|
|
||||||
};
|
|
||||||
let (opname, Some(trait_did)) = lang_item_for_op(self.tcx, op, span) else {
|
|
||||||
// Bail if the operator trait is not defined.
|
// Bail if the operator trait is not defined.
|
||||||
return Err(vec![]);
|
return Err(vec![]);
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
"lookup_op_method(lhs_ty={:?}, op={:?}, opname={:?}, trait_did={:?})",
|
"lookup_op_method(lhs_ty={:?}, opname={:?}, trait_did={:?})",
|
||||||
lhs_ty, op, opname, trait_did
|
lhs_ty, opname, trait_did
|
||||||
);
|
);
|
||||||
|
|
||||||
let opname = Ident::with_dummy_span(opname);
|
let opname = Ident::with_dummy_span(opname);
|
||||||
|
@ -980,37 +990,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lang_item_for_op(
|
fn lang_item_for_binop(tcx: TyCtxt<'_>, op: Op) -> (Symbol, Option<hir::def_id::DefId>) {
|
||||||
tcx: TyCtxt<'_>,
|
|
||||||
op: Op,
|
|
||||||
span: Span,
|
|
||||||
) -> (rustc_span::Symbol, Option<hir::def_id::DefId>) {
|
|
||||||
let lang = tcx.lang_items();
|
let lang = tcx.lang_items();
|
||||||
if let Op::Binary(op, IsAssign::Yes) = op {
|
match op {
|
||||||
match op.node {
|
Op::AssignOp(op) => match op.node {
|
||||||
hir::BinOpKind::Add => (sym::add_assign, lang.add_assign_trait()),
|
hir::AssignOpKind::AddAssign => (sym::add_assign, lang.add_assign_trait()),
|
||||||
hir::BinOpKind::Sub => (sym::sub_assign, lang.sub_assign_trait()),
|
hir::AssignOpKind::SubAssign => (sym::sub_assign, lang.sub_assign_trait()),
|
||||||
hir::BinOpKind::Mul => (sym::mul_assign, lang.mul_assign_trait()),
|
hir::AssignOpKind::MulAssign => (sym::mul_assign, lang.mul_assign_trait()),
|
||||||
hir::BinOpKind::Div => (sym::div_assign, lang.div_assign_trait()),
|
hir::AssignOpKind::DivAssign => (sym::div_assign, lang.div_assign_trait()),
|
||||||
hir::BinOpKind::Rem => (sym::rem_assign, lang.rem_assign_trait()),
|
hir::AssignOpKind::RemAssign => (sym::rem_assign, lang.rem_assign_trait()),
|
||||||
hir::BinOpKind::BitXor => (sym::bitxor_assign, lang.bitxor_assign_trait()),
|
hir::AssignOpKind::BitXorAssign => (sym::bitxor_assign, lang.bitxor_assign_trait()),
|
||||||
hir::BinOpKind::BitAnd => (sym::bitand_assign, lang.bitand_assign_trait()),
|
hir::AssignOpKind::BitAndAssign => (sym::bitand_assign, lang.bitand_assign_trait()),
|
||||||
hir::BinOpKind::BitOr => (sym::bitor_assign, lang.bitor_assign_trait()),
|
hir::AssignOpKind::BitOrAssign => (sym::bitor_assign, lang.bitor_assign_trait()),
|
||||||
hir::BinOpKind::Shl => (sym::shl_assign, lang.shl_assign_trait()),
|
hir::AssignOpKind::ShlAssign => (sym::shl_assign, lang.shl_assign_trait()),
|
||||||
hir::BinOpKind::Shr => (sym::shr_assign, lang.shr_assign_trait()),
|
hir::AssignOpKind::ShrAssign => (sym::shr_assign, lang.shr_assign_trait()),
|
||||||
hir::BinOpKind::Lt
|
},
|
||||||
| hir::BinOpKind::Le
|
Op::BinOp(op) => match op.node {
|
||||||
| 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 {
|
|
||||||
hir::BinOpKind::Add => (sym::add, lang.add_trait()),
|
hir::BinOpKind::Add => (sym::add, lang.add_trait()),
|
||||||
hir::BinOpKind::Sub => (sym::sub, lang.sub_trait()),
|
hir::BinOpKind::Sub => (sym::sub, lang.sub_trait()),
|
||||||
hir::BinOpKind::Mul => (sym::mul, lang.mul_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::Eq => (sym::eq, lang.eq_trait()),
|
||||||
hir::BinOpKind::Ne => (sym::ne, lang.eq_trait()),
|
hir::BinOpKind::Ne => (sym::ne, lang.eq_trait()),
|
||||||
hir::BinOpKind::And | hir::BinOpKind::Or => {
|
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())
|
fn lang_item_for_unop(tcx: TyCtxt<'_>, op: hir::UnOp) -> (Symbol, Option<hir::def_id::DefId>) {
|
||||||
} else {
|
let lang = tcx.lang_items();
|
||||||
bug!("lookup_op_method: op not supported: {:?}", op)
|
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
|
// Binary operator categories. These categories summarize the behavior
|
||||||
// with respect to the builtin operations supported.
|
// with respect to the builtin operations supported.
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
enum BinOpCategory {
|
enum BinOpCategory {
|
||||||
/// &&, || -- cannot be overridden
|
/// &&, || -- cannot be overridden
|
||||||
Shortcircuit,
|
Shortcircuit,
|
||||||
|
@ -1063,44 +1062,58 @@ enum BinOpCategory {
|
||||||
Comparison,
|
Comparison,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BinOpCategory {
|
impl From<hir::BinOpKind> for BinOpCategory {
|
||||||
fn from(op: hir::BinOp) -> BinOpCategory {
|
fn from(op: hir::BinOpKind) -> BinOpCategory {
|
||||||
match op.node {
|
use hir::BinOpKind::*;
|
||||||
hir::BinOpKind::Shl | hir::BinOpKind::Shr => BinOpCategory::Shift,
|
match op {
|
||||||
|
Shl | Shr => BinOpCategory::Shift,
|
||||||
hir::BinOpKind::Add
|
Add | Sub | Mul | Div | Rem => BinOpCategory::Math,
|
||||||
| hir::BinOpKind::Sub
|
BitXor | BitAnd | BitOr => BinOpCategory::Bitwise,
|
||||||
| hir::BinOpKind::Mul
|
Eq | Ne | Lt | Le | Ge | Gt => BinOpCategory::Comparison,
|
||||||
| hir::BinOpKind::Div
|
And | Or => BinOpCategory::Shortcircuit,
|
||||||
| 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,
|
|
||||||
|
|
||||||
hir::BinOpKind::And | hir::BinOpKind::Or => BinOpCategory::Shortcircuit,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether the binary operation is an assignment (`a += b`), or not (`a + b`)
|
impl From<hir::AssignOpKind> for BinOpCategory {
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
fn from(op: hir::AssignOpKind) -> BinOpCategory {
|
||||||
enum IsAssign {
|
use hir::AssignOpKind::*;
|
||||||
No,
|
match op {
|
||||||
Yes,
|
ShlAssign | ShrAssign => BinOpCategory::Shift,
|
||||||
|
AddAssign | SubAssign | MulAssign | DivAssign | RemAssign => BinOpCategory::Math,
|
||||||
|
BitXorAssign | BitAndAssign | BitOrAssign => BinOpCategory::Bitwise,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
/// An assignment op (e.g. `a += b`), or a binary op (e.g. `a + b`).
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
enum Op {
|
enum Op {
|
||||||
Binary(hir::BinOp, IsAssign),
|
BinOp(hir::BinOp),
|
||||||
Unary(hir::UnOp, Span),
|
AssignOp(hir::AssignOp),
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
/// 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
|
/// Reason #2 is the killer. I tried for a while to always use
|
||||||
/// overloaded logic and just check the types in constants/codegen after
|
/// overloaded logic and just check the types in constants/codegen after
|
||||||
/// the fact, and it worked fine, except for SIMD types. -nmatsakis
|
/// 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.
|
// 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.)
|
// (See https://github.com/rust-lang/rust/issues/57447.)
|
||||||
let (lhs, rhs) = (deref_ty_if_possible(lhs), deref_ty_if_possible(rhs));
|
let (lhs, rhs) = (deref_ty_if_possible(lhs), deref_ty_if_possible(rhs));
|
||||||
|
|
||||||
match BinOpCategory::from(op) {
|
match category.into() {
|
||||||
BinOpCategory::Shortcircuit => true,
|
BinOpCategory::Shortcircuit => true,
|
||||||
|
|
||||||
BinOpCategory::Shift => {
|
BinOpCategory::Shift => {
|
||||||
lhs.references_error()
|
lhs.references_error()
|
||||||
|| rhs.references_error()
|
|| rhs.references_error()
|
||||||
|| lhs.is_integral() && rhs.is_integral()
|
|| lhs.is_integral() && rhs.is_integral()
|
||||||
}
|
}
|
||||||
|
|
||||||
BinOpCategory::Math => {
|
BinOpCategory::Math => {
|
||||||
lhs.references_error()
|
lhs.references_error()
|
||||||
|| rhs.references_error()
|
|| rhs.references_error()
|
||||||
|| lhs.is_integral() && rhs.is_integral()
|
|| lhs.is_integral() && rhs.is_integral()
|
||||||
|| lhs.is_floating_point() && rhs.is_floating_point()
|
|| lhs.is_floating_point() && rhs.is_floating_point()
|
||||||
}
|
}
|
||||||
|
|
||||||
BinOpCategory::Bitwise => {
|
BinOpCategory::Bitwise => {
|
||||||
lhs.references_error()
|
lhs.references_error()
|
||||||
|| rhs.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_floating_point() && rhs.is_floating_point()
|
||||||
|| lhs.is_bool() && rhs.is_bool()
|
|| lhs.is_bool() && rhs.is_bool()
|
||||||
}
|
}
|
||||||
|
|
||||||
BinOpCategory::Comparison => {
|
BinOpCategory::Comparison => {
|
||||||
lhs.references_error() || rhs.references_error() || lhs.is_scalar() && rhs.is_scalar()
|
lhs.references_error() || rhs.references_error() || lhs.is_scalar() && rhs.is_scalar()
|
||||||
}
|
}
|
||||||
|
|
|
@ -157,7 +157,7 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
|
||||||
self.typeck_results.node_args_mut().remove(e.hir_id);
|
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 lhs_ty = self.typeck_results.node_type(lhs.hir_id);
|
||||||
let rhs_ty = self.typeck_results.node_type(rhs.hir_id);
|
let rhs_ty = self.typeck_results.node_type(rhs.hir_id);
|
||||||
|
|
||||||
|
@ -165,25 +165,27 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
|
||||||
self.typeck_results.type_dependent_defs_mut().remove(e.hir_id);
|
self.typeck_results.type_dependent_defs_mut().remove(e.hir_id);
|
||||||
self.typeck_results.node_args_mut().remove(e.hir_id);
|
self.typeck_results.node_args_mut().remove(e.hir_id);
|
||||||
|
|
||||||
match e.kind {
|
if !op.node.is_by_value() {
|
||||||
hir::ExprKind::Binary(..) => {
|
let mut adjustments = self.typeck_results.adjustments_mut();
|
||||||
if !op.node.is_by_value() {
|
if let Some(a) = adjustments.get_mut(lhs.hir_id) {
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
hir::ExprKind::AssignOp(..)
|
|
||||||
if let Some(a) =
|
|
||||||
self.typeck_results.adjustments_mut().get_mut(lhs.hir_id) =>
|
|
||||||
{
|
|
||||||
a.pop();
|
a.pop();
|
||||||
}
|
}
|
||||||
_ => {}
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ use rustc_middle::ty::{
|
||||||
};
|
};
|
||||||
use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass};
|
use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass};
|
||||||
use rustc_span::def_id::LocalDefId;
|
use rustc_span::def_id::LocalDefId;
|
||||||
use rustc_span::{Span, Symbol, source_map, sym};
|
use rustc_span::{Span, Symbol, sym};
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
use {rustc_ast as ast, rustc_hir as hir};
|
use {rustc_ast as ast, rustc_hir as hir};
|
||||||
|
|
||||||
|
@ -223,7 +223,7 @@ impl TypeLimits {
|
||||||
fn lint_nan<'tcx>(
|
fn lint_nan<'tcx>(
|
||||||
cx: &LateContext<'tcx>,
|
cx: &LateContext<'tcx>,
|
||||||
e: &'tcx hir::Expr<'tcx>,
|
e: &'tcx hir::Expr<'tcx>,
|
||||||
binop: hir::BinOp,
|
binop: hir::BinOpKind,
|
||||||
l: &'tcx hir::Expr<'tcx>,
|
l: &'tcx hir::Expr<'tcx>,
|
||||||
r: &'tcx hir::Expr<'tcx>,
|
r: &'tcx hir::Expr<'tcx>,
|
||||||
) {
|
) {
|
||||||
|
@ -262,19 +262,19 @@ fn lint_nan<'tcx>(
|
||||||
InvalidNanComparisons::EqNe { suggestion }
|
InvalidNanComparisons::EqNe { suggestion }
|
||||||
}
|
}
|
||||||
|
|
||||||
let lint = match binop.node {
|
let lint = match binop {
|
||||||
hir::BinOpKind::Eq | hir::BinOpKind::Ne if is_nan(cx, l) => {
|
hir::BinOpKind::Eq | hir::BinOpKind::Ne if is_nan(cx, l) => {
|
||||||
eq_ne(e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful {
|
eq_ne(e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful {
|
||||||
nan_plus_binop: l_span.until(r_span),
|
nan_plus_binop: l_span.until(r_span),
|
||||||
float: r_span.shrink_to_hi(),
|
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) => {
|
hir::BinOpKind::Eq | hir::BinOpKind::Ne if is_nan(cx, r) => {
|
||||||
eq_ne(e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful {
|
eq_ne(e, l, r, |l_span, r_span| InvalidNanComparisonsSuggestion::Spanful {
|
||||||
nan_plus_binop: l_span.shrink_to_hi().to(r_span),
|
nan_plus_binop: l_span.shrink_to_hi().to(r_span),
|
||||||
float: l_span.shrink_to_hi(),
|
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
|
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) => {
|
hir::ExprKind::Binary(binop, ref l, ref r) => {
|
||||||
if is_comparison(binop) {
|
if is_comparison(binop.node) {
|
||||||
if !check_limits(cx, binop, l, r) {
|
if !check_limits(cx, binop.node, l, r) {
|
||||||
cx.emit_span_lint(UNUSED_COMPARISONS, e.span, UnusedComparisons);
|
cx.emit_span_lint(UNUSED_COMPARISONS, e.span, UnusedComparisons);
|
||||||
} else {
|
} else {
|
||||||
lint_nan(cx, e, binop, l, r);
|
lint_nan(cx, e, binop.node, l, r);
|
||||||
let cmpop = ComparisonOp::BinOp(binop.node);
|
let cmpop = ComparisonOp::BinOp(binop.node);
|
||||||
lint_wide_pointer(cx, e, cmpop, l, r);
|
lint_wide_pointer(cx, e, cmpop, l, r);
|
||||||
lint_fn_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<T: PartialOrd>(binop: hir::BinOp, v: T, min: T, max: T) -> bool {
|
fn is_valid<T: PartialOrd>(binop: hir::BinOpKind, v: T, min: T, max: T) -> bool {
|
||||||
match binop.node {
|
match binop {
|
||||||
hir::BinOpKind::Lt => v > min && v <= max,
|
hir::BinOpKind::Lt => v > min && v <= max,
|
||||||
hir::BinOpKind::Le => v >= min && v < max,
|
hir::BinOpKind::Le => v >= min && v < max,
|
||||||
hir::BinOpKind::Gt => 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 {
|
fn rev_binop(binop: hir::BinOpKind) -> hir::BinOpKind {
|
||||||
source_map::respan(
|
match binop {
|
||||||
binop.span,
|
hir::BinOpKind::Lt => hir::BinOpKind::Gt,
|
||||||
match binop.node {
|
hir::BinOpKind::Le => hir::BinOpKind::Ge,
|
||||||
hir::BinOpKind::Lt => hir::BinOpKind::Gt,
|
hir::BinOpKind::Gt => hir::BinOpKind::Lt,
|
||||||
hir::BinOpKind::Le => hir::BinOpKind::Ge,
|
hir::BinOpKind::Ge => hir::BinOpKind::Le,
|
||||||
hir::BinOpKind::Gt => hir::BinOpKind::Lt,
|
_ => binop,
|
||||||
hir::BinOpKind::Ge => hir::BinOpKind::Le,
|
}
|
||||||
_ => return binop,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_limits(
|
fn check_limits(
|
||||||
cx: &LateContext<'_>,
|
cx: &LateContext<'_>,
|
||||||
binop: hir::BinOp,
|
binop: hir::BinOpKind,
|
||||||
l: &hir::Expr<'_>,
|
l: &hir::Expr<'_>,
|
||||||
r: &hir::Expr<'_>,
|
r: &hir::Expr<'_>,
|
||||||
) -> bool {
|
) -> 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!(
|
matches!(
|
||||||
binop.node,
|
binop,
|
||||||
hir::BinOpKind::Eq
|
hir::BinOpKind::Eq
|
||||||
| hir::BinOpKind::Lt
|
| hir::BinOpKind::Lt
|
||||||
| hir::BinOpKind::Le
|
| hir::BinOpKind::Le
|
||||||
|
|
|
@ -1668,6 +1668,42 @@ pub enum BinOp {
|
||||||
Offset,
|
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<AssignOp> 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.
|
// Some nodes are used a lot. Make sure they don't unintentionally get bigger.
|
||||||
#[cfg(target_pointer_width = "64")]
|
#[cfg(target_pointer_width = "64")]
|
||||||
mod size_asserts {
|
mod size_asserts {
|
||||||
|
|
|
@ -27,7 +27,7 @@ use tracing::instrument;
|
||||||
|
|
||||||
use crate::middle::region;
|
use crate::middle::region;
|
||||||
use crate::mir::interpret::AllocId;
|
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::thir::visit::for_each_immediate_subpat;
|
||||||
use crate::ty::adjustment::PointerCoercion;
|
use crate::ty::adjustment::PointerCoercion;
|
||||||
use crate::ty::layout::IntegerExt;
|
use crate::ty::layout::IntegerExt;
|
||||||
|
@ -403,7 +403,7 @@ pub enum ExprKind<'tcx> {
|
||||||
},
|
},
|
||||||
/// A *non-overloaded* operation assignment, e.g. `lhs += rhs`.
|
/// A *non-overloaded* operation assignment, e.g. `lhs += rhs`.
|
||||||
AssignOp {
|
AssignOp {
|
||||||
op: BinOp,
|
op: AssignOp,
|
||||||
lhs: ExprId,
|
lhs: ExprId,
|
||||||
rhs: ExprId,
|
rhs: ExprId,
|
||||||
},
|
},
|
||||||
|
|
|
@ -78,8 +78,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
// because AssignOp is only legal for Copy types
|
// because AssignOp is only legal for Copy types
|
||||||
// (overloaded ops should be desugared into a call).
|
// (overloaded ops should be desugared into a call).
|
||||||
let result = unpack!(
|
let result = unpack!(
|
||||||
block =
|
block = this.build_binary_op(
|
||||||
this.build_binary_op(block, op, expr_span, lhs_ty, Operand::Copy(lhs), rhs)
|
block,
|
||||||
|
op.into(),
|
||||||
|
expr_span,
|
||||||
|
lhs_ty,
|
||||||
|
Operand::Copy(lhs),
|
||||||
|
rhs
|
||||||
|
)
|
||||||
);
|
);
|
||||||
this.cfg.push_assign(block, source_info, lhs, result);
|
this.cfg.push_assign(block, source_info, lhs, result);
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ use rustc_middle::hir::place::{
|
||||||
Place as HirPlace, PlaceBase as HirPlaceBase, ProjectionKind as HirProjectionKind,
|
Place as HirPlace, PlaceBase as HirPlaceBase, ProjectionKind as HirProjectionKind,
|
||||||
};
|
};
|
||||||
use rustc_middle::middle::region;
|
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::thir::*;
|
||||||
use rustc_middle::ty::adjustment::{
|
use rustc_middle::ty::adjustment::{
|
||||||
Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, PointerCoercion,
|
Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, PointerCoercion,
|
||||||
|
@ -489,7 +489,7 @@ impl<'tcx> ThirBuildCx<'tcx> {
|
||||||
self.overloaded_operator(expr, Box::new([lhs, rhs]))
|
self.overloaded_operator(expr, Box::new([lhs, rhs]))
|
||||||
} else {
|
} else {
|
||||||
ExprKind::AssignOp {
|
ExprKind::AssignOp {
|
||||||
op: bin_op(op.node),
|
op: assign_op(op.node),
|
||||||
lhs: self.mirror_expr(lhs),
|
lhs: self.mirror_expr(lhs),
|
||||||
rhs: self.mirror_expr(rhs),
|
rhs: self.mirror_expr(rhs),
|
||||||
}
|
}
|
||||||
|
@ -1347,3 +1347,18 @@ fn bin_op(op: hir::BinOpKind) -> BinOp {
|
||||||
_ => bug!("no equivalent for ast binop {:?}", op),
|
_ => 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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::util::parser::{AssocOp, ExprPrecedence, Fixity, prec_let_scrutinee_needs_par};
|
||||||
use rustc_ast::visit::{Visitor, walk_expr};
|
use rustc_ast::visit::{Visitor, walk_expr};
|
||||||
use rustc_ast::{
|
use rustc_ast::{
|
||||||
self as ast, AnonConst, Arm, AttrStyle, AttrVec, BinOp, BinOpKind, BlockCheckMode, CaptureBy,
|
self as ast, AnonConst, Arm, AssignOp, AssignOpKind, AttrStyle, AttrVec, BinOp, BinOpKind,
|
||||||
ClosureBinder, DUMMY_NODE_ID, Expr, ExprField, ExprKind, FnDecl, FnRetTy, Label, MacCall,
|
BlockCheckMode, CaptureBy, ClosureBinder, DUMMY_NODE_ID, Expr, ExprField, ExprKind, FnDecl,
|
||||||
MetaItemLit, Movability, Param, RangeLimits, StmtKind, Ty, TyKind, UnOp, UnsafeBinderCastKind,
|
FnRetTy, Label, MacCall, MetaItemLit, Movability, Param, RangeLimits, StmtKind, Ty, TyKind,
|
||||||
YieldKind,
|
UnOp, UnsafeBinderCastKind, YieldKind,
|
||||||
};
|
};
|
||||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||||
use rustc_errors::{Applicability, Diag, PResult, StashKey, Subdiagnostic};
|
use rustc_errors::{Applicability, Diag, PResult, StashKey, Subdiagnostic};
|
||||||
|
@ -359,7 +359,7 @@ impl<'a> Parser<'a> {
|
||||||
(
|
(
|
||||||
Some(
|
Some(
|
||||||
AssocOp::Binary(BinOpKind::Shr | BinOpKind::Gt | BinOpKind::Ge)
|
AssocOp::Binary(BinOpKind::Shr | BinOpKind::Gt | BinOpKind::Ge)
|
||||||
| AssocOp::AssignOp(BinOpKind::Shr),
|
| AssocOp::AssignOp(AssignOpKind::ShrAssign),
|
||||||
),
|
),
|
||||||
_,
|
_,
|
||||||
) if self.restrictions.contains(Restrictions::CONST_EXPR) => {
|
) if self.restrictions.contains(Restrictions::CONST_EXPR) => {
|
||||||
|
@ -3914,8 +3914,8 @@ impl<'a> Parser<'a> {
|
||||||
self.dcx().emit_err(errors::LeftArrowOperator { span });
|
self.dcx().emit_err(errors::LeftArrowOperator { span });
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mk_assign_op(&self, binop: BinOp, lhs: P<Expr>, rhs: P<Expr>) -> ExprKind {
|
fn mk_assign_op(&self, assign_op: AssignOp, lhs: P<Expr>, rhs: P<Expr>) -> ExprKind {
|
||||||
ExprKind::AssignOp(binop, lhs, rhs)
|
ExprKind::AssignOp(assign_op, lhs, rhs)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mk_range(
|
fn mk_range(
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use clippy_utils::diagnostics::span_lint_and_then;
|
use clippy_utils::diagnostics::span_lint_and_then;
|
||||||
use clippy_utils::higher;
|
use clippy_utils::higher;
|
||||||
use clippy_utils::ty::is_type_lang_item;
|
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_lint::{LateContext, LateLintPass};
|
||||||
use rustc_session::declare_lint_pass;
|
use rustc_session::declare_lint_pass;
|
||||||
use rustc_span::sym;
|
use rustc_span::sym;
|
||||||
|
@ -77,7 +77,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatPushString {
|
||||||
return;
|
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,
|
_ => return,
|
||||||
};
|
};
|
||||||
if is_format(cx, arg) {
|
if is_format(cx, arg) {
|
||||||
|
|
|
@ -5,7 +5,7 @@ use clippy_utils::source::snippet_with_context;
|
||||||
use rustc_ast::ast::{LitIntType, LitKind};
|
use rustc_ast::ast::{LitIntType, LitKind};
|
||||||
use rustc_data_structures::packed::Pu128;
|
use rustc_data_structures::packed::Pu128;
|
||||||
use rustc_errors::Applicability;
|
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_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::ty::{IntTy, Ty, UintTy};
|
use rustc_middle::ty::{IntTy, Ty, UintTy};
|
||||||
use rustc_session::declare_lint_pass;
|
use rustc_session::declare_lint_pass;
|
||||||
|
@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd {
|
||||||
&& ex.span.ctxt() == ctxt
|
&& ex.span.ctxt() == ctxt
|
||||||
&& expr1.span.ctxt() == ctxt
|
&& expr1.span.ctxt() == ctxt
|
||||||
&& clippy_utils::SpanlessEq::new(cx).eq_expr(l, target)
|
&& clippy_utils::SpanlessEq::new(cx).eq_expr(l, target)
|
||||||
&& BinOpKind::Add == op1.node
|
&& AssignOpKind::AddAssign == op1.node
|
||||||
&& let ExprKind::Lit(lit) = value.kind
|
&& let ExprKind::Lit(lit) = value.kind
|
||||||
&& let LitKind::Int(Pu128(1), LitIntType::Unsuffixed) = lit.node
|
&& let LitKind::Int(Pu128(1), LitIntType::Unsuffixed) = lit.node
|
||||||
&& block.expr.is_none()
|
&& block.expr.is_none()
|
||||||
|
|
|
@ -8,7 +8,7 @@ use clippy_utils::{
|
||||||
use rustc_ast::ast::LitKind;
|
use rustc_ast::ast::LitKind;
|
||||||
use rustc_data_structures::packed::Pu128;
|
use rustc_data_structures::packed::Pu128;
|
||||||
use rustc_errors::Applicability;
|
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_lint::{LateContext, LateLintPass};
|
||||||
use rustc_session::impl_lint_pass;
|
use rustc_session::impl_lint_pass;
|
||||||
use rustc_span::Span;
|
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 {
|
match peel_blocks_with_stmt(expr).kind {
|
||||||
ExprKind::AssignOp(ref op1, target, value) => {
|
ExprKind::AssignOp(ref op1, target, value) => {
|
||||||
// Check if literal being subtracted is one
|
// 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, _) => {
|
ExprKind::Assign(target, value, _) => {
|
||||||
if let ExprKind::Binary(ref op1, left1, right1) = value.kind
|
if let ExprKind::Binary(ref op1, left1, right1) = value.kind
|
||||||
|
|
|
@ -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_ast::ast::{LitIntType, LitKind};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::intravisit::{Visitor, walk_expr, walk_local};
|
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_lint::LateContext;
|
||||||
use rustc_middle::hir::nested_filter;
|
use rustc_middle::hir::nested_filter;
|
||||||
use rustc_middle::ty::{self, Ty};
|
use rustc_middle::ty::{self, Ty};
|
||||||
|
@ -58,7 +60,7 @@ impl<'tcx> Visitor<'tcx> for IncrementVisitor<'_, 'tcx> {
|
||||||
match parent.kind {
|
match parent.kind {
|
||||||
ExprKind::AssignOp(op, lhs, rhs) => {
|
ExprKind::AssignOp(op, lhs, rhs) => {
|
||||||
if lhs.hir_id == expr.hir_id {
|
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)
|
&& is_integer_const(self.cx, rhs, 1)
|
||||||
&& *state == IncrementVisitorVarState::Initial
|
&& *state == IncrementVisitorVarState::Initial
|
||||||
&& self.depth == 0
|
&& self.depth == 0
|
||||||
|
|
|
@ -10,7 +10,7 @@ use rustc_ast::{LitKind, RangeLimits};
|
||||||
use rustc_data_structures::packed::Pu128;
|
use rustc_data_structures::packed::Pu128;
|
||||||
use rustc_data_structures::unhash::UnindexMap;
|
use rustc_data_structures::unhash::UnindexMap;
|
||||||
use rustc_errors::{Applicability, Diag};
|
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_lint::{LateContext, LateLintPass};
|
||||||
use rustc_session::declare_lint_pass;
|
use rustc_session::declare_lint_pass;
|
||||||
use rustc_span::source_map::Spanned;
|
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()))`
|
/// E.g. for `v.len() > 5` this returns `Some((LengthComparison::IntLessThanLength, 5, v.len()))`
|
||||||
fn len_comparison<'hir>(
|
fn len_comparison<'hir>(
|
||||||
bin_op: BinOp,
|
bin_op: BinOpKind,
|
||||||
left: &'hir Expr<'hir>,
|
left: &'hir Expr<'hir>,
|
||||||
right: &'hir Expr<'hir>,
|
right: &'hir Expr<'hir>,
|
||||||
) -> Option<(LengthComparison, usize, &'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()`
|
// normalize comparison, `v.len() > 4` becomes `4 < v.len()`
|
||||||
// this simplifies the logic a bit
|
// 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) {
|
match (op, left.kind, right.kind) {
|
||||||
(Rel::Lt, int_lit_pat!(left), _) => Some((LengthComparison::IntLessThanLength, left as usize, right)),
|
(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)),
|
(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::Unary(UnOp::Not, condition) = &cond.kind
|
||||||
&& let ExprKind::Binary(bin_op, left, right) = &condition.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
|
&& let ExprKind::MethodCall(method, recv, [], _) = &slice_len.kind
|
||||||
&& cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_slice()
|
&& cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_slice()
|
||||||
&& method.ident.name == sym::len
|
&& method.ident.name == sym::len
|
||||||
|
|
|
@ -261,10 +261,11 @@ fn check_expr<'tcx>(vis: &mut ReadVisitor<'_, 'tcx>, expr: &'tcx Expr<'_>) -> St
|
||||||
| ExprKind::Assign(..)
|
| ExprKind::Assign(..)
|
||||||
| ExprKind::Index(..)
|
| ExprKind::Index(..)
|
||||||
| ExprKind::Repeat(_, _)
|
| ExprKind::Repeat(_, _)
|
||||||
| ExprKind::Struct(_, _, _) => {
|
| ExprKind::Struct(_, _, _)
|
||||||
|
| ExprKind::AssignOp(_, _, _) => {
|
||||||
walk_expr(vis, expr);
|
walk_expr(vis, expr);
|
||||||
},
|
},
|
||||||
ExprKind::Binary(op, _, _) | ExprKind::AssignOp(op, _, _) => {
|
ExprKind::Binary(op, _, _) => {
|
||||||
if op.node == BinOpKind::And || op.node == BinOpKind::Or {
|
if op.node == BinOpKind::And || op.node == BinOpKind::Or {
|
||||||
// x && y and x || y always evaluate x first, so these are
|
// x && y and x || y always evaluate x first, so these are
|
||||||
// strictly sequenced.
|
// strictly sequenced.
|
||||||
|
|
|
@ -335,9 +335,12 @@ impl<'tcx> LateLintPass<'tcx> for ArithmeticSideEffects {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
match &expr.kind {
|
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);
|
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, _) => {
|
hir::ExprKind::MethodCall(ps, receiver, args, _) => {
|
||||||
self.manage_method_call(args, cx, expr, ps, receiver);
|
self.manage_method_call(args, cx, expr, ps, receiver);
|
||||||
},
|
},
|
||||||
|
|
|
@ -913,9 +913,10 @@ impl<'tcx> LateLintPass<'tcx> for Operators {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
ExprKind::AssignOp(op, lhs, rhs) => {
|
ExprKind::AssignOp(op, lhs, rhs) => {
|
||||||
self.arithmetic_context.check_binary(cx, e, op.node, lhs, rhs);
|
let bin_op = op.node.into();
|
||||||
misrefactored_assign_op::check(cx, e, op.node, lhs, rhs);
|
self.arithmetic_context.check_binary(cx, e, bin_op, lhs, rhs);
|
||||||
modulo_arithmetic::check(cx, e, op.node, lhs, rhs, false);
|
misrefactored_assign_op::check(cx, e, bin_op, lhs, rhs);
|
||||||
|
modulo_arithmetic::check(cx, e, bin_op, lhs, rhs, false);
|
||||||
},
|
},
|
||||||
ExprKind::Assign(lhs, rhs, _) => {
|
ExprKind::Assign(lhs, rhs, _) => {
|
||||||
assign_op_pattern::check(cx, e, lhs, rhs);
|
assign_op_pattern::check(cx, e, lhs, rhs);
|
||||||
|
|
|
@ -5,6 +5,7 @@ use core::ops::ControlFlow;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_session::declare_lint_pass;
|
use rustc_session::declare_lint_pass;
|
||||||
|
use rustc_span::Span;
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
|
@ -56,8 +57,27 @@ declare_lint_pass!(SuspiciousImpl => [SUSPICIOUS_ARITHMETIC_IMPL, SUSPICIOUS_OP_
|
||||||
|
|
||||||
impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl {
|
impl<'tcx> LateLintPass<'tcx> for SuspiciousImpl {
|
||||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||||
if let hir::ExprKind::Binary(binop, _, _) | hir::ExprKind::AssignOp(binop, ..) = expr.kind
|
match expr.kind {
|
||||||
&& let Some((binop_trait_lang, op_assign_trait_lang)) = binop_traits(binop.node)
|
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(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)
|
&& 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(
|
span_lint(
|
||||||
cx,
|
cx,
|
||||||
lint,
|
lint,
|
||||||
binop.span,
|
span,
|
||||||
format!(
|
format!(
|
||||||
"suspicious use of `{}` in `{}` impl",
|
"suspicious use of `{}` in `{}` impl",
|
||||||
binop.node.as_str(),
|
binop.as_str(),
|
||||||
cx.tcx.item_name(trait_id)
|
cx.tcx.item_name(trait_id)
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -10,7 +10,7 @@ use rustc_data_structures::fx::FxIndexSet;
|
||||||
use rustc_hir::intravisit::{Visitor, walk_expr};
|
use rustc_hir::intravisit::{Visitor, walk_expr};
|
||||||
|
|
||||||
use rustc_errors::Applicability;
|
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_lint::{LateContext, LateLintPass, LintContext};
|
||||||
use rustc_middle::ty;
|
use rustc_middle::ty;
|
||||||
use rustc_session::declare_lint_pass;
|
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
|
if let StmtKind::Semi(expr) = stmt.kind
|
||||||
&& let ExprKind::AssignOp(
|
&& let ExprKind::AssignOp(
|
||||||
Spanned {
|
Spanned {
|
||||||
node: BinOpKind::BitXor,
|
node: AssignOpKind::BitXorAssign,
|
||||||
..
|
..
|
||||||
},
|
},
|
||||||
lhs,
|
lhs,
|
||||||
|
|
|
@ -15,7 +15,7 @@ use rustc_apfloat::ieee::{Half, Quad};
|
||||||
use rustc_ast::ast::{self, LitFloatType, LitKind};
|
use rustc_ast::ast::{self, LitFloatType, LitKind};
|
||||||
use rustc_hir::def::{DefKind, Res};
|
use rustc_hir::def::{DefKind, Res};
|
||||||
use rustc_hir::{
|
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_lexer::tokenize;
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
|
@ -506,7 +506,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
|
||||||
UnOp::Deref => Some(if let Constant::Ref(r) = o { *r } else { o }),
|
UnOp::Deref => Some(if let Constant::Ref(r) = o { *r } else { o }),
|
||||||
}),
|
}),
|
||||||
ExprKind::If(cond, then, ref otherwise) => self.ifthenelse(cond, then, *otherwise),
|
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, []) => {
|
ExprKind::Call(callee, []) => {
|
||||||
// We only handle a few const functions for now.
|
// We only handle a few const functions for now.
|
||||||
if let ExprKind::Path(qpath) = &callee.kind
|
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<Constant<'tcx>> {
|
fn binop(&self, op: BinOpKind, left: &Expr<'_>, right: &Expr<'_>) -> Option<Constant<'tcx>> {
|
||||||
let l = self.expr(left)?;
|
let l = self.expr(left)?;
|
||||||
let r = self.expr(right);
|
let r = self.expr(right);
|
||||||
match (l, r) {
|
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
|
// 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
|
// 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
|
&& l == ty_min_value
|
||||||
&& r == -1
|
&& r == -1
|
||||||
{
|
{
|
||||||
|
@ -765,7 +765,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let zext = |n: i128| Constant::Int(unsext(self.tcx, n, ity));
|
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
|
// When +, * or binary - create a value greater than the maximum value, or less than
|
||||||
// the minimum value that can be stored, it panics.
|
// the minimum value that can be stored, it panics.
|
||||||
BinOpKind::Add => l.checked_add(r).and_then(|n| ity.ensure_fits(n)).map(zext),
|
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) => {
|
ty::Uint(ity) => {
|
||||||
let bits = ity.bits();
|
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::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::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),
|
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,
|
_ => None,
|
||||||
},
|
},
|
||||||
// FIXME(f16_f128): add these types when binary operations are available on all platforms
|
// 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::Add => Some(Constant::F32(l + r)),
|
||||||
BinOpKind::Sub => Some(Constant::F32(l - r)),
|
BinOpKind::Sub => Some(Constant::F32(l - r)),
|
||||||
BinOpKind::Mul => 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)),
|
BinOpKind::Gt => Some(Constant::Bool(l > r)),
|
||||||
_ => None,
|
_ => 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::Add => Some(Constant::F64(l + r)),
|
||||||
BinOpKind::Sub => Some(Constant::F64(l - r)),
|
BinOpKind::Sub => Some(Constant::F64(l - r)),
|
||||||
BinOpKind::Mul => 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)),
|
BinOpKind::Gt => Some(Constant::Bool(l > r)),
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
(l, r) => match (op.node, l, r) {
|
(l, r) => match (op, l, r) {
|
||||||
(BinOpKind::And, Constant::Bool(false), _) => Some(Constant::Bool(false)),
|
(BinOpKind::And, Constant::Bool(false), _) => Some(Constant::Bool(false)),
|
||||||
(BinOpKind::Or, Constant::Bool(true), _) => Some(Constant::Bool(true)),
|
(BinOpKind::Or, Constant::Bool(true), _) => Some(Constant::Bool(true)),
|
||||||
(BinOpKind::And, Constant::Bool(true), Some(r)) | (BinOpKind::Or, Constant::Bool(false), Some(r)) => {
|
(BinOpKind::And, Constant::Bool(true), Some(r)) | (BinOpKind::Or, Constant::Bool(false), Some(r)) => {
|
||||||
|
|
|
@ -357,7 +357,7 @@ fn binop_to_string(op: AssocOp, lhs: &str, rhs: &str) -> String {
|
||||||
match op {
|
match op {
|
||||||
AssocOp::Binary(op) => format!("{lhs} {} {rhs}", op.as_str()),
|
AssocOp::Binary(op) => format!("{lhs} {} {rhs}", op.as_str()),
|
||||||
AssocOp::Assign => format!("{lhs} = {rhs}"),
|
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::Cast => format!("{lhs} as {rhs}"),
|
||||||
AssocOp::Range(limits) => format!("{lhs}{}{rhs}", limits.as_str()),
|
AssocOp::Range(limits) => format!("{lhs}{}{rhs}", limits.as_str()),
|
||||||
}
|
}
|
||||||
|
|
|
@ -2058,7 +2058,7 @@ fn rewrite_assignment(
|
||||||
context: &RewriteContext<'_>,
|
context: &RewriteContext<'_>,
|
||||||
lhs: &ast::Expr,
|
lhs: &ast::Expr,
|
||||||
rhs: &ast::Expr,
|
rhs: &ast::Expr,
|
||||||
op: Option<&ast::BinOp>,
|
op: Option<&ast::AssignOp>,
|
||||||
shape: Shape,
|
shape: Shape,
|
||||||
) -> RewriteResult {
|
) -> RewriteResult {
|
||||||
let operator_str = match op {
|
let operator_str = match op {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue