]>
Commit | Line | Data |
---|---|---|
9fa01778 | 1 | use crate::ast::{self, BinOpKind}; |
dfeec247 XL |
2 | use crate::token::{self, BinOpToken, Token}; |
3 | use rustc_span::symbol::kw; | |
2c00a5a8 | 4 | |
92a42be0 SL |
5 | /// Associative operator with precedence. |
6 | /// | |
7 | /// This is the enum which specifies operator precedence and fixity to the parser. | |
f035d41b | 8 | #[derive(Copy, Clone, PartialEq, Debug)] |
92a42be0 SL |
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, | |
92a42be0 SL |
48 | /// `?=` where ? is one of the BinOpToken |
49 | AssignOp(BinOpToken), | |
50 | /// `as` | |
51 | As, | |
52 | /// `..` range | |
9cc50fc6 | 53 | DotDot, |
ea8adc8c XL |
54 | /// `..=` range |
55 | DotDotEq, | |
9cc50fc6 SL |
56 | /// `:` |
57 | Colon, | |
92a42be0 SL |
58 | } |
59 | ||
8faf50e0 | 60 | #[derive(PartialEq, Debug)] |
92a42be0 SL |
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 | |
dfeec247 | 67 | None, |
92a42be0 SL |
68 | } |
69 | ||
70 | impl AssocOp { | |
9fa01778 | 71 | /// Creates a new AssocOP from a token |
60c5eb7d | 72 | pub fn from_token(t: &Token) -> Option<AssocOp> { |
9fa01778 | 73 | use AssocOp::*; |
dc9dc135 XL |
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), | |
abe05a73 | 97 | // DotDotDot is no longer supported, but we need some way to display the error |
dc9dc135 XL |
98 | token::DotDotDot => Some(DotDotEq), |
99 | token::Colon => Some(Colon), | |
e1599b0c XL |
100 | // `<-` should probably be `< -` |
101 | token::LArrow => Some(Less), | |
dc9dc135 | 102 | _ if t.is_keyword(kw::As) => Some(As), |
dfeec247 | 103 | _ => None, |
92a42be0 SL |
104 | } |
105 | } | |
106 | ||
9fa01778 | 107 | /// Creates a new AssocOp from ast::BinOpKind. |
7453a54e | 108 | pub fn from_ast_binop(op: BinOpKind) -> Self { |
9fa01778 | 109 | use AssocOp::*; |
92a42be0 | 110 | match op { |
7453a54e SL |
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, | |
dfeec247 | 128 | BinOpKind::Or => LOr, |
92a42be0 SL |
129 | } |
130 | } | |
131 | ||
132 | /// Gets the precedence of this operator | |
133 | pub fn precedence(&self) -> usize { | |
9fa01778 | 134 | use AssocOp::*; |
92a42be0 | 135 | match *self { |
9cc50fc6 | 136 | As | Colon => 14, |
92a42be0 SL |
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, | |
ea8adc8c | 146 | DotDot | DotDotEq => 4, |
92a42be0 SL |
147 | Assign | AssignOp(_) => 2, |
148 | } | |
149 | } | |
150 | ||
151 | /// Gets the fixity of this operator | |
152 | pub fn fixity(&self) -> Fixity { | |
9fa01778 | 153 | use AssocOp::*; |
92a42be0 SL |
154 | // NOTE: it is a bug to have an operators that has same precedence but different fixities! |
155 | match *self { | |
dc9dc135 | 156 | Assign | AssignOp(_) => Fixity::Right, |
dfeec247 XL |
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, | |
92a42be0 SL |
161 | } |
162 | } | |
163 | ||
164 | pub fn is_comparison(&self) -> bool { | |
9fa01778 | 165 | use AssocOp::*; |
92a42be0 SL |
166 | match *self { |
167 | Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => true, | |
dfeec247 XL |
168 | Assign | AssignOp(_) | As | Multiply | Divide | Modulus | Add | Subtract |
169 | | ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd | LOr | DotDot | DotDotEq | |
170 | | Colon => false, | |
92a42be0 SL |
171 | } |
172 | } | |
173 | ||
174 | pub fn is_assign_like(&self) -> bool { | |
9fa01778 | 175 | use AssocOp::*; |
92a42be0 | 176 | match *self { |
dc9dc135 | 177 | Assign | AssignOp(_) => true, |
dfeec247 XL |
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, | |
92a42be0 SL |
181 | } |
182 | } | |
183 | ||
7453a54e | 184 | pub fn to_ast_binop(&self) -> Option<BinOpKind> { |
9fa01778 | 185 | use AssocOp::*; |
92a42be0 | 186 | match *self { |
7453a54e SL |
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), | |
dfeec247 | 205 | Assign | AssignOp(_) | As | DotDot | DotDotEq | Colon => None, |
92a42be0 SL |
206 | } |
207 | } | |
48663c56 XL |
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::*; | |
3c0e092e XL |
215 | matches!( |
216 | self, | |
48663c56 XL |
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 } +=` | |
48663c56 XL |
226 | As | // `{ 42 } as usize` |
227 | // Equal | // `{ 42 } == { 42 }` Accepting these here would regress incorrect | |
228 | // NotEqual | // `{ 42 } != { 42 } struct literals parser recovery. | |
3c0e092e XL |
229 | Colon, // `{ 42 }: usize` |
230 | ) | |
48663c56 | 231 | } |
92a42be0 | 232 | } |
ea8adc8c | 233 | |
ea8adc8c XL |
234 | pub const PREC_CLOSURE: i8 = -40; |
235 | pub const PREC_JUMP: i8 = -30; | |
236 | pub const PREC_RANGE: i8 = -10; | |
dc9dc135 | 237 | // The range 2..=14 is reserved for AssocOp binary operator precedences. |
ea8adc8c XL |
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 | ||
8faf50e0 | 243 | #[derive(Debug, Clone, Copy)] |
2c00a5a8 XL |
244 | pub enum ExprPrecedence { |
245 | Closure, | |
246 | Break, | |
247 | Continue, | |
248 | Ret, | |
249 | Yield, | |
04454e1e | 250 | Yeet, |
2c00a5a8 XL |
251 | |
252 | Range, | |
253 | ||
254 | Binary(BinOpKind), | |
255 | ||
2c00a5a8 XL |
256 | Cast, |
257 | Type, | |
258 | ||
259 | Assign, | |
260 | AssignOp, | |
261 | ||
262 | Box, | |
263 | AddrOf, | |
dc9dc135 | 264 | Let, |
2c00a5a8 XL |
265 | Unary, |
266 | ||
267 | Call, | |
268 | MethodCall, | |
269 | Field, | |
2c00a5a8 XL |
270 | Index, |
271 | Try, | |
272 | InlineAsm, | |
273 | Mac, | |
274 | ||
275 | Array, | |
276 | Repeat, | |
277 | Tup, | |
278 | Lit, | |
279 | Path, | |
280 | Paren, | |
281 | If, | |
2c00a5a8 | 282 | While, |
2c00a5a8 XL |
283 | ForLoop, |
284 | Loop, | |
285 | Match, | |
29967ef6 | 286 | ConstBlock, |
2c00a5a8 | 287 | Block, |
b7449926 | 288 | TryBlock, |
2c00a5a8 | 289 | Struct, |
8faf50e0 | 290 | Async, |
48663c56 | 291 | Await, |
0731742a | 292 | Err, |
2c00a5a8 | 293 | } |
ea8adc8c | 294 | |
2c00a5a8 XL |
295 | impl ExprPrecedence { |
296 | pub fn order(self) -> i8 { | |
297 | match self { | |
298 | ExprPrecedence::Closure => PREC_CLOSURE, | |
ea8adc8c | 299 | |
f2b60f7d FG |
300 | ExprPrecedence::Break |
301 | | ExprPrecedence::Continue | |
302 | | ExprPrecedence::Ret | |
303 | | ExprPrecedence::Yield | |
304 | | ExprPrecedence::Yeet => PREC_JUMP, | |
ea8adc8c | 305 | |
2c00a5a8 XL |
306 | // `Range` claims to have higher precedence than `Assign`, but `x .. x = x` fails to |
307 | // parse, instead of parsing as `(x .. x) = x`. Giving `Range` a lower precedence | |
308 | // ensures that `pprust` will add parentheses in the right places to get the desired | |
309 | // parse. | |
310 | ExprPrecedence::Range => PREC_RANGE, | |
ea8adc8c | 311 | |
2c00a5a8 XL |
312 | // Binop-like expr kinds, handled by `AssocOp`. |
313 | ExprPrecedence::Binary(op) => AssocOp::from_ast_binop(op).precedence() as i8, | |
2c00a5a8 XL |
314 | ExprPrecedence::Cast => AssocOp::As.precedence() as i8, |
315 | ExprPrecedence::Type => AssocOp::Colon.precedence() as i8, | |
ea8adc8c | 316 | |
2c00a5a8 XL |
317 | ExprPrecedence::Assign | |
318 | ExprPrecedence::AssignOp => AssocOp::Assign.precedence() as i8, | |
ea8adc8c | 319 | |
2c00a5a8 | 320 | // Unary, prefix |
f2b60f7d FG |
321 | ExprPrecedence::Box |
322 | | ExprPrecedence::AddrOf | |
dc9dc135 XL |
323 | // Here `let pats = expr` has `let pats =` as a "unary" prefix of `expr`. |
324 | // However, this is not exactly right. When `let _ = a` is the LHS of a binop we | |
325 | // need parens sometimes. E.g. we can print `(let _ = a) && b` as `let _ = a && b` | |
326 | // but we need to print `(let _ = a) < b` as-is with parens. | |
f2b60f7d FG |
327 | | ExprPrecedence::Let |
328 | | ExprPrecedence::Unary => PREC_PREFIX, | |
2c00a5a8 XL |
329 | |
330 | // Unary, postfix | |
f2b60f7d FG |
331 | ExprPrecedence::Await |
332 | | ExprPrecedence::Call | |
333 | | ExprPrecedence::MethodCall | |
334 | | ExprPrecedence::Field | |
335 | | ExprPrecedence::Index | |
336 | | ExprPrecedence::Try | |
337 | | ExprPrecedence::InlineAsm | |
338 | | ExprPrecedence::Mac => PREC_POSTFIX, | |
2c00a5a8 XL |
339 | |
340 | // Never need parens | |
f2b60f7d FG |
341 | ExprPrecedence::Array |
342 | | ExprPrecedence::Repeat | |
343 | | ExprPrecedence::Tup | |
344 | | ExprPrecedence::Lit | |
345 | | ExprPrecedence::Path | |
346 | | ExprPrecedence::Paren | |
347 | | ExprPrecedence::If | |
348 | | ExprPrecedence::While | |
349 | | ExprPrecedence::ForLoop | |
350 | | ExprPrecedence::Loop | |
351 | | ExprPrecedence::Match | |
352 | | ExprPrecedence::ConstBlock | |
353 | | ExprPrecedence::Block | |
354 | | ExprPrecedence::TryBlock | |
355 | | ExprPrecedence::Async | |
356 | | ExprPrecedence::Struct | |
357 | | ExprPrecedence::Err => PREC_PAREN, | |
2c00a5a8 | 358 | } |
ea8adc8c XL |
359 | } |
360 | } | |
361 | ||
3c0e092e | 362 | /// In `let p = e`, operators with precedence `<=` this one requires parentheses in `e`. |
60c5eb7d | 363 | pub fn prec_let_scrutinee_needs_par() -> usize { |
dc9dc135 XL |
364 | AssocOp::LAnd.precedence() |
365 | } | |
366 | ||
367 | /// Suppose we have `let _ = e` and the `order` of `e`. | |
3c0e092e | 368 | /// Is the `order` such that `e` in `let _ = e` needs parentheses when it is on the RHS? |
dc9dc135 XL |
369 | /// |
370 | /// Conversely, suppose that we have `(let _ = a) OP b` and `order` is that of `OP`. | |
371 | /// Can we print this as `let _ = a OP b`? | |
74b04a01 | 372 | pub fn needs_par_as_let_scrutinee(order: i8) -> bool { |
dc9dc135 XL |
373 | order <= prec_let_scrutinee_needs_par() as i8 |
374 | } | |
2c00a5a8 | 375 | |
0731742a XL |
376 | /// Expressions that syntactically contain an "exterior" struct literal i.e., not surrounded by any |
377 | /// parens or other delimiters, e.g., `X { y: 1 }`, `X { y: 1 }.method()`, `foo == X { y: 1 }` and | |
ea8adc8c XL |
378 | /// `X { y: 1 } == foo` all do, but `(X { y: 1 }) == foo` does not. |
379 | pub fn contains_exterior_struct_lit(value: &ast::Expr) -> bool { | |
e74abb32 | 380 | match value.kind { |
ea8adc8c XL |
381 | ast::ExprKind::Struct(..) => true, |
382 | ||
dfeec247 XL |
383 | ast::ExprKind::Assign(ref lhs, ref rhs, _) |
384 | | ast::ExprKind::AssignOp(_, ref lhs, ref rhs) | |
385 | | ast::ExprKind::Binary(_, ref lhs, ref rhs) => { | |
ea8adc8c XL |
386 | // X { y: 1 } + X { y: 2 } |
387 | contains_exterior_struct_lit(&lhs) || contains_exterior_struct_lit(&rhs) | |
388 | } | |
dfeec247 XL |
389 | ast::ExprKind::Await(ref x) |
390 | | ast::ExprKind::Unary(_, ref x) | |
391 | | ast::ExprKind::Cast(ref x, _) | |
392 | | ast::ExprKind::Type(ref x, _) | |
393 | | ast::ExprKind::Field(ref x, _) | |
394 | | ast::ExprKind::Index(ref x, _) => { | |
ea8adc8c XL |
395 | // &X { y: 1 }, X { y: 1 }.y |
396 | contains_exterior_struct_lit(&x) | |
397 | } | |
398 | ||
f035d41b | 399 | ast::ExprKind::MethodCall(.., ref exprs, _) => { |
ea8adc8c XL |
400 | // X { y: 1 }.bar(...) |
401 | contains_exterior_struct_lit(&exprs[0]) | |
402 | } | |
403 | ||
404 | _ => false, | |
405 | } | |
406 | } |