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     Colon,
58 }
59 
60 #[derive(PartialEq, Debug)]
61 pub enum Fixity {
62     /// The operator is left-associative
63     Left,
64     /// The operator is right-associative
65     Right,
66     /// The operator is not associative
67     None,
68 }
69 
70 impl AssocOp {
71     /// Creates a new AssocOP from a token
from_token(t: &Token) -> Option<AssocOp>72     pub fn from_token(t: &Token) -> Option<AssocOp> {
73         use AssocOp::*;
74         match t.kind {
75             token::BinOpEq(k) => Some(AssignOp(k)),
76             token::Eq => Some(Assign),
77             token::BinOp(BinOpToken::Star) => Some(Multiply),
78             token::BinOp(BinOpToken::Slash) => Some(Divide),
79             token::BinOp(BinOpToken::Percent) => Some(Modulus),
80             token::BinOp(BinOpToken::Plus) => Some(Add),
81             token::BinOp(BinOpToken::Minus) => Some(Subtract),
82             token::BinOp(BinOpToken::Shl) => Some(ShiftLeft),
83             token::BinOp(BinOpToken::Shr) => Some(ShiftRight),
84             token::BinOp(BinOpToken::And) => Some(BitAnd),
85             token::BinOp(BinOpToken::Caret) => Some(BitXor),
86             token::BinOp(BinOpToken::Or) => Some(BitOr),
87             token::Lt => Some(Less),
88             token::Le => Some(LessEqual),
89             token::Ge => Some(GreaterEqual),
90             token::Gt => Some(Greater),
91             token::EqEq => Some(Equal),
92             token::Ne => Some(NotEqual),
93             token::AndAnd => Some(LAnd),
94             token::OrOr => Some(LOr),
95             token::DotDot => Some(DotDot),
96             token::DotDotEq => Some(DotDotEq),
97             // DotDotDot is no longer supported, but we need some way to display the error
98             token::DotDotDot => Some(DotDotEq),
99             token::Colon => Some(Colon),
100             // `<-` should probably be `< -`
101             token::LArrow => Some(Less),
102             _ if t.is_keyword(kw::As) => Some(As),
103             _ => None,
104         }
105     }
106 
107     /// Creates a new AssocOp from ast::BinOpKind.
from_ast_binop(op: BinOpKind) -> Self108     pub fn from_ast_binop(op: BinOpKind) -> Self {
109         use AssocOp::*;
110         match op {
111             BinOpKind::Lt => Less,
112             BinOpKind::Gt => Greater,
113             BinOpKind::Le => LessEqual,
114             BinOpKind::Ge => GreaterEqual,
115             BinOpKind::Eq => Equal,
116             BinOpKind::Ne => NotEqual,
117             BinOpKind::Mul => Multiply,
118             BinOpKind::Div => Divide,
119             BinOpKind::Rem => Modulus,
120             BinOpKind::Add => Add,
121             BinOpKind::Sub => Subtract,
122             BinOpKind::Shl => ShiftLeft,
123             BinOpKind::Shr => ShiftRight,
124             BinOpKind::BitAnd => BitAnd,
125             BinOpKind::BitXor => BitXor,
126             BinOpKind::BitOr => BitOr,
127             BinOpKind::And => LAnd,
128             BinOpKind::Or => LOr,
129         }
130     }
131 
132     /// Gets the precedence of this operator
precedence(&self) -> usize133     pub fn precedence(&self) -> usize {
134         use AssocOp::*;
135         match *self {
136             As | Colon => 14,
137             Multiply | Divide | Modulus => 13,
138             Add | Subtract => 12,
139             ShiftLeft | ShiftRight => 11,
140             BitAnd => 10,
141             BitXor => 9,
142             BitOr => 8,
143             Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => 7,
144             LAnd => 6,
145             LOr => 5,
146             DotDot | DotDotEq => 4,
147             Assign | AssignOp(_) => 2,
148         }
149     }
150 
151     /// Gets the fixity of this operator
fixity(&self) -> Fixity152     pub fn fixity(&self) -> Fixity {
153         use AssocOp::*;
154         // NOTE: it is a bug to have an operators that has same precedence but different fixities!
155         match *self {
156             Assign | AssignOp(_) => Fixity::Right,
157             As | Multiply | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd
158             | BitXor | BitOr | Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual
159             | LAnd | LOr | Colon => Fixity::Left,
160             DotDot | DotDotEq => Fixity::None,
161         }
162     }
163 
is_comparison(&self) -> bool164     pub fn is_comparison(&self) -> bool {
165         use AssocOp::*;
166         match *self {
167             Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => true,
168             Assign | AssignOp(_) | As | Multiply | Divide | Modulus | Add | Subtract
169             | ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd | LOr | DotDot | DotDotEq
170             | Colon => false,
171         }
172     }
173 
is_assign_like(&self) -> bool174     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 | Colon => false,
181         }
182     }
183 
to_ast_binop(&self) -> Option<BinOpKind>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 | Colon => 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.
can_continue_expr_unambiguously(&self) -> bool213     pub fn can_continue_expr_unambiguously(&self) -> bool {
214         use AssocOp::*;
215         match self {
216             BitXor | // `{ 42 } ^ 3`
217             Assign | // `{ 42 } = { 42 }`
218             Divide | // `{ 42 } / 42`
219             Modulus | // `{ 42 } % 2`
220             ShiftRight | // `{ 42 } >> 2`
221             LessEqual | // `{ 42 } <= 3`
222             Greater | // `{ 42 } > 3`
223             GreaterEqual | // `{ 42 } >= 3`
224             AssignOp(_) | // `{ 42 } +=`
225             As | // `{ 42 } as usize`
226             // Equal | // `{ 42 } == { 42 }`    Accepting these here would regress incorrect
227             // NotEqual | // `{ 42 } != { 42 }  struct literals parser recovery.
228             Colon => true, // `{ 42 }: usize`
229             _ => false,
230         }
231     }
232 }
233 
234 pub const PREC_CLOSURE: i8 = -40;
235 pub const PREC_JUMP: i8 = -30;
236 pub const PREC_RANGE: i8 = -10;
237 // The range 2..=14 is reserved for AssocOp binary operator precedences.
238 pub const PREC_PREFIX: i8 = 50;
239 pub const PREC_POSTFIX: i8 = 60;
240 pub const PREC_PAREN: i8 = 99;
241 pub const PREC_FORCE_PAREN: i8 = 100;
242 
243 #[derive(Debug, Clone, Copy)]
244 pub enum ExprPrecedence {
245     Closure,
246     Break,
247     Continue,
248     Ret,
249     Yield,
250 
251     Range,
252 
253     Binary(BinOpKind),
254 
255     Cast,
256     Type,
257 
258     Assign,
259     AssignOp,
260 
261     Box,
262     AddrOf,
263     Let,
264     Unary,
265 
266     Call,
267     MethodCall,
268     Field,
269     Index,
270     Try,
271     InlineAsm,
272     Mac,
273 
274     Array,
275     Repeat,
276     Tup,
277     Lit,
278     Path,
279     Paren,
280     If,
281     While,
282     ForLoop,
283     Loop,
284     Match,
285     ConstBlock,
286     Block,
287     TryBlock,
288     Struct,
289     Async,
290     Await,
291     Err,
292 }
293 
294 impl ExprPrecedence {
order(self) -> i8295     pub fn order(self) -> i8 {
296         match self {
297             ExprPrecedence::Closure => PREC_CLOSURE,
298 
299             ExprPrecedence::Break |
300             ExprPrecedence::Continue |
301             ExprPrecedence::Ret |
302             ExprPrecedence::Yield => PREC_JUMP,
303 
304             // `Range` claims to have higher precedence than `Assign`, but `x .. x = x` fails to
305             // parse, instead of parsing as `(x .. x) = x`.  Giving `Range` a lower precedence
306             // ensures that `pprust` will add parentheses in the right places to get the desired
307             // parse.
308             ExprPrecedence::Range => PREC_RANGE,
309 
310             // Binop-like expr kinds, handled by `AssocOp`.
311             ExprPrecedence::Binary(op) => AssocOp::from_ast_binop(op).precedence() as i8,
312             ExprPrecedence::Cast => AssocOp::As.precedence() as i8,
313             ExprPrecedence::Type => AssocOp::Colon.precedence() as i8,
314 
315             ExprPrecedence::Assign |
316             ExprPrecedence::AssignOp => AssocOp::Assign.precedence() as i8,
317 
318             // Unary, prefix
319             ExprPrecedence::Box |
320             ExprPrecedence::AddrOf |
321             // Here `let pats = expr` has `let pats =` as a "unary" prefix of `expr`.
322             // However, this is not exactly right. When `let _ = a` is the LHS of a binop we
323             // need parens sometimes. E.g. we can print `(let _ = a) && b` as `let _ = a && b`
324             // but we need to print `(let _ = a) < b` as-is with parens.
325             ExprPrecedence::Let |
326             ExprPrecedence::Unary => PREC_PREFIX,
327 
328             // Unary, postfix
329             ExprPrecedence::Await |
330             ExprPrecedence::Call |
331             ExprPrecedence::MethodCall |
332             ExprPrecedence::Field |
333             ExprPrecedence::Index |
334             ExprPrecedence::Try |
335             ExprPrecedence::InlineAsm |
336             ExprPrecedence::Mac => PREC_POSTFIX,
337 
338             // Never need parens
339             ExprPrecedence::Array |
340             ExprPrecedence::Repeat |
341             ExprPrecedence::Tup |
342             ExprPrecedence::Lit |
343             ExprPrecedence::Path |
344             ExprPrecedence::Paren |
345             ExprPrecedence::If |
346             ExprPrecedence::While |
347             ExprPrecedence::ForLoop |
348             ExprPrecedence::Loop |
349             ExprPrecedence::Match |
350             ExprPrecedence::ConstBlock |
351             ExprPrecedence::Block |
352             ExprPrecedence::TryBlock |
353             ExprPrecedence::Async |
354             ExprPrecedence::Struct |
355             ExprPrecedence::Err => PREC_PAREN,
356         }
357     }
358 }
359 
360 /// In `let p = e`, operators with precedence `<=` this one requires parenthesis in `e`.
prec_let_scrutinee_needs_par() -> usize361 pub fn prec_let_scrutinee_needs_par() -> usize {
362     AssocOp::LAnd.precedence()
363 }
364 
365 /// Suppose we have `let _ = e` and the `order` of `e`.
366 /// Is the `order` such that `e` in `let _ = e` needs parenthesis when it is on the RHS?
367 ///
368 /// Conversely, suppose that we have `(let _ = a) OP b` and `order` is that of `OP`.
369 /// Can we print this as `let _ = a OP b`?
needs_par_as_let_scrutinee(order: i8) -> bool370 pub fn needs_par_as_let_scrutinee(order: i8) -> bool {
371     order <= prec_let_scrutinee_needs_par() as i8
372 }
373 
374 /// Expressions that syntactically contain an "exterior" struct literal i.e., not surrounded by any
375 /// parens or other delimiters, e.g., `X { y: 1 }`, `X { y: 1 }.method()`, `foo == X { y: 1 }` and
376 /// `X { y: 1 } == foo` all do, but `(X { y: 1 }) == foo` does not.
contains_exterior_struct_lit(value: &ast::Expr) -> bool377 pub fn contains_exterior_struct_lit(value: &ast::Expr) -> bool {
378     match value.kind {
379         ast::ExprKind::Struct(..) => true,
380 
381         ast::ExprKind::Assign(ref lhs, ref rhs, _)
382         | ast::ExprKind::AssignOp(_, ref lhs, ref rhs)
383         | ast::ExprKind::Binary(_, ref lhs, ref rhs) => {
384             // X { y: 1 } + X { y: 2 }
385             contains_exterior_struct_lit(&lhs) || contains_exterior_struct_lit(&rhs)
386         }
387         ast::ExprKind::Await(ref x)
388         | ast::ExprKind::Unary(_, ref x)
389         | ast::ExprKind::Cast(ref x, _)
390         | ast::ExprKind::Type(ref x, _)
391         | ast::ExprKind::Field(ref x, _)
392         | ast::ExprKind::Index(ref x, _) => {
393             // &X { y: 1 }, X { y: 1 }.y
394             contains_exterior_struct_lit(&x)
395         }
396 
397         ast::ExprKind::MethodCall(.., ref exprs, _) => {
398             // X { y: 1 }.bar(...)
399             contains_exterior_struct_lit(&exprs[0])
400         }
401 
402         _ => false,
403     }
404 }
405