| use rustc_span::kw; | |
| use crate::ast::{self, AssignOpKind, BinOpKind, RangeLimits}; | |
| use crate::token::{self, Token}; | |
| /// Associative operator. | |
| #[derive(Copy, Clone, PartialEq, Debug)] | |
| pub enum AssocOp { | |
| /// A binary op. | |
| Binary(BinOpKind), | |
| /// `?=` where ? is one of the assignable BinOps | |
| AssignOp(AssignOpKind), | |
| /// `=` | |
| Assign, | |
| /// `as` | |
| Cast, | |
| /// `..` or `..=` range | |
| Range(RangeLimits), | |
| } | |
| #[derive(PartialEq, Debug)] | |
| pub enum Fixity { | |
| /// The operator is left-associative | |
| Left, | |
| /// The operator is right-associative | |
| Right, | |
| /// The operator is not associative | |
| None, | |
| } | |
| impl AssocOp { | |
| /// Creates a new AssocOp from a token. | |
| pub fn from_token(t: &Token) -> Option<AssocOp> { | |
| use AssocOp::*; | |
| match t.kind { | |
| token::Eq => Some(Assign), | |
| token::Plus => Some(Binary(BinOpKind::Add)), | |
| token::Minus => Some(Binary(BinOpKind::Sub)), | |
| token::Star => Some(Binary(BinOpKind::Mul)), | |
| token::Slash => Some(Binary(BinOpKind::Div)), | |
| token::Percent => Some(Binary(BinOpKind::Rem)), | |
| token::Caret => Some(Binary(BinOpKind::BitXor)), | |
| token::And => Some(Binary(BinOpKind::BitAnd)), | |
| token::Or => Some(Binary(BinOpKind::BitOr)), | |
| token::Shl => Some(Binary(BinOpKind::Shl)), | |
| token::Shr => Some(Binary(BinOpKind::Shr)), | |
| token::PlusEq => Some(AssignOp(AssignOpKind::AddAssign)), | |
| token::MinusEq => Some(AssignOp(AssignOpKind::SubAssign)), | |
| token::StarEq => Some(AssignOp(AssignOpKind::MulAssign)), | |
| token::SlashEq => Some(AssignOp(AssignOpKind::DivAssign)), | |
| token::PercentEq => Some(AssignOp(AssignOpKind::RemAssign)), | |
| token::CaretEq => Some(AssignOp(AssignOpKind::BitXorAssign)), | |
| token::AndEq => Some(AssignOp(AssignOpKind::BitAndAssign)), | |
| token::OrEq => Some(AssignOp(AssignOpKind::BitOrAssign)), | |
| token::ShlEq => Some(AssignOp(AssignOpKind::ShlAssign)), | |
| token::ShrEq => Some(AssignOp(AssignOpKind::ShrAssign)), | |
| token::Lt => Some(Binary(BinOpKind::Lt)), | |
| token::Le => Some(Binary(BinOpKind::Le)), | |
| token::Ge => Some(Binary(BinOpKind::Ge)), | |
| token::Gt => Some(Binary(BinOpKind::Gt)), | |
| token::EqEq => Some(Binary(BinOpKind::Eq)), | |
| token::Ne => Some(Binary(BinOpKind::Ne)), | |
| token::AndAnd => Some(Binary(BinOpKind::And)), | |
| token::OrOr => Some(Binary(BinOpKind::Or)), | |
| token::DotDot => Some(Range(RangeLimits::HalfOpen)), | |
| // DotDotDot is no longer supported, but we need some way to display the error | |
| token::DotDotEq | token::DotDotDot => Some(Range(RangeLimits::Closed)), | |
| // `<-` should probably be `< -` | |
| token::LArrow => Some(Binary(BinOpKind::Lt)), | |
| _ if t.is_keyword(kw::As) => Some(Cast), | |
| _ => None, | |
| } | |
| } | |
| /// Gets the precedence of this operator | |
| pub fn precedence(&self) -> ExprPrecedence { | |
| use AssocOp::*; | |
| match *self { | |
| Cast => ExprPrecedence::Cast, | |
| Binary(bin_op) => bin_op.precedence(), | |
| Range(_) => ExprPrecedence::Range, | |
| Assign | AssignOp(_) => ExprPrecedence::Assign, | |
| } | |
| } | |
| /// Gets the fixity of this operator | |
| pub fn fixity(&self) -> Fixity { | |
| use AssocOp::*; | |
| // NOTE: it is a bug to have an operators that has same precedence but different fixities! | |
| match *self { | |
| Assign | AssignOp(_) => Fixity::Right, | |
| Binary(binop) => binop.fixity(), | |
| Cast => Fixity::Left, | |
| Range(_) => Fixity::None, | |
| } | |
| } | |
| pub fn is_comparison(&self) -> bool { | |
| use AssocOp::*; | |
| match *self { | |
| Binary(binop) => binop.is_comparison(), | |
| Assign | AssignOp(_) | Cast | Range(_) => false, | |
| } | |
| } | |
| pub fn is_assign_like(&self) -> bool { | |
| use AssocOp::*; | |
| match *self { | |
| Assign | AssignOp(_) => true, | |
| Cast | Binary(_) | Range(_) => false, | |
| } | |
| } | |
| /// This operator could be used to follow a block unambiguously. | |
| /// | |
| /// This is used for error recovery at the moment, providing a suggestion to wrap blocks with | |
| /// parentheses while having a high degree of confidence on the correctness of the suggestion. | |
| pub fn can_continue_expr_unambiguously(&self) -> bool { | |
| use AssocOp::*; | |
| use BinOpKind::*; | |
| matches!( | |
| self, | |
| Assign | // `{ 42 } = { 42 }` | |
| Binary( | |
| BitXor | // `{ 42 } ^ 3` | |
| Div | // `{ 42 } / 42` | |
| Rem | // `{ 42 } % 2` | |
| Shr | // `{ 42 } >> 2` | |
| Le | // `{ 42 } <= 3` | |
| Gt | // `{ 42 } > 3` | |
| Ge // `{ 42 } >= 3` | |
| ) | | |
| AssignOp(_) | // `{ 42 } +=` | |
| // Equal | // `{ 42 } == { 42 }` Accepting these here would regress incorrect | |
| // NotEqual | // `{ 42 } != { 42 } struct literals parser recovery. | |
| Cast // `{ 42 } as usize` | |
| ) | |
| } | |
| } | |
| #[derive(Clone, Copy, PartialEq, PartialOrd)] | |
| pub enum ExprPrecedence { | |
| // return, break, yield, closures | |
| Jump, | |
| // = += -= *= /= %= &= |= ^= <<= >>= | |
| Assign, | |
| // .. ..= | |
| Range, | |
| // || | |
| LOr, | |
| // && | |
| LAnd, | |
| // == != < > <= >= | |
| Compare, | |
| // | | |
| BitOr, | |
| // ^ | |
| BitXor, | |
| // & | |
| BitAnd, | |
| // << >> | |
| Shift, | |
| // + - | |
| Sum, | |
| // * / % | |
| Product, | |
| // as | |
| Cast, | |
| // unary - * ! & &mut | |
| Prefix, | |
| // paths, loops, function calls, array indexing, field expressions, method calls | |
| Unambiguous, | |
| } | |
| /// In `let p = e`, operators with precedence `<=` this one requires parentheses in `e`. | |
| pub fn prec_let_scrutinee_needs_par() -> ExprPrecedence { | |
| ExprPrecedence::LAnd | |
| } | |
| /// Suppose we have `let _ = e` and the `order` of `e`. | |
| /// Is the `order` such that `e` in `let _ = e` needs parentheses when it is on the RHS? | |
| /// | |
| /// Conversely, suppose that we have `(let _ = a) OP b` and `order` is that of `OP`. | |
| /// Can we print this as `let _ = a OP b`? | |
| pub fn needs_par_as_let_scrutinee(order: ExprPrecedence) -> bool { | |
| order <= prec_let_scrutinee_needs_par() | |
| } | |
| /// Expressions that syntactically contain an "exterior" struct literal i.e., not surrounded by any | |
| /// parens or other delimiters, e.g., `X { y: 1 }`, `X { y: 1 }.method()`, `foo == X { y: 1 }` and | |
| /// `X { y: 1 } == foo` all do, but `(X { y: 1 }) == foo` does not. | |
| pub fn contains_exterior_struct_lit(value: &ast::Expr) -> bool { | |
| match &value.kind { | |
| ast::ExprKind::Struct(..) => true, | |
| ast::ExprKind::Assign(lhs, rhs, _) | |
| | ast::ExprKind::AssignOp(_, lhs, rhs) | |
| | ast::ExprKind::Binary(_, lhs, rhs) => { | |
| // X { y: 1 } + X { y: 2 } | |
| contains_exterior_struct_lit(lhs) || contains_exterior_struct_lit(rhs) | |
| } | |
| ast::ExprKind::Await(x, _) | |
| | ast::ExprKind::Unary(_, x) | |
| | ast::ExprKind::Cast(x, _) | |
| | ast::ExprKind::Type(x, _) | |
| | ast::ExprKind::Field(x, _) | |
| | ast::ExprKind::Index(x, _, _) | |
| | ast::ExprKind::Match(x, _, ast::MatchKind::Postfix) => { | |
| // &X { y: 1 }, X { y: 1 }.y | |
| contains_exterior_struct_lit(x) | |
| } | |
| ast::ExprKind::MethodCall(ast::MethodCall { receiver, .. }) => { | |
| // X { y: 1 }.bar(...) | |
| contains_exterior_struct_lit(receiver) | |
| } | |
| _ => false, | |
| } | |
| } |