rustc_ast/util/
parser.rs

1use rustc_span::kw;
2
3use crate::ast::{self, BinOpKind};
4use crate::token::{self, BinOpToken, Token};
5
6/// Associative operator with precedence.
7///
8/// This is the enum which specifies operator precedence and fixity to the parser.
9#[derive(Copy, Clone, PartialEq, Debug)]
10pub enum AssocOp {
11    /// `+`
12    Add,
13    /// `-`
14    Subtract,
15    /// `*`
16    Multiply,
17    /// `/`
18    Divide,
19    /// `%`
20    Modulus,
21    /// `&&`
22    LAnd,
23    /// `||`
24    LOr,
25    /// `^`
26    BitXor,
27    /// `&`
28    BitAnd,
29    /// `|`
30    BitOr,
31    /// `<<`
32    ShiftLeft,
33    /// `>>`
34    ShiftRight,
35    /// `==`
36    Equal,
37    /// `<`
38    Less,
39    /// `<=`
40    LessEqual,
41    /// `!=`
42    NotEqual,
43    /// `>`
44    Greater,
45    /// `>=`
46    GreaterEqual,
47    /// `=`
48    Assign,
49    /// `?=` where ? is one of the BinOpToken
50    AssignOp(BinOpToken),
51    /// `as`
52    As,
53    /// `..` range
54    DotDot,
55    /// `..=` range
56    DotDotEq,
57}
58
59#[derive(PartialEq, Debug)]
60pub enum Fixity {
61    /// The operator is left-associative
62    Left,
63    /// The operator is right-associative
64    Right,
65    /// The operator is not associative
66    None,
67}
68
69impl AssocOp {
70    /// Creates a new AssocOP from a token
71    pub fn from_token(t: &Token) -> Option<AssocOp> {
72        use AssocOp::*;
73        match t.kind {
74            token::BinOpEq(k) => Some(AssignOp(k)),
75            token::Eq => Some(Assign),
76            token::BinOp(BinOpToken::Star) => Some(Multiply),
77            token::BinOp(BinOpToken::Slash) => Some(Divide),
78            token::BinOp(BinOpToken::Percent) => Some(Modulus),
79            token::BinOp(BinOpToken::Plus) => Some(Add),
80            token::BinOp(BinOpToken::Minus) => Some(Subtract),
81            token::BinOp(BinOpToken::Shl) => Some(ShiftLeft),
82            token::BinOp(BinOpToken::Shr) => Some(ShiftRight),
83            token::BinOp(BinOpToken::And) => Some(BitAnd),
84            token::BinOp(BinOpToken::Caret) => Some(BitXor),
85            token::BinOp(BinOpToken::Or) => Some(BitOr),
86            token::Lt => Some(Less),
87            token::Le => Some(LessEqual),
88            token::Ge => Some(GreaterEqual),
89            token::Gt => Some(Greater),
90            token::EqEq => Some(Equal),
91            token::Ne => Some(NotEqual),
92            token::AndAnd => Some(LAnd),
93            token::OrOr => Some(LOr),
94            token::DotDot => Some(DotDot),
95            token::DotDotEq => Some(DotDotEq),
96            // DotDotDot is no longer supported, but we need some way to display the error
97            token::DotDotDot => Some(DotDotEq),
98            // `<-` should probably be `< -`
99            token::LArrow => Some(Less),
100            _ if t.is_keyword(kw::As) => Some(As),
101            _ => None,
102        }
103    }
104
105    /// Creates a new AssocOp from ast::BinOpKind.
106    pub fn from_ast_binop(op: BinOpKind) -> Self {
107        use AssocOp::*;
108        match op {
109            BinOpKind::Lt => Less,
110            BinOpKind::Gt => Greater,
111            BinOpKind::Le => LessEqual,
112            BinOpKind::Ge => GreaterEqual,
113            BinOpKind::Eq => Equal,
114            BinOpKind::Ne => NotEqual,
115            BinOpKind::Mul => Multiply,
116            BinOpKind::Div => Divide,
117            BinOpKind::Rem => Modulus,
118            BinOpKind::Add => Add,
119            BinOpKind::Sub => Subtract,
120            BinOpKind::Shl => ShiftLeft,
121            BinOpKind::Shr => ShiftRight,
122            BinOpKind::BitAnd => BitAnd,
123            BinOpKind::BitXor => BitXor,
124            BinOpKind::BitOr => BitOr,
125            BinOpKind::And => LAnd,
126            BinOpKind::Or => LOr,
127        }
128    }
129
130    /// Gets the precedence of this operator
131    pub fn precedence(&self) -> ExprPrecedence {
132        use AssocOp::*;
133        match *self {
134            As => ExprPrecedence::Cast,
135            Multiply | Divide | Modulus => ExprPrecedence::Product,
136            Add | Subtract => ExprPrecedence::Sum,
137            ShiftLeft | ShiftRight => ExprPrecedence::Shift,
138            BitAnd => ExprPrecedence::BitAnd,
139            BitXor => ExprPrecedence::BitXor,
140            BitOr => ExprPrecedence::BitOr,
141            Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => ExprPrecedence::Compare,
142            LAnd => ExprPrecedence::LAnd,
143            LOr => ExprPrecedence::LOr,
144            DotDot | DotDotEq => ExprPrecedence::Range,
145            Assign | AssignOp(_) => ExprPrecedence::Assign,
146        }
147    }
148
149    /// Gets the fixity of this operator
150    pub fn fixity(&self) -> Fixity {
151        use AssocOp::*;
152        // NOTE: it is a bug to have an operators that has same precedence but different fixities!
153        match *self {
154            Assign | AssignOp(_) => Fixity::Right,
155            As | Multiply | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd
156            | BitXor | BitOr | LAnd | LOr => Fixity::Left,
157            Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | DotDot | DotDotEq => {
158                Fixity::None
159            }
160        }
161    }
162
163    pub fn is_comparison(&self) -> bool {
164        use AssocOp::*;
165        match *self {
166            Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => true,
167            Assign | AssignOp(_) | As | Multiply | Divide | Modulus | Add | Subtract
168            | ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd | LOr | DotDot | DotDotEq => {
169                false
170            }
171        }
172    }
173
174    pub fn is_assign_like(&self) -> bool {
175        use AssocOp::*;
176        match *self {
177            Assign | AssignOp(_) => true,
178            Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | As | Multiply
179            | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd | BitXor
180            | BitOr | LAnd | LOr | DotDot | DotDotEq => false,
181        }
182    }
183
184    pub fn to_ast_binop(&self) -> Option<BinOpKind> {
185        use AssocOp::*;
186        match *self {
187            Less => Some(BinOpKind::Lt),
188            Greater => Some(BinOpKind::Gt),
189            LessEqual => Some(BinOpKind::Le),
190            GreaterEqual => Some(BinOpKind::Ge),
191            Equal => Some(BinOpKind::Eq),
192            NotEqual => Some(BinOpKind::Ne),
193            Multiply => Some(BinOpKind::Mul),
194            Divide => Some(BinOpKind::Div),
195            Modulus => Some(BinOpKind::Rem),
196            Add => Some(BinOpKind::Add),
197            Subtract => Some(BinOpKind::Sub),
198            ShiftLeft => Some(BinOpKind::Shl),
199            ShiftRight => Some(BinOpKind::Shr),
200            BitAnd => Some(BinOpKind::BitAnd),
201            BitXor => Some(BinOpKind::BitXor),
202            BitOr => Some(BinOpKind::BitOr),
203            LAnd => Some(BinOpKind::And),
204            LOr => Some(BinOpKind::Or),
205            Assign | AssignOp(_) | As | DotDot | DotDotEq => None,
206        }
207    }
208
209    /// This operator could be used to follow a block unambiguously.
210    ///
211    /// This is used for error recovery at the moment, providing a suggestion to wrap blocks with
212    /// parentheses while having a high degree of confidence on the correctness of the suggestion.
213    pub fn can_continue_expr_unambiguously(&self) -> bool {
214        use AssocOp::*;
215        matches!(
216            self,
217            BitXor | // `{ 42 } ^ 3`
218            Assign | // `{ 42 } = { 42 }`
219            Divide | // `{ 42 } / 42`
220            Modulus | // `{ 42 } % 2`
221            ShiftRight | // `{ 42 } >> 2`
222            LessEqual | // `{ 42 } <= 3`
223            Greater | // `{ 42 } > 3`
224            GreaterEqual | // `{ 42 } >= 3`
225            AssignOp(_) | // `{ 42 } +=`
226            // Equal | // `{ 42 } == { 42 }`    Accepting these here would regress incorrect
227            // NotEqual | // `{ 42 } != { 42 }  struct literals parser recovery.
228            As // `{ 42 } as usize`
229        )
230    }
231}
232
233#[derive(Clone, Copy, PartialEq, PartialOrd)]
234pub enum ExprPrecedence {
235    // return, break, yield, closures
236    Jump,
237    // = += -= *= /= %= &= |= ^= <<= >>=
238    Assign,
239    // .. ..=
240    Range,
241    // ||
242    LOr,
243    // &&
244    LAnd,
245    // == != < > <= >=
246    Compare,
247    // |
248    BitOr,
249    // ^
250    BitXor,
251    // &
252    BitAnd,
253    // << >>
254    Shift,
255    // + -
256    Sum,
257    // * / %
258    Product,
259    // as
260    Cast,
261    // unary - * ! & &mut
262    Prefix,
263    // paths, loops, function calls, array indexing, field expressions, method calls
264    Unambiguous,
265}
266
267/// In `let p = e`, operators with precedence `<=` this one requires parentheses in `e`.
268pub fn prec_let_scrutinee_needs_par() -> ExprPrecedence {
269    ExprPrecedence::LAnd
270}
271
272/// Suppose we have `let _ = e` and the `order` of `e`.
273/// Is the `order` such that `e` in `let _ = e` needs parentheses when it is on the RHS?
274///
275/// Conversely, suppose that we have `(let _ = a) OP b` and `order` is that of `OP`.
276/// Can we print this as `let _ = a OP b`?
277pub fn needs_par_as_let_scrutinee(order: ExprPrecedence) -> bool {
278    order <= prec_let_scrutinee_needs_par()
279}
280
281/// Expressions that syntactically contain an "exterior" struct literal i.e., not surrounded by any
282/// parens or other delimiters, e.g., `X { y: 1 }`, `X { y: 1 }.method()`, `foo == X { y: 1 }` and
283/// `X { y: 1 } == foo` all do, but `(X { y: 1 }) == foo` does not.
284pub fn contains_exterior_struct_lit(value: &ast::Expr) -> bool {
285    match &value.kind {
286        ast::ExprKind::Struct(..) => true,
287
288        ast::ExprKind::Assign(lhs, rhs, _)
289        | ast::ExprKind::AssignOp(_, lhs, rhs)
290        | ast::ExprKind::Binary(_, lhs, rhs) => {
291            // X { y: 1 } + X { y: 2 }
292            contains_exterior_struct_lit(lhs) || contains_exterior_struct_lit(rhs)
293        }
294        ast::ExprKind::Await(x, _)
295        | ast::ExprKind::Unary(_, x)
296        | ast::ExprKind::Cast(x, _)
297        | ast::ExprKind::Type(x, _)
298        | ast::ExprKind::Field(x, _)
299        | ast::ExprKind::Index(x, _, _)
300        | ast::ExprKind::Match(x, _, ast::MatchKind::Postfix) => {
301            // &X { y: 1 }, X { y: 1 }.y
302            contains_exterior_struct_lit(x)
303        }
304
305        ast::ExprKind::MethodCall(box ast::MethodCall { receiver, .. }) => {
306            // X { y: 1 }.bar(...)
307            contains_exterior_struct_lit(receiver)
308        }
309
310        _ => false,
311    }
312}