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 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)
105 | ForLoop(ast::ForLoop { label, .. })
106 | Loop(_, label, _)
107 | While(_, _, label) => {
108 return label.is_some();
109 }
110
111 Assign(e, _, _)
112 | AssignOp(_, e, _)
113 | Await(e, _)
114 | Move(e, _)
115 | Use(e, _)
116 | Binary(_, e, _)
117 | Call(e, _)
118 | Cast(e, _)
119 | Field(e, _)
120 | Index(e, _, _)
121 | Match(e, _, MatchKind::Postfix)
122 | Range(Some(e), _, _)
123 | Try(e) => {
124 expr = e;
125 }
126 MethodCall(method_call) => {
127 expr = &method_call.receiver;
128 }
129
130 AddrOf(..)
131 | Array(..)
132 | Become(..)
133 | Break(..)
134 | Closure(..)
135 | ConstBlock(..)
136 | Continue(..)
137 | FormatArgs(..)
138 | Gen(..)
139 | If(..)
140 | IncludedBytes(..)
141 | InlineAsm(..)
142 | Let(..)
143 | Lit(..)
144 | MacCall(..)
145 | Match(_, _, MatchKind::Prefix)
146 | OffsetOf(..)
147 | Paren(..)
148 | Path(..)
149 | Range(None, _, _)
150 | Repeat(..)
151 | Ret(..)
152 | Struct(..)
153 | TryBlock(..)
154 | Tup(..)
155 | Type(..)
156 | Unary(..)
157 | Underscore
158 | Yeet(..)
159 | Yield(..)
160 | UnsafeBinderCast(..)
161 | DirectConstArg(..)
162 | Err(..)
163 | Dummy => return false,
164 }
165 }
166}
167
168pub enum TrailingBrace<'a> {
169 /// Trailing brace in a macro call, like the one in `x as *const brace! {}`.
170 /// We will suggest changing the macro call to a different delimiter.
171 MacCall(&'a ast::MacCall),
172 /// Trailing brace in any other expression, such as `a + B {}`. We will
173 /// suggest wrapping the innermost expression in parentheses: `a + (B {})`.
174 Expr(&'a ast::Expr),
175}
176
177/// If an expression ends with `}`, returns the innermost expression ending in the `}`
178pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<TrailingBrace<'_>> {
179 loop {
180 match &expr.kind {
181 AddrOf(_, _, e)
182 | Assign(_, e, _)
183 | AssignOp(_, _, e)
184 | Binary(_, _, e)
185 | Break(_, Some(e))
186 | Let(_, e, _, _)
187 | Range(_, Some(e), _)
188 | Ret(Some(e))
189 | Unary(_, e)
190 | Yeet(Some(e))
191 | Move(e, _)
192 | Become(e) => {
193 expr = e;
194 }
195 Yield(kind) => match kind.expr() {
196 Some(e) => expr = e,
197 None => break None,
198 },
199 Closure(closure) => {
200 expr = &closure.body;
201 }
202 Gen(..)
203 | Block(..)
204 | ForLoop { .. }
205 | If(..)
206 | Loop(..)
207 | Match(..)
208 | Struct(..)
209 | TryBlock(..)
210 | While(..)
211 | ConstBlock(_) => break Some(TrailingBrace::Expr(expr)),
212
213 Cast(_, ty) => {
214 break type_trailing_braced_mac_call(ty).map(TrailingBrace::MacCall);
215 }
216
217 MacCall(mac) => {
218 break (mac.args.delim == Delimiter::Brace).then_some(TrailingBrace::MacCall(mac));
219 }
220
221 InlineAsm(_) | OffsetOf(_, _) | IncludedBytes(_) | FormatArgs(_) => {
222 // These should have been denied pre-expansion.
223 break None;
224 }
225
226 Break(_, None)
227 | Range(_, None, _)
228 | Ret(None)
229 | Array(_)
230 | Call(_, _)
231 | MethodCall(_)
232 | Tup(_)
233 | Lit(_)
234 | Type(_, _)
235 | Await(_, _)
236 | Use(_, _)
237 | Field(_, _)
238 | Index(_, _, _)
239 | Underscore
240 | Path(_, _)
241 | Continue(_)
242 | Repeat(_, _)
243 | Paren(_)
244 | Try(_)
245 | Yeet(None)
246 | UnsafeBinderCast(..)
247 | DirectConstArg(..)
248 | Err(_)
249 | Dummy => {
250 break None;
251 }
252 }
253 }
254}
255
256/// If the type's last token is `}`, it must be due to a braced macro call, such
257/// as in `*const brace! { ... }`. Returns that trailing macro call.
258fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> {
259 loop {
260 match &ty.kind {
261 ast::TyKind::MacCall(mac) => {
262 break (mac.args.delim == Delimiter::Brace).then_some(mac);
263 }
264
265 ast::TyKind::Ptr(mut_ty)
266 | ast::TyKind::Ref(_, mut_ty)
267 | ast::TyKind::PinnedRef(_, mut_ty) => {
268 ty = &mut_ty.ty;
269 }
270
271 ast::TyKind::UnsafeBinder(binder) => {
272 ty = &binder.inner_ty;
273 }
274
275 ast::TyKind::FnPtr(fn_ty) => match &fn_ty.decl.output {
276 ast::FnRetTy::Default(_) => break None,
277 ast::FnRetTy::Ty(ret) => ty = ret,
278 },
279
280 ast::TyKind::Path(_, path) => match path_return_type(path) {
281 Some(trailing_ty) => ty = trailing_ty,
282 None => break None,
283 },
284
285 ast::TyKind::TraitObject(bounds, _) | ast::TyKind::ImplTrait(_, bounds) => {
286 match bounds.last() {
287 Some(ast::GenericBound::Trait(bound)) => {
288 match path_return_type(&bound.trait_ref.path) {
289 Some(trailing_ty) => ty = trailing_ty,
290 None => break None,
291 }
292 }
293 Some(ast::GenericBound::Outlives(_) | ast::GenericBound::Use(..)) | None => {
294 break None;
295 }
296 }
297 }
298
299 ast::TyKind::Slice(..)
300 | ast::TyKind::Array(..)
301 | ast::TyKind::Never
302 | ast::TyKind::Tup(..)
303 | ast::TyKind::Paren(..)
304 | ast::TyKind::Infer
305 | ast::TyKind::ImplicitSelf
306 | ast::TyKind::CVarArgs
307 | ast::TyKind::Pat(..)
308 | ast::TyKind::FieldOf(..)
309 | ast::TyKind::View(..)
310 | ast::TyKind::DirectConstArg(..)
311 | ast::TyKind::Dummy
312 | ast::TyKind::Err(..) => break None,
313 }
314 }
315}
316
317/// Returns the trailing return type in the given path, if it has one.
318///
319/// ```ignore (illustrative)
320/// ::std::ops::FnOnce(&str) -> fn() -> *const c_void
321/// ^^^^^^^^^^^^^^^^^^^^^
322/// ```
323fn path_return_type(path: &ast::Path) -> Option<&ast::Ty> {
324 let last_segment = path.segments.last()?;
325 let args = last_segment.args.as_ref()?;
326 match &**args {
327 ast::GenericArgs::Parenthesized(args) => match &args.output {
328 ast::FnRetTy::Default(_) => None,
329 ast::FnRetTy::Ty(ret) => Some(ret),
330 },
331 ast::GenericArgs::AngleBracketed(_) | ast::GenericArgs::ParenthesizedElided(_) => None,
332 }
333}