Skip to main content

rustc_ast/util/
classify.rs

1//! Routines the parser and pretty-printer use to classify AST nodes.
2
3use crate::ast::ExprKind::*;
4use crate::ast::{self, MatchKind};
5use crate::token::Delimiter;
6
7/// This classification determines whether various syntactic positions break out
8/// of parsing the current expression (true) or continue parsing more of the
9/// same expression (false).
10///
11/// For example, it's relevant in the parsing of match arms:
12///
13/// ```ignore (illustrative)
14/// match ... {
15///     // Is this calling $e as a function, or is it the start of a new arm
16///     // with a tuple pattern?
17///     _ => $e (
18///             ^                                                          )
19///
20///     // Is this an Index operation, or new arm with a slice pattern?
21///     _ => $e [
22///             ^                                                          ]
23///
24///     // Is this a binary operator, or leading vert in a new arm? Same for
25///     // other punctuation which can either be a binary operator in
26///     // expression or unary operator in pattern, such as `&` and `-`.
27///     _ => $e |
28///             ^
29/// }
30/// ```
31///
32/// If $e is something like `{}` or `if … {}`, then terminate the current
33/// arm and parse a new arm.
34///
35/// If $e is something like `path::to` or `(…)`, continue parsing the same
36/// arm.
37///
38/// *Almost* the same classification is used as an early bail-out for parsing
39/// statements. See `expr_requires_semi_to_be_stmt`.
40pub fn expr_is_complete(e: &ast::Expr) -> bool {
41    #[allow(non_exhaustive_omitted_patterns)] match e.kind {
    If(..) | Match(..) | Block(..) | While(..) | Loop(..) | ForLoop { .. } |
        TryBlock(..) | ConstBlock(..) => true,
    _ => false,
}matches!(
42        e.kind,
43        If(..)
44            | Match(..)
45            | Block(..)
46            | While(..)
47            | Loop(..)
48            | ForLoop { .. }
49            | TryBlock(..)
50            | ConstBlock(..)
51    )
52}
53
54/// Does this expression require a semicolon to be treated as a statement?
55///
56/// The negation of this: "can this expression be used as a statement without a
57/// semicolon" -- is used as an early bail-out when parsing statements so that,
58/// for instance,
59///
60/// ```ignore (illustrative)
61/// if true {...} else {...}
62/// |x| 5
63/// ```
64///
65/// isn't parsed as `(if true {...} else {...} | x) | 5`.
66///
67/// Surprising special case: even though braced macro calls like `m! {}`
68/// normally do not introduce a boundary when found at the head of a match arm,
69/// they do terminate the parsing of a statement.
70///
71/// ```ignore (illustrative)
72/// match ... {
73///     _ => m! {} (),  // macro that expands to a function, which is then called
74/// }
75///
76/// let _ = { m! {} () };  // macro call followed by unit
77/// ```
78pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool {
79    match &e.kind {
80        MacCall(mac_call) => mac_call.args.delim != Delimiter::Brace,
81        _ => !expr_is_complete(e),
82    }
83}
84
85/// Returns whether the leftmost token of the given expression is the label of a
86/// labeled loop or block, such as in `'inner: loop { break 'inner 1 } + 1`.
87///
88/// Such expressions are not allowed as the value of an unlabeled break.
89///
90/// ```ignore (illustrative)
91/// 'outer: {
92///     break 'inner: loop { break 'inner 1 } + 1;  // invalid syntax
93///
94///     break 'outer 'inner: loop { break 'inner 1 } + 1;  // okay
95///
96///     break ('inner: loop { break 'inner 1 } + 1);  // okay
97///
98///     break ('inner: loop { break 'inner 1 }) + 1;  // okay
99/// }
100/// ```
101pub fn leading_labeled_expr(mut expr: &ast::Expr) -> bool {
102    loop {
103        match &expr.kind {
104            Block(_, label) | ForLoop { label, .. } | Loop(_, label, _) | While(_, _, label) => {
105                return label.is_some();
106            }
107
108            Assign(e, _, _)
109            | AssignOp(_, e, _)
110            | Await(e, _)
111            | Move(e, _)
112            | Use(e, _)
113            | Binary(_, e, _)
114            | Call(e, _)
115            | Cast(e, _)
116            | Field(e, _)
117            | Index(e, _, _)
118            | Match(e, _, MatchKind::Postfix)
119            | Range(Some(e), _, _)
120            | Try(e) => {
121                expr = e;
122            }
123            MethodCall(method_call) => {
124                expr = &method_call.receiver;
125            }
126
127            AddrOf(..)
128            | Array(..)
129            | Become(..)
130            | Break(..)
131            | Closure(..)
132            | ConstBlock(..)
133            | Continue(..)
134            | FormatArgs(..)
135            | Gen(..)
136            | If(..)
137            | IncludedBytes(..)
138            | InlineAsm(..)
139            | Let(..)
140            | Lit(..)
141            | MacCall(..)
142            | Match(_, _, MatchKind::Prefix)
143            | OffsetOf(..)
144            | Paren(..)
145            | Path(..)
146            | Range(None, _, _)
147            | Repeat(..)
148            | Ret(..)
149            | Struct(..)
150            | TryBlock(..)
151            | Tup(..)
152            | Type(..)
153            | Unary(..)
154            | Underscore
155            | Yeet(..)
156            | Yield(..)
157            | UnsafeBinderCast(..)
158            | DirectConstArg(..)
159            | Err(..)
160            | Dummy => return false,
161        }
162    }
163}
164
165pub enum TrailingBrace<'a> {
166    /// Trailing brace in a macro call, like the one in `x as *const brace! {}`.
167    /// We will suggest changing the macro call to a different delimiter.
168    MacCall(&'a ast::MacCall),
169    /// Trailing brace in any other expression, such as `a + B {}`. We will
170    /// suggest wrapping the innermost expression in parentheses: `a + (B {})`.
171    Expr(&'a ast::Expr),
172}
173
174/// If an expression ends with `}`, returns the innermost expression ending in the `}`
175pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<TrailingBrace<'_>> {
176    loop {
177        match &expr.kind {
178            AddrOf(_, _, e)
179            | Assign(_, e, _)
180            | AssignOp(_, _, e)
181            | Binary(_, _, e)
182            | Break(_, Some(e))
183            | Let(_, e, _, _)
184            | Range(_, Some(e), _)
185            | Ret(Some(e))
186            | Unary(_, e)
187            | Yeet(Some(e))
188            | Move(e, _)
189            | Become(e) => {
190                expr = e;
191            }
192            Yield(kind) => match kind.expr() {
193                Some(e) => expr = e,
194                None => break None,
195            },
196            Closure(closure) => {
197                expr = &closure.body;
198            }
199            Gen(..)
200            | Block(..)
201            | ForLoop { .. }
202            | If(..)
203            | Loop(..)
204            | Match(..)
205            | Struct(..)
206            | TryBlock(..)
207            | While(..)
208            | ConstBlock(_) => break Some(TrailingBrace::Expr(expr)),
209
210            Cast(_, ty) => {
211                break type_trailing_braced_mac_call(ty).map(TrailingBrace::MacCall);
212            }
213
214            MacCall(mac) => {
215                break (mac.args.delim == Delimiter::Brace).then_some(TrailingBrace::MacCall(mac));
216            }
217
218            InlineAsm(_) | OffsetOf(_, _) | IncludedBytes(_) | FormatArgs(_) => {
219                // These should have been denied pre-expansion.
220                break None;
221            }
222
223            Break(_, None)
224            | Range(_, None, _)
225            | Ret(None)
226            | Array(_)
227            | Call(_, _)
228            | MethodCall(_)
229            | Tup(_)
230            | Lit(_)
231            | Type(_, _)
232            | Await(_, _)
233            | Use(_, _)
234            | Field(_, _)
235            | Index(_, _, _)
236            | Underscore
237            | Path(_, _)
238            | Continue(_)
239            | Repeat(_, _)
240            | Paren(_)
241            | Try(_)
242            | Yeet(None)
243            | UnsafeBinderCast(..)
244            | DirectConstArg(..)
245            | Err(_)
246            | Dummy => {
247                break None;
248            }
249        }
250    }
251}
252
253/// If the type's last token is `}`, it must be due to a braced macro call, such
254/// as in `*const brace! { ... }`. Returns that trailing macro call.
255fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> {
256    loop {
257        match &ty.kind {
258            ast::TyKind::MacCall(mac) => {
259                break (mac.args.delim == Delimiter::Brace).then_some(mac);
260            }
261
262            ast::TyKind::Ptr(mut_ty)
263            | ast::TyKind::Ref(_, mut_ty)
264            | ast::TyKind::PinnedRef(_, mut_ty) => {
265                ty = &mut_ty.ty;
266            }
267
268            ast::TyKind::UnsafeBinder(binder) => {
269                ty = &binder.inner_ty;
270            }
271
272            ast::TyKind::FnPtr(fn_ty) => match &fn_ty.decl.output {
273                ast::FnRetTy::Default(_) => break None,
274                ast::FnRetTy::Ty(ret) => ty = ret,
275            },
276
277            ast::TyKind::Path(_, path) => match path_return_type(path) {
278                Some(trailing_ty) => ty = trailing_ty,
279                None => break None,
280            },
281
282            ast::TyKind::TraitObject(bounds, _) | ast::TyKind::ImplTrait(_, bounds) => {
283                match bounds.last() {
284                    Some(ast::GenericBound::Trait(bound)) => {
285                        match path_return_type(&bound.trait_ref.path) {
286                            Some(trailing_ty) => ty = trailing_ty,
287                            None => break None,
288                        }
289                    }
290                    Some(ast::GenericBound::Outlives(_) | ast::GenericBound::Use(..)) | None => {
291                        break None;
292                    }
293                }
294            }
295
296            ast::TyKind::Slice(..)
297            | ast::TyKind::Array(..)
298            | ast::TyKind::Never
299            | ast::TyKind::Tup(..)
300            | ast::TyKind::Paren(..)
301            | ast::TyKind::Infer
302            | ast::TyKind::ImplicitSelf
303            | ast::TyKind::CVarArgs
304            | ast::TyKind::Pat(..)
305            | ast::TyKind::FieldOf(..)
306            | ast::TyKind::View(..)
307            | ast::TyKind::DirectConstArg(..)
308            | ast::TyKind::Dummy
309            | ast::TyKind::Err(..) => break None,
310        }
311    }
312}
313
314/// Returns the trailing return type in the given path, if it has one.
315///
316/// ```ignore (illustrative)
317/// ::std::ops::FnOnce(&str) -> fn() -> *const c_void
318///                             ^^^^^^^^^^^^^^^^^^^^^
319/// ```
320fn path_return_type(path: &ast::Path) -> Option<&ast::Ty> {
321    let last_segment = path.segments.last()?;
322    let args = last_segment.args.as_ref()?;
323    match &**args {
324        ast::GenericArgs::Parenthesized(args) => match &args.output {
325            ast::FnRetTy::Default(_) => None,
326            ast::FnRetTy::Ty(ret) => Some(ret),
327        },
328        ast::GenericArgs::AngleBracketed(_) | ast::GenericArgs::ParenthesizedElided(_) => None,
329    }
330}