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