summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_ast/src/util/parser.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_ast/src/util/parser.rs')
-rw-r--r--compiler/rustc_ast/src/util/parser.rs406
1 files changed, 406 insertions, 0 deletions
diff --git a/compiler/rustc_ast/src/util/parser.rs b/compiler/rustc_ast/src/util/parser.rs
new file mode 100644
index 000000000..74b7fe9e2
--- /dev/null
+++ b/compiler/rustc_ast/src/util/parser.rs
@@ -0,0 +1,406 @@
+use crate::ast::{self, BinOpKind};
+use crate::token::{self, BinOpToken, Token};
+use rustc_span::symbol::kw;
+
+/// Associative operator with precedence.
+///
+/// This is the enum which specifies operator precedence and fixity to the parser.
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub enum AssocOp {
+ /// `+`
+ Add,
+ /// `-`
+ Subtract,
+ /// `*`
+ Multiply,
+ /// `/`
+ Divide,
+ /// `%`
+ Modulus,
+ /// `&&`
+ LAnd,
+ /// `||`
+ LOr,
+ /// `^`
+ BitXor,
+ /// `&`
+ BitAnd,
+ /// `|`
+ BitOr,
+ /// `<<`
+ ShiftLeft,
+ /// `>>`
+ ShiftRight,
+ /// `==`
+ Equal,
+ /// `<`
+ Less,
+ /// `<=`
+ LessEqual,
+ /// `!=`
+ NotEqual,
+ /// `>`
+ Greater,
+ /// `>=`
+ GreaterEqual,
+ /// `=`
+ Assign,
+ /// `?=` where ? is one of the BinOpToken
+ AssignOp(BinOpToken),
+ /// `as`
+ As,
+ /// `..` range
+ DotDot,
+ /// `..=` range
+ DotDotEq,
+ /// `:`
+ Colon,
+}
+
+#[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::BinOpEq(k) => Some(AssignOp(k)),
+ token::Eq => Some(Assign),
+ token::BinOp(BinOpToken::Star) => Some(Multiply),
+ token::BinOp(BinOpToken::Slash) => Some(Divide),
+ token::BinOp(BinOpToken::Percent) => Some(Modulus),
+ token::BinOp(BinOpToken::Plus) => Some(Add),
+ token::BinOp(BinOpToken::Minus) => Some(Subtract),
+ token::BinOp(BinOpToken::Shl) => Some(ShiftLeft),
+ token::BinOp(BinOpToken::Shr) => Some(ShiftRight),
+ token::BinOp(BinOpToken::And) => Some(BitAnd),
+ token::BinOp(BinOpToken::Caret) => Some(BitXor),
+ token::BinOp(BinOpToken::Or) => Some(BitOr),
+ 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::DotDotEq => Some(DotDotEq),
+ // DotDotDot is no longer supported, but we need some way to display the error
+ token::DotDotDot => Some(DotDotEq),
+ token::Colon => Some(Colon),
+ // `<-` should probably be `< -`
+ token::LArrow => Some(Less),
+ _ if t.is_keyword(kw::As) => Some(As),
+ _ => 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
+ pub fn precedence(&self) -> usize {
+ use AssocOp::*;
+ match *self {
+ As | Colon => 14,
+ Multiply | Divide | Modulus => 13,
+ Add | Subtract => 12,
+ ShiftLeft | ShiftRight => 11,
+ BitAnd => 10,
+ BitXor => 9,
+ BitOr => 8,
+ Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => 7,
+ LAnd => 6,
+ LOr => 5,
+ DotDot | DotDotEq => 4,
+ Assign | AssignOp(_) => 2,
+ }
+ }
+
+ /// 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,
+ As | Multiply | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd
+ | BitXor | BitOr | Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual
+ | LAnd | LOr | Colon => Fixity::Left,
+ DotDot | DotDotEq => Fixity::None,
+ }
+ }
+
+ pub fn is_comparison(&self) -> bool {
+ use AssocOp::*;
+ match *self {
+ Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => true,
+ Assign | AssignOp(_) | As | Multiply | Divide | Modulus | Add | Subtract
+ | ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd | LOr | DotDot | DotDotEq
+ | Colon => false,
+ }
+ }
+
+ pub fn is_assign_like(&self) -> bool {
+ use AssocOp::*;
+ match *self {
+ Assign | AssignOp(_) => true,
+ Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | As | Multiply
+ | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd | BitXor
+ | BitOr | LAnd | LOr | DotDot | DotDotEq | Colon => 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 | Colon => None,
+ }
+ }
+
+ /// 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::*;
+ matches!(
+ self,
+ BitXor | // `{ 42 } ^ 3`
+ Assign | // `{ 42 } = { 42 }`
+ Divide | // `{ 42 } / 42`
+ Modulus | // `{ 42 } % 2`
+ ShiftRight | // `{ 42 } >> 2`
+ LessEqual | // `{ 42 } <= 3`
+ Greater | // `{ 42 } > 3`
+ GreaterEqual | // `{ 42 } >= 3`
+ AssignOp(_) | // `{ 42 } +=`
+ As | // `{ 42 } as usize`
+ // Equal | // `{ 42 } == { 42 }` Accepting these here would regress incorrect
+ // NotEqual | // `{ 42 } != { 42 } struct literals parser recovery.
+ Colon, // `{ 42 }: usize`
+ )
+ }
+}
+
+pub const PREC_CLOSURE: i8 = -40;
+pub const PREC_JUMP: i8 = -30;
+pub const PREC_RANGE: i8 = -10;
+// The range 2..=14 is reserved for AssocOp binary operator precedences.
+pub const PREC_PREFIX: i8 = 50;
+pub const PREC_POSTFIX: i8 = 60;
+pub const PREC_PAREN: i8 = 99;
+pub const PREC_FORCE_PAREN: i8 = 100;
+
+#[derive(Debug, Clone, Copy)]
+pub enum ExprPrecedence {
+ Closure,
+ Break,
+ Continue,
+ Ret,
+ Yield,
+ Yeet,
+
+ Range,
+
+ Binary(BinOpKind),
+
+ Cast,
+ Type,
+
+ Assign,
+ AssignOp,
+
+ Box,
+ AddrOf,
+ Let,
+ Unary,
+
+ Call,
+ MethodCall,
+ Field,
+ Index,
+ Try,
+ InlineAsm,
+ Mac,
+
+ Array,
+ Repeat,
+ Tup,
+ Lit,
+ Path,
+ Paren,
+ If,
+ While,
+ ForLoop,
+ Loop,
+ Match,
+ ConstBlock,
+ Block,
+ TryBlock,
+ Struct,
+ Async,
+ Await,
+ Err,
+}
+
+impl ExprPrecedence {
+ pub fn order(self) -> i8 {
+ match self {
+ ExprPrecedence::Closure => PREC_CLOSURE,
+
+ ExprPrecedence::Break |
+ ExprPrecedence::Continue |
+ ExprPrecedence::Ret |
+ ExprPrecedence::Yield |
+ ExprPrecedence::Yeet => PREC_JUMP,
+
+ // `Range` claims to have higher precedence than `Assign`, but `x .. x = x` fails to
+ // parse, instead of parsing as `(x .. x) = x`. Giving `Range` a lower precedence
+ // ensures that `pprust` will add parentheses in the right places to get the desired
+ // parse.
+ ExprPrecedence::Range => PREC_RANGE,
+
+ // Binop-like expr kinds, handled by `AssocOp`.
+ ExprPrecedence::Binary(op) => AssocOp::from_ast_binop(op).precedence() as i8,
+ ExprPrecedence::Cast => AssocOp::As.precedence() as i8,
+ ExprPrecedence::Type => AssocOp::Colon.precedence() as i8,
+
+ ExprPrecedence::Assign |
+ ExprPrecedence::AssignOp => AssocOp::Assign.precedence() as i8,
+
+ // Unary, prefix
+ ExprPrecedence::Box |
+ ExprPrecedence::AddrOf |
+ // Here `let pats = expr` has `let pats =` as a "unary" prefix of `expr`.
+ // However, this is not exactly right. When `let _ = a` is the LHS of a binop we
+ // need parens sometimes. E.g. we can print `(let _ = a) && b` as `let _ = a && b`
+ // but we need to print `(let _ = a) < b` as-is with parens.
+ ExprPrecedence::Let |
+ ExprPrecedence::Unary => PREC_PREFIX,
+
+ // Unary, postfix
+ ExprPrecedence::Await |
+ ExprPrecedence::Call |
+ ExprPrecedence::MethodCall |
+ ExprPrecedence::Field |
+ ExprPrecedence::Index |
+ ExprPrecedence::Try |
+ ExprPrecedence::InlineAsm |
+ ExprPrecedence::Mac => PREC_POSTFIX,
+
+ // Never need parens
+ ExprPrecedence::Array |
+ ExprPrecedence::Repeat |
+ ExprPrecedence::Tup |
+ ExprPrecedence::Lit |
+ ExprPrecedence::Path |
+ ExprPrecedence::Paren |
+ ExprPrecedence::If |
+ ExprPrecedence::While |
+ ExprPrecedence::ForLoop |
+ ExprPrecedence::Loop |
+ ExprPrecedence::Match |
+ ExprPrecedence::ConstBlock |
+ ExprPrecedence::Block |
+ ExprPrecedence::TryBlock |
+ ExprPrecedence::Async |
+ ExprPrecedence::Struct |
+ ExprPrecedence::Err => PREC_PAREN,
+ }
+ }
+}
+
+/// In `let p = e`, operators with precedence `<=` this one requires parentheses in `e`.
+pub fn prec_let_scrutinee_needs_par() -> usize {
+ AssocOp::LAnd.precedence()
+}
+
+/// 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: i8) -> bool {
+ order <= prec_let_scrutinee_needs_par() as i8
+}
+
+/// 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(ref lhs, ref rhs, _)
+ | ast::ExprKind::AssignOp(_, ref lhs, ref rhs)
+ | ast::ExprKind::Binary(_, ref lhs, ref rhs) => {
+ // X { y: 1 } + X { y: 2 }
+ contains_exterior_struct_lit(&lhs) || contains_exterior_struct_lit(&rhs)
+ }
+ ast::ExprKind::Await(ref x)
+ | ast::ExprKind::Unary(_, ref x)
+ | ast::ExprKind::Cast(ref x, _)
+ | ast::ExprKind::Type(ref x, _)
+ | ast::ExprKind::Field(ref x, _)
+ | ast::ExprKind::Index(ref x, _) => {
+ // &X { y: 1 }, X { y: 1 }.y
+ contains_exterior_struct_lit(&x)
+ }
+
+ ast::ExprKind::MethodCall(.., ref exprs, _) => {
+ // X { y: 1 }.bar(...)
+ contains_exterior_struct_lit(&exprs[0])
+ }
+
+ _ => false,
+ }
+}