1
Fork 0

Introduce AssocOp::Binary.

It mirrors `ExprKind::Binary`, and contains a `BinOpKind`. This makes
`AssocOp` more like `ExprKind`. Note that the variants removed from
`AssocOp` are all named differently to `BinOpToken`, e.g. `Multiply`
instead of `Mul`, so that's an inconsistency removed.

The commit adds `precedence` and `fixity` methods to `BinOpKind`, and
calls them from the corresponding methods in `AssocOp`. This avoids the
need to create an `AssocOp` from a `BinOpKind` in a bunch of places, and
`AssocOp::from_ast_binop` is removed.

`AssocOp::to_ast_binop` is also no longer needed.

Overall things are shorter and nicer.
This commit is contained in:
Nicholas Nethercote 2024-12-19 18:24:07 +11:00
parent a8364f3b2a
commit ceafbad81f
10 changed files with 152 additions and 256 deletions

View file

@ -39,7 +39,7 @@ pub use crate::format::*;
use crate::ptr::P; use crate::ptr::P;
use crate::token::{self, CommentKind, Delimiter}; use crate::token::{self, CommentKind, Delimiter};
use crate::tokenstream::{DelimSpan, LazyAttrTokenStream, TokenStream}; use crate::tokenstream::{DelimSpan, LazyAttrTokenStream, TokenStream};
use crate::util::parser::{AssocOp, ExprPrecedence}; use crate::util::parser::{ExprPrecedence, Fixity};
/// A "Label" is an identifier of some point in sources, /// A "Label" is an identifier of some point in sources,
/// e.g. in the following code: /// e.g. in the following code:
@ -937,8 +937,37 @@ impl BinOpKind {
matches!(self, BinOpKind::And | BinOpKind::Or) matches!(self, BinOpKind::And | BinOpKind::Or)
} }
pub fn precedence(&self) -> ExprPrecedence {
use BinOpKind::*;
match *self {
Mul | Div | Rem => ExprPrecedence::Product,
Add | Sub => ExprPrecedence::Sum,
Shl | Shr => ExprPrecedence::Shift,
BitAnd => ExprPrecedence::BitAnd,
BitXor => ExprPrecedence::BitXor,
BitOr => ExprPrecedence::BitOr,
Lt | Gt | Le | Ge | Eq | Ne => ExprPrecedence::Compare,
And => ExprPrecedence::LAnd,
Or => ExprPrecedence::LOr,
}
}
pub fn fixity(&self) -> Fixity {
use BinOpKind::*;
match self {
Eq | Ne | Lt | Le | Gt | Ge => Fixity::None,
Add | Sub | Mul | Div | Rem | And | Or | BitXor | BitAnd | BitOr | Shl | Shr => {
Fixity::Left
}
}
}
pub fn is_comparison(self) -> bool { pub fn is_comparison(self) -> bool {
crate::util::parser::AssocOp::from_ast_binop(self).is_comparison() use BinOpKind::*;
match self {
Eq | Ne | Lt | Le | Gt | Ge => true,
Add | Sub | Mul | Div | Rem | And | Or | BitXor | BitAnd | BitOr | Shl | Shr => false,
}
} }
/// Returns `true` if the binary operator takes its arguments by value. /// Returns `true` if the binary operator takes its arguments by value.
@ -1332,7 +1361,7 @@ impl Expr {
ExprKind::Range(..) => ExprPrecedence::Range, ExprKind::Range(..) => ExprPrecedence::Range,
// Binop-like expr kinds, handled by `AssocOp`. // Binop-like expr kinds, handled by `AssocOp`.
ExprKind::Binary(op, ..) => AssocOp::from_ast_binop(op.node).precedence(), ExprKind::Binary(op, ..) => op.node.precedence(),
ExprKind::Cast(..) => ExprPrecedence::Cast, ExprKind::Cast(..) => ExprPrecedence::Cast,
ExprKind::Assign(..) | ExprKind::Assign(..) |

View file

@ -3,51 +3,15 @@ use rustc_span::kw;
use crate::ast::{self, BinOpKind}; use crate::ast::{self, BinOpKind};
use crate::token::{self, BinOpToken, Token}; use crate::token::{self, BinOpToken, Token};
/// Associative operator with precedence. /// Associative operator.
///
/// This is the enum which specifies operator precedence and fixity to the parser.
#[derive(Copy, Clone, PartialEq, Debug)] #[derive(Copy, Clone, PartialEq, Debug)]
pub enum AssocOp { pub enum AssocOp {
/// `+` /// A binary op.
Add, Binary(BinOpKind),
/// `-`
Subtract,
/// `*`
Multiply,
/// `/`
Divide,
/// `%`
Modulus,
/// `&&`
LAnd,
/// `||`
LOr,
/// `^`
BitXor,
/// `&`
BitAnd,
/// `|`
BitOr,
/// `<<`
ShiftLeft,
/// `>>`
ShiftRight,
/// `==`
Equal,
/// `<`
Less,
/// `<=`
LessEqual,
/// `!=`
NotEqual,
/// `>`
Greater,
/// `>=`
GreaterEqual,
/// `=`
Assign,
/// `?=` where ? is one of the assignable BinOps /// `?=` where ? is one of the assignable BinOps
AssignOp(BinOpKind), AssignOp(BinOpKind),
/// `=`
Assign,
/// `as` /// `as`
As, As,
/// `..` range /// `..` range
@ -67,11 +31,21 @@ pub enum Fixity {
} }
impl AssocOp { impl AssocOp {
/// Creates a new AssocOP from a token /// Creates a new AssocOp from a token.
pub fn from_token(t: &Token) -> Option<AssocOp> { pub fn from_token(t: &Token) -> Option<AssocOp> {
use AssocOp::*; use AssocOp::*;
match t.kind { match t.kind {
token::Eq => Some(Assign), token::Eq => Some(Assign),
token::BinOp(BinOpToken::Plus) => Some(Binary(BinOpKind::Add)),
token::BinOp(BinOpToken::Minus) => Some(Binary(BinOpKind::Sub)),
token::BinOp(BinOpToken::Star) => Some(Binary(BinOpKind::Mul)),
token::BinOp(BinOpToken::Slash) => Some(Binary(BinOpKind::Div)),
token::BinOp(BinOpToken::Percent) => Some(Binary(BinOpKind::Rem)),
token::BinOp(BinOpToken::Caret) => Some(Binary(BinOpKind::BitXor)),
token::BinOp(BinOpToken::And) => Some(Binary(BinOpKind::BitAnd)),
token::BinOp(BinOpToken::Or) => Some(Binary(BinOpKind::BitOr)),
token::BinOp(BinOpToken::Shl) => Some(Binary(BinOpKind::Shl)),
token::BinOp(BinOpToken::Shr) => Some(Binary(BinOpKind::Shr)),
token::BinOpEq(BinOpToken::Plus) => Some(AssignOp(BinOpKind::Add)), token::BinOpEq(BinOpToken::Plus) => Some(AssignOp(BinOpKind::Add)),
token::BinOpEq(BinOpToken::Minus) => Some(AssignOp(BinOpKind::Sub)), token::BinOpEq(BinOpToken::Minus) => Some(AssignOp(BinOpKind::Sub)),
token::BinOpEq(BinOpToken::Star) => Some(AssignOp(BinOpKind::Mul)), token::BinOpEq(BinOpToken::Star) => Some(AssignOp(BinOpKind::Mul)),
@ -82,74 +56,31 @@ impl AssocOp {
token::BinOpEq(BinOpToken::Or) => Some(AssignOp(BinOpKind::BitOr)), token::BinOpEq(BinOpToken::Or) => Some(AssignOp(BinOpKind::BitOr)),
token::BinOpEq(BinOpToken::Shl) => Some(AssignOp(BinOpKind::Shl)), token::BinOpEq(BinOpToken::Shl) => Some(AssignOp(BinOpKind::Shl)),
token::BinOpEq(BinOpToken::Shr) => Some(AssignOp(BinOpKind::Shr)), token::BinOpEq(BinOpToken::Shr) => Some(AssignOp(BinOpKind::Shr)),
token::BinOp(BinOpToken::Plus) => Some(Add), token::Lt => Some(Binary(BinOpKind::Lt)),
token::BinOp(BinOpToken::Minus) => Some(Subtract), token::Le => Some(Binary(BinOpKind::Le)),
token::BinOp(BinOpToken::Star) => Some(Multiply), token::Ge => Some(Binary(BinOpKind::Ge)),
token::BinOp(BinOpToken::Slash) => Some(Divide), token::Gt => Some(Binary(BinOpKind::Gt)),
token::BinOp(BinOpToken::Percent) => Some(Modulus), token::EqEq => Some(Binary(BinOpKind::Eq)),
token::BinOp(BinOpToken::Caret) => Some(BitXor), token::Ne => Some(Binary(BinOpKind::Ne)),
token::BinOp(BinOpToken::And) => Some(BitAnd), token::AndAnd => Some(Binary(BinOpKind::And)),
token::BinOp(BinOpToken::Or) => Some(BitOr), token::OrOr => Some(Binary(BinOpKind::Or)),
token::BinOp(BinOpToken::Shl) => Some(ShiftLeft),
token::BinOp(BinOpToken::Shr) => Some(ShiftRight),
token::Lt => Some(Less),
token::Le => Some(LessEqual),
token::Ge => Some(GreaterEqual),
token::Gt => Some(Greater),
token::EqEq => Some(Equal),
token::Ne => Some(NotEqual),
token::AndAnd => Some(LAnd),
token::OrOr => Some(LOr),
token::DotDot => Some(DotDot), token::DotDot => Some(DotDot),
token::DotDotEq => Some(DotDotEq), token::DotDotEq => Some(DotDotEq),
// DotDotDot is no longer supported, but we need some way to display the error // DotDotDot is no longer supported, but we need some way to display the error
token::DotDotDot => Some(DotDotEq), token::DotDotDot => Some(DotDotEq),
// `<-` should probably be `< -` // `<-` should probably be `< -`
token::LArrow => Some(Less), token::LArrow => Some(Binary(BinOpKind::Lt)),
_ if t.is_keyword(kw::As) => Some(As), _ if t.is_keyword(kw::As) => Some(As),
_ => None, _ => None,
} }
} }
/// Creates a new AssocOp from ast::BinOpKind.
pub fn from_ast_binop(op: BinOpKind) -> Self {
use AssocOp::*;
match op {
BinOpKind::Lt => Less,
BinOpKind::Gt => Greater,
BinOpKind::Le => LessEqual,
BinOpKind::Ge => GreaterEqual,
BinOpKind::Eq => Equal,
BinOpKind::Ne => NotEqual,
BinOpKind::Mul => Multiply,
BinOpKind::Div => Divide,
BinOpKind::Rem => Modulus,
BinOpKind::Add => Add,
BinOpKind::Sub => Subtract,
BinOpKind::Shl => ShiftLeft,
BinOpKind::Shr => ShiftRight,
BinOpKind::BitAnd => BitAnd,
BinOpKind::BitXor => BitXor,
BinOpKind::BitOr => BitOr,
BinOpKind::And => LAnd,
BinOpKind::Or => LOr,
}
}
/// Gets the precedence of this operator /// Gets the precedence of this operator
pub fn precedence(&self) -> ExprPrecedence { pub fn precedence(&self) -> ExprPrecedence {
use AssocOp::*; use AssocOp::*;
match *self { match *self {
As => ExprPrecedence::Cast, As => ExprPrecedence::Cast,
Multiply | Divide | Modulus => ExprPrecedence::Product, Binary(bin_op) => bin_op.precedence(),
Add | Subtract => ExprPrecedence::Sum,
ShiftLeft | ShiftRight => ExprPrecedence::Shift,
BitAnd => ExprPrecedence::BitAnd,
BitXor => ExprPrecedence::BitXor,
BitOr => ExprPrecedence::BitOr,
Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => ExprPrecedence::Compare,
LAnd => ExprPrecedence::LAnd,
LOr => ExprPrecedence::LOr,
DotDot | DotDotEq => ExprPrecedence::Range, DotDot | DotDotEq => ExprPrecedence::Range,
Assign | AssignOp(_) => ExprPrecedence::Assign, Assign | AssignOp(_) => ExprPrecedence::Assign,
} }
@ -161,22 +92,17 @@ impl AssocOp {
// NOTE: it is a bug to have an operators that has same precedence but different fixities! // NOTE: it is a bug to have an operators that has same precedence but different fixities!
match *self { match *self {
Assign | AssignOp(_) => Fixity::Right, Assign | AssignOp(_) => Fixity::Right,
As | Multiply | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd Binary(binop) => binop.fixity(),
| BitXor | BitOr | LAnd | LOr => Fixity::Left, As => Fixity::Left,
Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | DotDot | DotDotEq => { DotDot | DotDotEq => Fixity::None,
Fixity::None
}
} }
} }
pub fn is_comparison(&self) -> bool { pub fn is_comparison(&self) -> bool {
use AssocOp::*; use AssocOp::*;
match *self { match *self {
Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => true, Binary(binop) => binop.is_comparison(),
Assign | AssignOp(_) | As | Multiply | Divide | Modulus | Add | Subtract Assign | AssignOp(_) | As | DotDot | DotDotEq => false,
| ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd | LOr | DotDot | DotDotEq => {
false
}
} }
} }
@ -184,34 +110,7 @@ impl AssocOp {
use AssocOp::*; use AssocOp::*;
match *self { match *self {
Assign | AssignOp(_) => true, Assign | AssignOp(_) => true,
Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | As | Multiply As | Binary(_) | DotDot | DotDotEq => false,
| Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd | BitXor
| BitOr | LAnd | LOr | DotDot | DotDotEq => false,
}
}
pub fn to_ast_binop(&self) -> Option<BinOpKind> {
use AssocOp::*;
match *self {
Less => Some(BinOpKind::Lt),
Greater => Some(BinOpKind::Gt),
LessEqual => Some(BinOpKind::Le),
GreaterEqual => Some(BinOpKind::Ge),
Equal => Some(BinOpKind::Eq),
NotEqual => Some(BinOpKind::Ne),
Multiply => Some(BinOpKind::Mul),
Divide => Some(BinOpKind::Div),
Modulus => Some(BinOpKind::Rem),
Add => Some(BinOpKind::Add),
Subtract => Some(BinOpKind::Sub),
ShiftLeft => Some(BinOpKind::Shl),
ShiftRight => Some(BinOpKind::Shr),
BitAnd => Some(BinOpKind::BitAnd),
BitXor => Some(BinOpKind::BitXor),
BitOr => Some(BinOpKind::BitOr),
LAnd => Some(BinOpKind::And),
LOr => Some(BinOpKind::Or),
Assign | AssignOp(_) | As | DotDot | DotDotEq => None,
} }
} }
@ -221,16 +120,19 @@ impl AssocOp {
/// parentheses while having a high degree of confidence on the correctness of the suggestion. /// parentheses while having a high degree of confidence on the correctness of the suggestion.
pub fn can_continue_expr_unambiguously(&self) -> bool { pub fn can_continue_expr_unambiguously(&self) -> bool {
use AssocOp::*; use AssocOp::*;
use BinOpKind::*;
matches!( matches!(
self, self,
BitXor | // `{ 42 } ^ 3`
Assign | // `{ 42 } = { 42 }` Assign | // `{ 42 } = { 42 }`
Divide | // `{ 42 } / 42` Binary(
Modulus | // `{ 42 } % 2` BitXor | // `{ 42 } ^ 3`
ShiftRight | // `{ 42 } >> 2` Div | // `{ 42 } / 42`
LessEqual | // `{ 42 } <= 3` Rem | // `{ 42 } % 2`
Greater | // `{ 42 } > 3` Shr | // `{ 42 } >> 2`
GreaterEqual | // `{ 42 } >= 3` Le | // `{ 42 } <= 3`
Gt | // `{ 42 } > 3`
Ge // `{ 42 } >= 3`
) |
AssignOp(_) | // `{ 42 } +=` AssignOp(_) | // `{ 42 } +=`
// Equal | // `{ 42 } == { 42 }` Accepting these here would regress incorrect // Equal | // `{ 42 } == { 42 }` Accepting these here would regress incorrect
// NotEqual | // `{ 42 } != { 42 } struct literals parser recovery. // NotEqual | // `{ 42 } != { 42 } struct literals parser recovery.

View file

@ -5,7 +5,7 @@ use itertools::{Itertools, Position};
use rustc_ast::ptr::P; use rustc_ast::ptr::P;
use rustc_ast::util::classify; use rustc_ast::util::classify;
use rustc_ast::util::literal::escape_byte_str_symbol; use rustc_ast::util::literal::escape_byte_str_symbol;
use rustc_ast::util::parser::{self, AssocOp, ExprPrecedence, Fixity}; use rustc_ast::util::parser::{self, ExprPrecedence, Fixity};
use rustc_ast::{ use rustc_ast::{
self as ast, BlockCheckMode, FormatAlignment, FormatArgPosition, FormatArgsPiece, FormatCount, self as ast, BlockCheckMode, FormatAlignment, FormatArgPosition, FormatArgsPiece, FormatCount,
FormatDebugHex, FormatSign, FormatTrait, token, FormatDebugHex, FormatSign, FormatTrait, token,
@ -279,12 +279,11 @@ impl<'a> State<'a> {
rhs: &ast::Expr, rhs: &ast::Expr,
fixup: FixupContext, fixup: FixupContext,
) { ) {
let assoc_op = AssocOp::from_ast_binop(op.node); let binop_prec = op.node.precedence();
let binop_prec = assoc_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 assoc_op.fixity() { let (mut left_needs_paren, right_needs_paren) = match op.node.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),

View file

@ -4,7 +4,7 @@ use std::fmt;
use rustc_abi::ExternAbi; use rustc_abi::ExternAbi;
use rustc_ast::attr::AttributeExt; use rustc_ast::attr::AttributeExt;
use rustc_ast::token::CommentKind; use rustc_ast::token::CommentKind;
use rustc_ast::util::parser::{AssocOp, ExprPrecedence}; use rustc_ast::util::parser::ExprPrecedence;
use rustc_ast::{ use rustc_ast::{
self as ast, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, IntTy, Label, LitIntType, self as ast, FloatTy, InlineAsmOptions, InlineAsmTemplatePiece, IntTy, Label, LitIntType,
LitKind, TraitObjectSyntax, UintTy, UnsafeBinderCastKind, LitKind, TraitObjectSyntax, UintTy, UnsafeBinderCastKind,
@ -2124,7 +2124,7 @@ impl Expr<'_> {
| ExprKind::Become(..) => ExprPrecedence::Jump, | ExprKind::Become(..) => ExprPrecedence::Jump,
// Binop-like expr kinds, handled by `AssocOp`. // Binop-like expr kinds, handled by `AssocOp`.
ExprKind::Binary(op, ..) => AssocOp::from_ast_binop(op.node).precedence(), ExprKind::Binary(op, ..) => op.node.precedence(),
ExprKind::Cast(..) => ExprPrecedence::Cast, ExprKind::Cast(..) => ExprPrecedence::Cast,
ExprKind::Assign(..) | ExprKind::Assign(..) |

View file

@ -10,7 +10,7 @@ use std::cell::Cell;
use std::vec; use std::vec;
use rustc_abi::ExternAbi; use rustc_abi::ExternAbi;
use rustc_ast::util::parser::{self, AssocOp, ExprPrecedence, Fixity}; use rustc_ast::util::parser::{self, ExprPrecedence, Fixity};
use rustc_ast::{AttrStyle, DUMMY_NODE_ID, DelimArgs}; use rustc_ast::{AttrStyle, DUMMY_NODE_ID, DelimArgs};
use rustc_ast_pretty::pp::Breaks::{Consistent, Inconsistent}; use rustc_ast_pretty::pp::Breaks::{Consistent, Inconsistent};
use rustc_ast_pretty::pp::{self, Breaks}; use rustc_ast_pretty::pp::{self, Breaks};
@ -1296,12 +1296,11 @@ impl<'a> State<'a> {
} }
fn print_expr_binary(&mut self, op: hir::BinOp, lhs: &hir::Expr<'_>, rhs: &hir::Expr<'_>) { fn print_expr_binary(&mut self, op: hir::BinOp, lhs: &hir::Expr<'_>, rhs: &hir::Expr<'_>) {
let assoc_op = AssocOp::from_ast_binop(op.node); let binop_prec = op.node.precedence();
let binop_prec = assoc_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 assoc_op.fixity() { let (mut left_needs_paren, right_needs_paren) = match op.node.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),

View file

@ -1516,10 +1516,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
ty::ExprKind::Binop(op) => { ty::ExprKind::Binop(op) => {
let (_, _, c1, c2) = expr.binop_args(); let (_, _, c1, c2) = expr.binop_args();
let precedence = |binop: crate::mir::BinOp| { let precedence = |binop: crate::mir::BinOp| binop.to_hir_binop().precedence();
use rustc_ast::util::parser::AssocOp;
AssocOp::from_ast_binop(binop.to_hir_binop()).precedence()
};
let op_precedence = precedence(op); let op_precedence = precedence(op);
let formatted_op = op.to_hir_binop().as_str(); let formatted_op = op.to_hir_binop().as_str();
let (lhs_parenthesized, rhs_parenthesized) = match (c1.kind(), c2.kind()) { let (lhs_parenthesized, rhs_parenthesized) = match (c1.kind(), c2.kind()) {

View file

@ -1350,13 +1350,13 @@ impl<'a> Parser<'a> {
} }
return match (op.node, &outer_op.node) { return match (op.node, &outer_op.node) {
// `x == y == z` // `x == y == z`
(BinOpKind::Eq, AssocOp::Equal) | (BinOpKind::Eq, AssocOp::Binary(BinOpKind::Eq)) |
// `x < y < z` and friends. // `x < y < z` and friends.
(BinOpKind::Lt, AssocOp::Less | AssocOp::LessEqual) | (BinOpKind::Lt, AssocOp::Binary(BinOpKind::Lt | BinOpKind::Le)) |
(BinOpKind::Le, AssocOp::LessEqual | AssocOp::Less) | (BinOpKind::Le, AssocOp::Binary(BinOpKind::Lt | BinOpKind::Le)) |
// `x > y > z` and friends. // `x > y > z` and friends.
(BinOpKind::Gt, AssocOp::Greater | AssocOp::GreaterEqual) | (BinOpKind::Gt, AssocOp::Binary(BinOpKind::Gt | BinOpKind::Ge)) |
(BinOpKind::Ge, AssocOp::GreaterEqual | AssocOp::Greater) => { (BinOpKind::Ge, AssocOp::Binary(BinOpKind::Gt | BinOpKind::Ge)) => {
let expr_to_str = |e: &Expr| { let expr_to_str = |e: &Expr| {
self.span_to_snippet(e.span) self.span_to_snippet(e.span)
.unwrap_or_else(|_| pprust::expr_to_string(e)) .unwrap_or_else(|_| pprust::expr_to_string(e))
@ -1368,7 +1368,10 @@ impl<'a> Parser<'a> {
false // Keep the current parse behavior, where the AST is `(x < y) < z`. false // Keep the current parse behavior, where the AST is `(x < y) < z`.
} }
// `x == y < z` // `x == y < z`
(BinOpKind::Eq, AssocOp::Less | AssocOp::LessEqual | AssocOp::Greater | AssocOp::GreaterEqual) => { (
BinOpKind::Eq,
AssocOp::Binary(BinOpKind::Lt | BinOpKind::Le | BinOpKind::Gt | BinOpKind::Ge)
) => {
// Consume `z`/outer-op-rhs. // Consume `z`/outer-op-rhs.
let snapshot = self.create_snapshot_for_diagnostic(); let snapshot = self.create_snapshot_for_diagnostic();
match self.parse_expr() { match self.parse_expr() {
@ -1389,7 +1392,10 @@ impl<'a> Parser<'a> {
} }
} }
// `x > y == z` // `x > y == z`
(BinOpKind::Lt | BinOpKind::Le | BinOpKind::Gt | BinOpKind::Ge, AssocOp::Equal) => { (
BinOpKind::Lt | BinOpKind::Le | BinOpKind::Gt | BinOpKind::Ge,
AssocOp::Binary(BinOpKind::Eq)
) => {
let snapshot = self.create_snapshot_for_diagnostic(); let snapshot = self.create_snapshot_for_diagnostic();
// At this point it is always valid to enclose the lhs in parentheses, no // At this point it is always valid to enclose the lhs in parentheses, no
// further checks are necessary. // further checks are necessary.
@ -1457,10 +1463,10 @@ impl<'a> Parser<'a> {
// Include `<` to provide this recommendation even in a case like // Include `<` to provide this recommendation even in a case like
// `Foo<Bar<Baz<Qux, ()>>>` // `Foo<Bar<Baz<Qux, ()>>>`
if op.node == BinOpKind::Lt && outer_op.node == AssocOp::Less if op.node == BinOpKind::Lt && outer_op.node == AssocOp::Binary(BinOpKind::Lt)
|| outer_op.node == AssocOp::Greater || outer_op.node == AssocOp::Binary(BinOpKind::Gt)
{ {
if outer_op.node == AssocOp::Less { if outer_op.node == AssocOp::Binary(BinOpKind::Lt) {
let snapshot = self.create_snapshot_for_diagnostic(); let snapshot = self.create_snapshot_for_diagnostic();
self.bump(); self.bump();
// So far we have parsed `foo<bar<`, consume the rest of the type args. // So far we have parsed `foo<bar<`, consume the rest of the type args.
@ -2635,10 +2641,12 @@ impl<'a> Parser<'a> {
) -> PResult<'a, GenericArg> { ) -> PResult<'a, GenericArg> {
let is_op_or_dot = AssocOp::from_token(&self.token) let is_op_or_dot = AssocOp::from_token(&self.token)
.and_then(|op| { .and_then(|op| {
if let AssocOp::Greater if let AssocOp::Binary(
| AssocOp::Less BinOpKind::Gt
| AssocOp::ShiftRight | BinOpKind::Lt
| AssocOp::GreaterEqual | BinOpKind::Shr
| BinOpKind::Ge
)
// Don't recover from `foo::<bar = baz>`, because this could be an attempt to // Don't recover from `foo::<bar = baz>`, because this could be an attempt to
// assign a value to a defaulted generic parameter. // assign a value to a defaulted generic parameter.
| AssocOp::Assign | AssocOp::Assign

View file

@ -188,17 +188,12 @@ impl<'a> Parser<'a> {
} }
// Look for JS' `===` and `!==` and recover // Look for JS' `===` and `!==` and recover
if (op.node == AssocOp::Equal || op.node == AssocOp::NotEqual) if let AssocOp::Binary(bop @ BinOpKind::Eq | bop @ BinOpKind::Ne) = op.node
&& self.token == token::Eq && self.token == token::Eq
&& self.prev_token.span.hi() == self.token.span.lo() && self.prev_token.span.hi() == self.token.span.lo()
{ {
let sp = op.span.to(self.token.span); let sp = op.span.to(self.token.span);
let sugg = match op.node { let sugg = bop.as_str().into();
AssocOp::Equal => "==",
AssocOp::NotEqual => "!=",
_ => unreachable!(),
}
.into();
let invalid = format!("{sugg}="); let invalid = format!("{sugg}=");
self.dcx().emit_err(errors::InvalidComparisonOperator { self.dcx().emit_err(errors::InvalidComparisonOperator {
span: sp, span: sp,
@ -213,7 +208,7 @@ impl<'a> Parser<'a> {
} }
// Look for PHP's `<>` and recover // Look for PHP's `<>` and recover
if op.node == AssocOp::Less if op.node == AssocOp::Binary(BinOpKind::Lt)
&& self.token == token::Gt && self.token == token::Gt
&& self.prev_token.span.hi() == self.token.span.lo() && self.prev_token.span.hi() == self.token.span.lo()
{ {
@ -231,7 +226,7 @@ impl<'a> Parser<'a> {
} }
// Look for C++'s `<=>` and recover // Look for C++'s `<=>` and recover
if op.node == AssocOp::LessEqual if op.node == AssocOp::Binary(BinOpKind::Le)
&& self.token == token::Gt && self.token == token::Gt
&& self.prev_token.span.hi() == self.token.span.lo() && self.prev_token.span.hi() == self.token.span.lo()
{ {
@ -290,25 +285,7 @@ impl<'a> Parser<'a> {
let span = self.mk_expr_sp(&lhs, lhs_span, rhs.span); let span = self.mk_expr_sp(&lhs, lhs_span, rhs.span);
lhs = match op { lhs = match op {
AssocOp::Add AssocOp::Binary(ast_op) => {
| AssocOp::Subtract
| AssocOp::Multiply
| AssocOp::Divide
| AssocOp::Modulus
| AssocOp::LAnd
| AssocOp::LOr
| AssocOp::BitXor
| AssocOp::BitAnd
| AssocOp::BitOr
| AssocOp::ShiftLeft
| AssocOp::ShiftRight
| AssocOp::Equal
| AssocOp::Less
| AssocOp::LessEqual
| AssocOp::NotEqual
| AssocOp::Greater
| AssocOp::GreaterEqual => {
let ast_op = op.to_ast_binop().unwrap();
let binary = self.mk_binary(source_map::respan(cur_op_span, ast_op), lhs, rhs); let binary = self.mk_binary(source_map::respan(cur_op_span, ast_op), lhs, rhs);
self.mk_expr(span, binary) self.mk_expr(span, binary)
} }
@ -335,13 +312,14 @@ impl<'a> Parser<'a> {
// An exhaustive check is done in the following block, but these are checked first // An exhaustive check is done in the following block, but these are checked first
// because they *are* ambiguous but also reasonable looking incorrect syntax, so we // because they *are* ambiguous but also reasonable looking incorrect syntax, so we
// want to keep their span info to improve diagnostics in these cases in a later stage. // want to keep their span info to improve diagnostics in these cases in a later stage.
(true, Some(AssocOp::Multiply)) | // `{ 42 } *foo = bar;` or `{ 42 } * 3` (true, Some(AssocOp::Binary(
(true, Some(AssocOp::Subtract)) | // `{ 42 } -5` BinOpKind::Mul | // `{ 42 } *foo = bar;` or `{ 42 } * 3`
(true, Some(AssocOp::Add)) | // `{ 42 } + 42` (unary plus) BinOpKind::Sub | // `{ 42 } -5`
(true, Some(AssocOp::LAnd)) | // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }` BinOpKind::Add | // `{ 42 } + 42` (unary plus)
(true, Some(AssocOp::LOr)) | // `{ 42 } || 42` ("logical or" or closure) BinOpKind::And | // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }`
(true, Some(AssocOp::BitOr)) // `{ 42 } | 42` or `{ 42 } |x| 42` BinOpKind::Or | // `{ 42 } || 42` ("logical or" or closure)
=> { BinOpKind::BitOr // `{ 42 } | 42` or `{ 42 } |x| 42`
))) => {
// These cases are ambiguous and can't be identified in the parser alone. // These cases are ambiguous and can't be identified in the parser alone.
// //
// Bitwise AND is left out because guessing intent is hard. We can make // Bitwise AND is left out because guessing intent is hard. We can make
@ -380,21 +358,20 @@ impl<'a> Parser<'a> {
// When parsing const expressions, stop parsing when encountering `>`. // When parsing const expressions, stop parsing when encountering `>`.
( (
Some( Some(
AssocOp::ShiftRight AssocOp::Binary(BinOpKind::Shr | BinOpKind::Gt | BinOpKind::Ge)
| AssocOp::Greater
| AssocOp::GreaterEqual
| AssocOp::AssignOp(BinOpKind::Shr), | AssocOp::AssignOp(BinOpKind::Shr),
), ),
_, _,
) if self.restrictions.contains(Restrictions::CONST_EXPR) => { ) if self.restrictions.contains(Restrictions::CONST_EXPR) => {
return None; return None;
} }
// When recovering patterns as expressions, stop parsing when encountering an assignment `=`, an alternative `|`, or a range `..`. // When recovering patterns as expressions, stop parsing when encountering an
// assignment `=`, an alternative `|`, or a range `..`.
( (
Some( Some(
AssocOp::Assign AssocOp::Assign
| AssocOp::AssignOp(_) | AssocOp::AssignOp(_)
| AssocOp::BitOr | AssocOp::Binary(BinOpKind::BitOr)
| AssocOp::DotDot | AssocOp::DotDot
| AssocOp::DotDotEq, | AssocOp::DotDotEq,
), ),
@ -411,7 +388,7 @@ impl<'a> Parser<'a> {
incorrect: "and".into(), incorrect: "and".into(),
sub: errors::InvalidLogicalOperatorSub::Conjunction(self.token.span), sub: errors::InvalidLogicalOperatorSub::Conjunction(self.token.span),
}); });
(AssocOp::LAnd, span) (AssocOp::Binary(BinOpKind::And), span)
} }
(None, Some((Ident { name: sym::or, span }, IdentIsRaw::No))) if self.may_recover() => { (None, Some((Ident { name: sym::or, span }, IdentIsRaw::No))) if self.may_recover() => {
self.dcx().emit_err(errors::InvalidLogicalOperator { self.dcx().emit_err(errors::InvalidLogicalOperator {
@ -419,7 +396,7 @@ impl<'a> Parser<'a> {
incorrect: "or".into(), incorrect: "or".into(),
sub: errors::InvalidLogicalOperatorSub::Disjunction(self.token.span), sub: errors::InvalidLogicalOperatorSub::Disjunction(self.token.span),
}); });
(AssocOp::LOr, span) (AssocOp::Binary(BinOpKind::Or), span)
} }
_ => return None, _ => return None,
}; };

View file

@ -50,7 +50,7 @@ pub(crate) fn check<'tcx>(
// format the suggestion // format the suggestion
let suggestion = format!( let suggestion = format!(
"{}.abs()", "{}.abs()",
sugg::make_assoc(AssocOp::Subtract, &sug_l, &sug_r).maybe_par() sugg::make_assoc(AssocOp::Binary(BinOpKind::Sub), &sug_l, &sug_r).maybe_par()
); );
// spans the lint // spans the lint
span_lint_and_then( span_lint_and_then(

View file

@ -160,7 +160,7 @@ impl<'a> Sugg<'a> {
Sugg::BinOp(AssocOp::AssignOp(op.node), get_snippet(lhs.span), get_snippet(rhs.span)) Sugg::BinOp(AssocOp::AssignOp(op.node), get_snippet(lhs.span), get_snippet(rhs.span))
}, },
ExprKind::Binary(op, lhs, rhs) => Sugg::BinOp( ExprKind::Binary(op, lhs, rhs) => Sugg::BinOp(
AssocOp::from_ast_binop(op.node), AssocOp::Binary(op.node),
get_snippet(lhs.span), get_snippet(lhs.span),
get_snippet(rhs.span), get_snippet(rhs.span),
), ),
@ -249,7 +249,7 @@ impl<'a> Sugg<'a> {
snippet(rhs.span), snippet(rhs.span),
), ),
ast::ExprKind::Binary(op, ref lhs, ref rhs) => Sugg::BinOp( ast::ExprKind::Binary(op, ref lhs, ref rhs) => Sugg::BinOp(
AssocOp::from_ast_binop(op.node), AssocOp::Binary(op.node),
snippet(lhs.span), snippet(lhs.span),
snippet(rhs.span), snippet(rhs.span),
), ),
@ -366,30 +366,9 @@ impl<'a> Sugg<'a> {
/// Generates a string from the operator and both sides. /// Generates a string from the operator and both sides.
fn binop_to_string(op: AssocOp, lhs: &str, rhs: &str) -> String { fn binop_to_string(op: AssocOp, lhs: &str, rhs: &str) -> String {
match op { match op {
AssocOp::Add AssocOp::Binary(op) => format!("{lhs} {} {rhs}", op.as_str()),
| AssocOp::Subtract
| AssocOp::Multiply
| AssocOp::Divide
| AssocOp::Modulus
| AssocOp::LAnd
| AssocOp::LOr
| AssocOp::BitXor
| AssocOp::BitAnd
| AssocOp::BitOr
| AssocOp::ShiftLeft
| AssocOp::ShiftRight
| AssocOp::Equal
| AssocOp::Less
| AssocOp::LessEqual
| AssocOp::NotEqual
| AssocOp::Greater
| AssocOp::GreaterEqual => {
format!("{lhs} {} {rhs}", op.to_ast_binop().expect("Those are AST ops").as_str())
},
AssocOp::Assign => format!("{lhs} = {rhs}"), AssocOp::Assign => format!("{lhs} = {rhs}"),
AssocOp::AssignOp(op) => { AssocOp::AssignOp(op) => format!("{lhs} {}= {rhs}", op.as_str()),
format!("{lhs} {}= {rhs}", op.as_str())
},
AssocOp::As => format!("{lhs} as {rhs}"), AssocOp::As => format!("{lhs} as {rhs}"),
AssocOp::DotDot => format!("{lhs}..{rhs}"), AssocOp::DotDot => format!("{lhs}..{rhs}"),
AssocOp::DotDotEq => format!("{lhs}..={rhs}"), AssocOp::DotDotEq => format!("{lhs}..={rhs}"),
@ -476,16 +455,17 @@ impl Neg for Sugg<'_> {
impl<'a> Not for Sugg<'a> { impl<'a> Not for Sugg<'a> {
type Output = Sugg<'a>; type Output = Sugg<'a>;
fn not(self) -> Sugg<'a> { fn not(self) -> Sugg<'a> {
use AssocOp::{Equal, Greater, GreaterEqual, Less, LessEqual, NotEqual}; use AssocOp::Binary;
use ast::BinOpKind::{Eq, Gt, Ge, Lt, Le, Ne};
if let Sugg::BinOp(op, lhs, rhs) = self { if let Sugg::BinOp(op, lhs, rhs) = self {
let to_op = match op { let to_op = match op {
Equal => NotEqual, Binary(Eq) => Binary(Ne),
NotEqual => Equal, Binary(Ne) => Binary(Eq),
Less => GreaterEqual, Binary(Lt) => Binary(Ge),
GreaterEqual => Less, Binary(Ge) => Binary(Lt),
Greater => LessEqual, Binary(Gt) => Binary(Le),
LessEqual => Greater, Binary(Le) => Binary(Gt),
_ => return make_unop("!", Sugg::BinOp(op, lhs, rhs)), _ => return make_unop("!", Sugg::BinOp(op, lhs, rhs)),
}; };
Sugg::BinOp(to_op, lhs, rhs) Sugg::BinOp(to_op, lhs, rhs)
@ -537,7 +517,7 @@ pub fn make_unop(op: &str, expr: Sugg<'_>) -> Sugg<'static> {
pub fn make_assoc(op: AssocOp, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static> { pub fn make_assoc(op: AssocOp, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static> {
/// Returns `true` if the operator is a shift operator `<<` or `>>`. /// Returns `true` if the operator is a shift operator `<<` or `>>`.
fn is_shift(op: AssocOp) -> bool { fn is_shift(op: AssocOp) -> bool {
matches!(op, AssocOp::ShiftLeft | AssocOp::ShiftRight) matches!(op, AssocOp::Binary(ast::BinOpKind::Shl | ast::BinOpKind::Shr))
} }
/// Returns `true` if the operator is an arithmetic operator /// Returns `true` if the operator is an arithmetic operator
@ -545,7 +525,13 @@ pub fn make_assoc(op: AssocOp, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static>
fn is_arith(op: AssocOp) -> bool { fn is_arith(op: AssocOp) -> bool {
matches!( matches!(
op, op,
AssocOp::Add | AssocOp::Subtract | AssocOp::Multiply | AssocOp::Divide | AssocOp::Modulus AssocOp::Binary(
ast::BinOpKind::Add
| ast::BinOpKind::Sub
| ast::BinOpKind::Mul
| ast::BinOpKind::Div
| ast::BinOpKind::Rem
)
) )
} }
@ -577,9 +563,9 @@ pub fn make_assoc(op: AssocOp, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static>
Sugg::BinOp(op, lhs.into(), rhs.into()) Sugg::BinOp(op, lhs.into(), rhs.into())
} }
/// Convenience wrapper around `make_assoc` and `AssocOp::from_ast_binop`. /// Convenience wrapper around `make_assoc` and `AssocOp::Binary`.
pub fn make_binop(op: ast::BinOpKind, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static> { pub fn make_binop(op: ast::BinOpKind, lhs: &Sugg<'_>, rhs: &Sugg<'_>) -> Sugg<'static> {
make_assoc(AssocOp::from_ast_binop(op), lhs, rhs) make_assoc(AssocOp::Binary(op), lhs, rhs)
} }
#[derive(PartialEq, Eq, Clone, Copy)] #[derive(PartialEq, Eq, Clone, Copy)]
@ -604,16 +590,15 @@ enum Associativity {
/// associative. /// associative.
#[must_use] #[must_use]
fn associativity(op: AssocOp) -> Associativity { fn associativity(op: AssocOp) -> Associativity {
use rustc_ast::util::parser::AssocOp::{ use rustc_ast::util::parser::AssocOp::{As, Assign, AssignOp, Binary, DotDot, DotDotEq};
Add, As, Assign, AssignOp, BitAnd, BitOr, BitXor, Divide, DotDot, DotDotEq, Equal, Greater, GreaterEqual, LAnd, use ast::BinOpKind::{
LOr, Less, LessEqual, Modulus, Multiply, NotEqual, ShiftLeft, ShiftRight, Subtract, Add, BitAnd, BitOr, BitXor, Div, Eq, Gt, Ge, And, Or, Lt, Le, Rem, Mul, Ne, Shl, Shr, Sub,
}; };
match op { match op {
Assign | AssignOp(_) => Associativity::Right, Assign | AssignOp(_) => Associativity::Right,
Add | BitAnd | BitOr | BitXor | LAnd | LOr | Multiply | As => Associativity::Both, Binary(Add | BitAnd | BitOr | BitXor | And | Or | Mul) | As => Associativity::Both,
Divide | Equal | Greater | GreaterEqual | Less | LessEqual | Modulus | NotEqual | ShiftLeft | ShiftRight Binary(Div | Eq | Gt | Ge | Lt | Le | Rem | Ne | Shl | Shr | Sub) => Associativity::Left,
| Subtract => Associativity::Left,
DotDot | DotDotEq => Associativity::None, DotDot | DotDotEq => Associativity::None,
} }
} }