rustc_ast/util/
parser.rs

1use rustc_span::kw;
2
3use crate::ast::{self, BinOpKind, RangeLimits};
4use crate::token::{self, Token};
5
6/// Associative operator.
7#[derive(Copy, Clone, PartialEq, Debug)]
8pub enum AssocOp {
9    /// A binary op.
10    Binary(BinOpKind),
11    /// `?=` where ? is one of the assignable BinOps
12    AssignOp(BinOpKind),
13    /// `=`
14    Assign,
15    /// `as`
16    Cast,
17    /// `..` or `..=` range
18    Range(RangeLimits),
19}
20
21#[derive(PartialEq, Debug)]
22pub enum Fixity {
23    /// The operator is left-associative
24    Left,
25    /// The operator is right-associative
26    Right,
27    /// The operator is not associative
28    None,
29}
30
31impl AssocOp {
32    /// Creates a new AssocOp from a token.
33    pub fn from_token(t: &Token) -> Option<AssocOp> {
34        use AssocOp::*;
35        match t.kind {
36            token::Eq => Some(Assign),
37            token::Plus => Some(Binary(BinOpKind::Add)),
38            token::Minus => Some(Binary(BinOpKind::Sub)),
39            token::Star => Some(Binary(BinOpKind::Mul)),
40            token::Slash => Some(Binary(BinOpKind::Div)),
41            token::Percent => Some(Binary(BinOpKind::Rem)),
42            token::Caret => Some(Binary(BinOpKind::BitXor)),
43            token::And => Some(Binary(BinOpKind::BitAnd)),
44            token::Or => Some(Binary(BinOpKind::BitOr)),
45            token::Shl => Some(Binary(BinOpKind::Shl)),
46            token::Shr => Some(Binary(BinOpKind::Shr)),
47            token::PlusEq => Some(AssignOp(BinOpKind::Add)),
48            token::MinusEq => Some(AssignOp(BinOpKind::Sub)),
49            token::StarEq => Some(AssignOp(BinOpKind::Mul)),
50            token::SlashEq => Some(AssignOp(BinOpKind::Div)),
51            token::PercentEq => Some(AssignOp(BinOpKind::Rem)),
52            token::CaretEq => Some(AssignOp(BinOpKind::BitXor)),
53            token::AndEq => Some(AssignOp(BinOpKind::BitAnd)),
54            token::OrEq => Some(AssignOp(BinOpKind::BitOr)),
55            token::ShlEq => Some(AssignOp(BinOpKind::Shl)),
56            token::ShrEq => Some(AssignOp(BinOpKind::Shr)),
57            token::Lt => Some(Binary(BinOpKind::Lt)),
58            token::Le => Some(Binary(BinOpKind::Le)),
59            token::Ge => Some(Binary(BinOpKind::Ge)),
60            token::Gt => Some(Binary(BinOpKind::Gt)),
61            token::EqEq => Some(Binary(BinOpKind::Eq)),
62            token::Ne => Some(Binary(BinOpKind::Ne)),
63            token::AndAnd => Some(Binary(BinOpKind::And)),
64            token::OrOr => Some(Binary(BinOpKind::Or)),
65            token::DotDot => Some(Range(RangeLimits::HalfOpen)),
66            // DotDotDot is no longer supported, but we need some way to display the error
67            token::DotDotEq | token::DotDotDot => Some(Range(RangeLimits::Closed)),
68            // `<-` should probably be `< -`
69            token::LArrow => Some(Binary(BinOpKind::Lt)),
70            _ if t.is_keyword(kw::As) => Some(Cast),
71            _ => None,
72        }
73    }
74
75    /// Gets the precedence of this operator
76    pub fn precedence(&self) -> ExprPrecedence {
77        use AssocOp::*;
78        match *self {
79            Cast => ExprPrecedence::Cast,
80            Binary(bin_op) => bin_op.precedence(),
81            Range(_) => ExprPrecedence::Range,
82            Assign | AssignOp(_) => ExprPrecedence::Assign,
83        }
84    }
85
86    /// Gets the fixity of this operator
87    pub fn fixity(&self) -> Fixity {
88        use AssocOp::*;
89        // NOTE: it is a bug to have an operators that has same precedence but different fixities!
90        match *self {
91            Assign | AssignOp(_) => Fixity::Right,
92            Binary(binop) => binop.fixity(),
93            Cast => Fixity::Left,
94            Range(_) => Fixity::None,
95        }
96    }
97
98    pub fn is_comparison(&self) -> bool {
99        use AssocOp::*;
100        match *self {
101            Binary(binop) => binop.is_comparison(),
102            Assign | AssignOp(_) | Cast | Range(_) => false,
103        }
104    }
105
106    pub fn is_assign_like(&self) -> bool {
107        use AssocOp::*;
108        match *self {
109            Assign | AssignOp(_) => true,
110            Cast | Binary(_) | Range(_) => false,
111        }
112    }
113
114    /// This operator could be used to follow a block unambiguously.
115    ///
116    /// This is used for error recovery at the moment, providing a suggestion to wrap blocks with
117    /// parentheses while having a high degree of confidence on the correctness of the suggestion.
118    pub fn can_continue_expr_unambiguously(&self) -> bool {
119        use AssocOp::*;
120        use BinOpKind::*;
121        matches!(
122            self,
123            Assign | // `{ 42 } = { 42 }`
124            Binary(
125                BitXor | // `{ 42 } ^ 3`
126                Div | // `{ 42 } / 42`
127                Rem | // `{ 42 } % 2`
128                Shr | // `{ 42 } >> 2`
129                Le | // `{ 42 } <= 3`
130                Gt | // `{ 42 } > 3`
131                Ge   // `{ 42 } >= 3`
132            ) |
133            AssignOp(_) | // `{ 42 } +=`
134            // Equal | // `{ 42 } == { 42 }`    Accepting these here would regress incorrect
135            // NotEqual | // `{ 42 } != { 42 }  struct literals parser recovery.
136            Cast // `{ 42 } as usize`
137        )
138    }
139}
140
141#[derive(Clone, Copy, PartialEq, PartialOrd)]
142pub enum ExprPrecedence {
143    // return, break, yield, closures
144    Jump,
145    // = += -= *= /= %= &= |= ^= <<= >>=
146    Assign,
147    // .. ..=
148    Range,
149    // ||
150    LOr,
151    // &&
152    LAnd,
153    // == != < > <= >=
154    Compare,
155    // |
156    BitOr,
157    // ^
158    BitXor,
159    // &
160    BitAnd,
161    // << >>
162    Shift,
163    // + -
164    Sum,
165    // * / %
166    Product,
167    // as
168    Cast,
169    // unary - * ! & &mut
170    Prefix,
171    // paths, loops, function calls, array indexing, field expressions, method calls
172    Unambiguous,
173}
174
175/// In `let p = e`, operators with precedence `<=` this one requires parentheses in `e`.
176pub fn prec_let_scrutinee_needs_par() -> ExprPrecedence {
177    ExprPrecedence::LAnd
178}
179
180/// Suppose we have `let _ = e` and the `order` of `e`.
181/// Is the `order` such that `e` in `let _ = e` needs parentheses when it is on the RHS?
182///
183/// Conversely, suppose that we have `(let _ = a) OP b` and `order` is that of `OP`.
184/// Can we print this as `let _ = a OP b`?
185pub fn needs_par_as_let_scrutinee(order: ExprPrecedence) -> bool {
186    order <= prec_let_scrutinee_needs_par()
187}
188
189/// Expressions that syntactically contain an "exterior" struct literal i.e., not surrounded by any
190/// parens or other delimiters, e.g., `X { y: 1 }`, `X { y: 1 }.method()`, `foo == X { y: 1 }` and
191/// `X { y: 1 } == foo` all do, but `(X { y: 1 }) == foo` does not.
192pub fn contains_exterior_struct_lit(value: &ast::Expr) -> bool {
193    match &value.kind {
194        ast::ExprKind::Struct(..) => true,
195
196        ast::ExprKind::Assign(lhs, rhs, _)
197        | ast::ExprKind::AssignOp(_, lhs, rhs)
198        | ast::ExprKind::Binary(_, lhs, rhs) => {
199            // X { y: 1 } + X { y: 2 }
200            contains_exterior_struct_lit(lhs) || contains_exterior_struct_lit(rhs)
201        }
202        ast::ExprKind::Await(x, _)
203        | ast::ExprKind::Unary(_, x)
204        | ast::ExprKind::Cast(x, _)
205        | ast::ExprKind::Type(x, _)
206        | ast::ExprKind::Field(x, _)
207        | ast::ExprKind::Index(x, _, _)
208        | ast::ExprKind::Match(x, _, ast::MatchKind::Postfix) => {
209            // &X { y: 1 }, X { y: 1 }.y
210            contains_exterior_struct_lit(x)
211        }
212
213        ast::ExprKind::MethodCall(box ast::MethodCall { receiver, .. }) => {
214            // X { y: 1 }.bar(...)
215            contains_exterior_struct_lit(receiver)
216        }
217
218        _ => false,
219    }
220}