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