• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::ast::{self, BinOpKind};
2 use crate::token::{self, BinOpToken, Token};
3 use rustc_span::symbol::kw;
4 
5 /// Associative operator with precedence.
6 ///
7 /// This is the enum which specifies operator precedence and fixity to the parser.
8 #[derive(Copy, Clone, PartialEq, Debug)]
9 pub enum AssocOp {
10     /// `+`
11     Add,
12     /// `-`
13     Subtract,
14     /// `*`
15     Multiply,
16     /// `/`
17     Divide,
18     /// `%`
19     Modulus,
20     /// `&&`
21     LAnd,
22     /// `||`
23     LOr,
24     /// `^`
25     BitXor,
26     /// `&`
27     BitAnd,
28     /// `|`
29     BitOr,
30     /// `<<`
31     ShiftLeft,
32     /// `>>`
33     ShiftRight,
34     /// `==`
35     Equal,
36     /// `<`
37     Less,
38     /// `<=`
39     LessEqual,
40     /// `!=`
41     NotEqual,
42     /// `>`
43     Greater,
44     /// `>=`
45     GreaterEqual,
46     /// `=`
47     Assign,
48     /// `?=` where ? is one of the BinOpToken
49     AssignOp(BinOpToken),
50     /// `as`
51     As,
52     /// `..` range
53     DotDot,
54     /// `..=` range
55     DotDotEq,
56 }
57 
58 #[derive(PartialEq, Debug)]
59 pub enum Fixity {
60     /// The operator is left-associative
61     Left,
62     /// The operator is right-associative
63     Right,
64     /// The operator is not associative
65     None,
66 }
67 
68 impl AssocOp {
69     /// Creates a new AssocOP from a token
from_token(t: &Token) -> Option<AssocOp>70     pub fn from_token(t: &Token) -> Option<AssocOp> {
71         use AssocOp::*;
72         match t.kind {
73             token::BinOpEq(k) => Some(AssignOp(k)),
74             token::Eq => Some(Assign),
75             token::BinOp(BinOpToken::Star) => Some(Multiply),
76             token::BinOp(BinOpToken::Slash) => Some(Divide),
77             token::BinOp(BinOpToken::Percent) => Some(Modulus),
78             token::BinOp(BinOpToken::Plus) => Some(Add),
79             token::BinOp(BinOpToken::Minus) => Some(Subtract),
80             token::BinOp(BinOpToken::Shl) => Some(ShiftLeft),
81             token::BinOp(BinOpToken::Shr) => Some(ShiftRight),
82             token::BinOp(BinOpToken::And) => Some(BitAnd),
83             token::BinOp(BinOpToken::Caret) => Some(BitXor),
84             token::BinOp(BinOpToken::Or) => Some(BitOr),
85             token::Lt => Some(Less),
86             token::Le => Some(LessEqual),
87             token::Ge => Some(GreaterEqual),
88             token::Gt => Some(Greater),
89             token::EqEq => Some(Equal),
90             token::Ne => Some(NotEqual),
91             token::AndAnd => Some(LAnd),
92             token::OrOr => Some(LOr),
93             token::DotDot => Some(DotDot),
94             token::DotDotEq => Some(DotDotEq),
95             // DotDotDot is no longer supported, but we need some way to display the error
96             token::DotDotDot => Some(DotDotEq),
97             // `<-` should probably be `< -`
98             token::LArrow => Some(Less),
99             _ if t.is_keyword(kw::As) => Some(As),
100             _ => None,
101         }
102     }
103 
104     /// Creates a new AssocOp from ast::BinOpKind.
from_ast_binop(op: BinOpKind) -> Self105     pub fn from_ast_binop(op: BinOpKind) -> Self {
106         use AssocOp::*;
107         match op {
108             BinOpKind::Lt => Less,
109             BinOpKind::Gt => Greater,
110             BinOpKind::Le => LessEqual,
111             BinOpKind::Ge => GreaterEqual,
112             BinOpKind::Eq => Equal,
113             BinOpKind::Ne => NotEqual,
114             BinOpKind::Mul => Multiply,
115             BinOpKind::Div => Divide,
116             BinOpKind::Rem => Modulus,
117             BinOpKind::Add => Add,
118             BinOpKind::Sub => Subtract,
119             BinOpKind::Shl => ShiftLeft,
120             BinOpKind::Shr => ShiftRight,
121             BinOpKind::BitAnd => BitAnd,
122             BinOpKind::BitXor => BitXor,
123             BinOpKind::BitOr => BitOr,
124             BinOpKind::And => LAnd,
125             BinOpKind::Or => LOr,
126         }
127     }
128 
129     /// Gets the precedence of this operator
precedence(&self) -> usize130     pub fn precedence(&self) -> usize {
131         use AssocOp::*;
132         match *self {
133             As => 14,
134             Multiply | Divide | Modulus => 13,
135             Add | Subtract => 12,
136             ShiftLeft | ShiftRight => 11,
137             BitAnd => 10,
138             BitXor => 9,
139             BitOr => 8,
140             Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => 7,
141             LAnd => 6,
142             LOr => 5,
143             DotDot | DotDotEq => 4,
144             Assign | AssignOp(_) => 2,
145         }
146     }
147 
148     /// Gets the fixity of this operator
fixity(&self) -> Fixity149     pub fn fixity(&self) -> Fixity {
150         use AssocOp::*;
151         // NOTE: it is a bug to have an operators that has same precedence but different fixities!
152         match *self {
153             Assign | AssignOp(_) => Fixity::Right,
154             As | Multiply | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd
155             | BitXor | BitOr | Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual
156             | LAnd | LOr => Fixity::Left,
157             DotDot | DotDotEq => Fixity::None,
158         }
159     }
160 
is_comparison(&self) -> bool161     pub fn is_comparison(&self) -> bool {
162         use AssocOp::*;
163         match *self {
164             Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => true,
165             Assign | AssignOp(_) | As | Multiply | Divide | Modulus | Add | Subtract
166             | ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd | LOr | DotDot | DotDotEq => {
167                 false
168             }
169         }
170     }
171 
is_assign_like(&self) -> bool172     pub fn is_assign_like(&self) -> bool {
173         use AssocOp::*;
174         match *self {
175             Assign | AssignOp(_) => true,
176             Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | As | Multiply
177             | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd | BitXor
178             | BitOr | LAnd | LOr | DotDot | DotDotEq => false,
179         }
180     }
181 
to_ast_binop(&self) -> Option<BinOpKind>182     pub fn to_ast_binop(&self) -> Option<BinOpKind> {
183         use AssocOp::*;
184         match *self {
185             Less => Some(BinOpKind::Lt),
186             Greater => Some(BinOpKind::Gt),
187             LessEqual => Some(BinOpKind::Le),
188             GreaterEqual => Some(BinOpKind::Ge),
189             Equal => Some(BinOpKind::Eq),
190             NotEqual => Some(BinOpKind::Ne),
191             Multiply => Some(BinOpKind::Mul),
192             Divide => Some(BinOpKind::Div),
193             Modulus => Some(BinOpKind::Rem),
194             Add => Some(BinOpKind::Add),
195             Subtract => Some(BinOpKind::Sub),
196             ShiftLeft => Some(BinOpKind::Shl),
197             ShiftRight => Some(BinOpKind::Shr),
198             BitAnd => Some(BinOpKind::BitAnd),
199             BitXor => Some(BinOpKind::BitXor),
200             BitOr => Some(BinOpKind::BitOr),
201             LAnd => Some(BinOpKind::And),
202             LOr => Some(BinOpKind::Or),
203             Assign | AssignOp(_) | As | DotDot | DotDotEq => None,
204         }
205     }
206 
207     /// This operator could be used to follow a block unambiguously.
208     ///
209     /// This is used for error recovery at the moment, providing a suggestion to wrap blocks with
210     /// parentheses while having a high degree of confidence on the correctness of the suggestion.
can_continue_expr_unambiguously(&self) -> bool211     pub fn can_continue_expr_unambiguously(&self) -> bool {
212         use AssocOp::*;
213         matches!(
214             self,
215             BitXor | // `{ 42 } ^ 3`
216             Assign | // `{ 42 } = { 42 }`
217             Divide | // `{ 42 } / 42`
218             Modulus | // `{ 42 } % 2`
219             ShiftRight | // `{ 42 } >> 2`
220             LessEqual | // `{ 42 } <= 3`
221             Greater | // `{ 42 } > 3`
222             GreaterEqual | // `{ 42 } >= 3`
223             AssignOp(_) | // `{ 42 } +=`
224             // Equal | // `{ 42 } == { 42 }`    Accepting these here would regress incorrect
225             // NotEqual | // `{ 42 } != { 42 }  struct literals parser recovery.
226             As // `{ 42 } as usize`
227         )
228     }
229 }
230 
231 pub const PREC_CLOSURE: i8 = -40;
232 pub const PREC_JUMP: i8 = -30;
233 pub const PREC_RANGE: i8 = -10;
234 // The range 2..=14 is reserved for AssocOp binary operator precedences.
235 pub const PREC_PREFIX: i8 = 50;
236 pub const PREC_POSTFIX: i8 = 60;
237 pub const PREC_PAREN: i8 = 99;
238 pub const PREC_FORCE_PAREN: i8 = 100;
239 
240 #[derive(Debug, Clone, Copy)]
241 pub enum ExprPrecedence {
242     Closure,
243     Break,
244     Continue,
245     Ret,
246     Yield,
247     Yeet,
248     Become,
249 
250     Range,
251 
252     Binary(BinOpKind),
253 
254     Cast,
255 
256     Assign,
257     AssignOp,
258 
259     AddrOf,
260     Let,
261     Unary,
262 
263     Call,
264     MethodCall,
265     Field,
266     Index,
267     Try,
268     InlineAsm,
269     OffsetOf,
270     Mac,
271     FormatArgs,
272 
273     Array,
274     Repeat,
275     Tup,
276     Lit,
277     Path,
278     Paren,
279     If,
280     While,
281     ForLoop,
282     Loop,
283     Match,
284     ConstBlock,
285     Block,
286     TryBlock,
287     Struct,
288     Async,
289     Await,
290     Err,
291 }
292 
293 impl ExprPrecedence {
order(self) -> i8294     pub fn order(self) -> i8 {
295         match self {
296             ExprPrecedence::Closure => PREC_CLOSURE,
297 
298             ExprPrecedence::Break
299             | ExprPrecedence::Continue
300             | ExprPrecedence::Ret
301             | ExprPrecedence::Yield
302             | ExprPrecedence::Yeet
303             | ExprPrecedence::Become => PREC_JUMP,
304 
305             // `Range` claims to have higher precedence than `Assign`, but `x .. x = x` fails to
306             // parse, instead of parsing as `(x .. x) = x`. Giving `Range` a lower precedence
307             // ensures that `pprust` will add parentheses in the right places to get the desired
308             // parse.
309             ExprPrecedence::Range => PREC_RANGE,
310 
311             // Binop-like expr kinds, handled by `AssocOp`.
312             ExprPrecedence::Binary(op) => AssocOp::from_ast_binop(op).precedence() as i8,
313             ExprPrecedence::Cast => AssocOp::As.precedence() as i8,
314 
315             ExprPrecedence::Assign |
316             ExprPrecedence::AssignOp => AssocOp::Assign.precedence() as i8,
317 
318             // Unary, prefix
319             ExprPrecedence::AddrOf
320             // Here `let pats = expr` has `let pats =` as a "unary" prefix of `expr`.
321             // However, this is not exactly right. When `let _ = a` is the LHS of a binop we
322             // need parens sometimes. E.g. we can print `(let _ = a) && b` as `let _ = a && b`
323             // but we need to print `(let _ = a) < b` as-is with parens.
324             | ExprPrecedence::Let
325             | ExprPrecedence::Unary => PREC_PREFIX,
326 
327             // Unary, postfix
328             ExprPrecedence::Await
329             | ExprPrecedence::Call
330             | ExprPrecedence::MethodCall
331             | ExprPrecedence::Field
332             | ExprPrecedence::Index
333             | ExprPrecedence::Try
334             | ExprPrecedence::InlineAsm
335             | ExprPrecedence::Mac
336             | ExprPrecedence::FormatArgs
337             | ExprPrecedence::OffsetOf => PREC_POSTFIX,
338 
339             // Never need parens
340             ExprPrecedence::Array
341             | ExprPrecedence::Repeat
342             | ExprPrecedence::Tup
343             | ExprPrecedence::Lit
344             | ExprPrecedence::Path
345             | ExprPrecedence::Paren
346             | ExprPrecedence::If
347             | ExprPrecedence::While
348             | ExprPrecedence::ForLoop
349             | ExprPrecedence::Loop
350             | ExprPrecedence::Match
351             | ExprPrecedence::ConstBlock
352             | ExprPrecedence::Block
353             | ExprPrecedence::TryBlock
354             | ExprPrecedence::Async
355             | ExprPrecedence::Struct
356             | ExprPrecedence::Err => PREC_PAREN,
357         }
358     }
359 }
360 
361 /// In `let p = e`, operators with precedence `<=` this one requires parentheses in `e`.
prec_let_scrutinee_needs_par() -> usize362 pub fn prec_let_scrutinee_needs_par() -> usize {
363     AssocOp::LAnd.precedence()
364 }
365 
366 /// Suppose we have `let _ = e` and the `order` of `e`.
367 /// Is the `order` such that `e` in `let _ = e` needs parentheses when it is on the RHS?
368 ///
369 /// Conversely, suppose that we have `(let _ = a) OP b` and `order` is that of `OP`.
370 /// Can we print this as `let _ = a OP b`?
needs_par_as_let_scrutinee(order: i8) -> bool371 pub fn needs_par_as_let_scrutinee(order: i8) -> bool {
372     order <= prec_let_scrutinee_needs_par() as i8
373 }
374 
375 /// Expressions that syntactically contain an "exterior" struct literal i.e., not surrounded by any
376 /// parens or other delimiters, e.g., `X { y: 1 }`, `X { y: 1 }.method()`, `foo == X { y: 1 }` and
377 /// `X { y: 1 } == foo` all do, but `(X { y: 1 }) == foo` does not.
contains_exterior_struct_lit(value: &ast::Expr) -> bool378 pub fn contains_exterior_struct_lit(value: &ast::Expr) -> bool {
379     match &value.kind {
380         ast::ExprKind::Struct(..) => true,
381 
382         ast::ExprKind::Assign(lhs, rhs, _)
383         | ast::ExprKind::AssignOp(_, lhs, rhs)
384         | ast::ExprKind::Binary(_, lhs, rhs) => {
385             // X { y: 1 } + X { y: 2 }
386             contains_exterior_struct_lit(lhs) || contains_exterior_struct_lit(rhs)
387         }
388         ast::ExprKind::Await(x, _)
389         | ast::ExprKind::Unary(_, x)
390         | ast::ExprKind::Cast(x, _)
391         | ast::ExprKind::Type(x, _)
392         | ast::ExprKind::Field(x, _)
393         | ast::ExprKind::Index(x, _) => {
394             // &X { y: 1 }, X { y: 1 }.y
395             contains_exterior_struct_lit(x)
396         }
397 
398         ast::ExprKind::MethodCall(box ast::MethodCall { receiver, .. }) => {
399             // X { y: 1 }.bar(...)
400             contains_exterior_struct_lit(receiver)
401         }
402 
403         _ => false,
404     }
405 }
406