Skip to content

Commit 10227ea

Browse files
committed
Add classify::expr_is_complete
1 parent 53521fa commit 10227ea

File tree

2 files changed

+60
-98
lines changed

2 files changed

+60
-98
lines changed

compiler/rustc_ast/src/util/classify.rs

+57-39
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,60 @@
11
//! Routines the parser and pretty-printer use to classify AST nodes.
22
3+
use crate::ast::ExprKind::*;
34
use crate::{ast, token::Delimiter};
45

6+
/// This classification determines whether various syntactic positions break out
7+
/// of parsing the current expression (true) or continue parsing more of the
8+
/// same expression (false).
9+
///
10+
/// For example, it's relevant in the parsing of match arms:
11+
///
12+
/// ```ignore (illustrative)
13+
/// match ... {
14+
/// // Is this calling $e as a function, or is it the start of a new arm
15+
/// // with a tuple pattern?
16+
/// _ => $e (
17+
/// ^ )
18+
///
19+
/// // Is this an Index operation, or new arm with a slice pattern?
20+
/// _ => $e [
21+
/// ^ ]
22+
///
23+
/// // Is this a binary operator, or leading vert in a new arm? Same for
24+
/// // other punctuation which can either be a binary operator in
25+
/// // expression or unary operator in pattern, such as `&` and `-`.
26+
/// _ => $e |
27+
/// ^
28+
/// }
29+
/// ```
30+
///
31+
/// If $e is something like `{}` or `if … {}`, then terminate the current
32+
/// arm and parse a new arm.
33+
///
34+
/// If $e is something like `path::to` or `(…)`, continue parsing the same
35+
/// arm.
36+
///
37+
/// *Almost* the same classification is used as an early bail-out for parsing
38+
/// statements. See `expr_requires_semi_to_be_stmt`.
39+
pub fn expr_is_complete(e: &ast::Expr) -> bool {
40+
matches!(
41+
e.kind,
42+
If(..)
43+
| Match(..)
44+
| Block(..)
45+
| While(..)
46+
| Loop(..)
47+
| ForLoop { .. }
48+
| TryBlock(..)
49+
| ConstBlock(..)
50+
)
51+
}
52+
553
/// Does this expression require a semicolon to be treated as a statement?
654
///
755
/// The negation of this: "can this expression be used as a statement without a
8-
/// semicolon" -- is used as an early bail-out in the parser so that, for
9-
/// instance,
56+
/// semicolon" -- is used as an early bail-out when parsing statements so that,
57+
/// for instance,
1058
///
1159
/// ```ignore (illustrative)
1260
/// if true {...} else {...}
@@ -15,56 +63,26 @@ use crate::{ast, token::Delimiter};
1563
///
1664
/// isn't parsed as `(if true {...} else {...} | x) | 5`.
1765
///
18-
/// Nearly the same early bail-out also occurs in the right-hand side of match
19-
/// arms:
66+
/// Surprising special case: even though braced macro calls like `m! {}`
67+
/// normally do not introduce a boundary when found at the head of a match arm,
68+
/// they do terminate the parsing of a statement.
2069
///
2170
/// ```ignore (illustrative)
22-
/// match i {
23-
/// 0 => if true {...} else {...}
24-
/// | x => {}
71+
/// match ... {
72+
/// _ => m! {} (), // macro that expands to a function, which is then called
2573
/// }
26-
/// ```
27-
///
28-
/// Here the `|` is a leading vert in a second match arm. It is not a binary
29-
/// operator with the If as its left operand. If the first arm were some other
30-
/// expression for which `expr_requires_semi_to_be_stmt` returns true, then the
31-
/// `|` on the next line would be a binary operator (leading to a parse error).
3274
///
33-
/// The statement case and the match-arm case are "nearly" the same early
34-
/// bail-out because of 1 edge case. Macro calls with brace delimiter terminate
35-
/// a statement without a semicolon, but do not terminate a match-arm without
36-
/// comma.
37-
///
38-
/// ```ignore (illustrative)
39-
/// m! {} - 1; // two statements: a macro call followed by -1 literal
40-
///
41-
/// match () {
42-
/// _ => m! {} - 1, // binary subtraction operator
43-
/// }
75+
/// let _ = { m! {} () }; // macro call followed by unit
4476
/// ```
4577
pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool {
46-
use ast::ExprKind::*;
47-
4878
match &e.kind {
49-
If(..)
50-
| Match(..)
51-
| Block(..)
52-
| While(..)
53-
| Loop(..)
54-
| ForLoop { .. }
55-
| TryBlock(..)
56-
| ConstBlock(..) => false,
57-
5879
MacCall(mac_call) => mac_call.args.delim != Delimiter::Brace,
59-
60-
_ => true,
80+
_ => !expr_is_complete(e),
6181
}
6282
}
6383

6484
/// If an expression ends with `}`, returns the innermost expression ending in the `}`
6585
pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> {
66-
use ast::ExprKind::*;
67-
6886
loop {
6987
match &expr.kind {
7088
AddrOf(_, _, e)

compiler/rustc_parse/src/parser/expr.rs

+3-59
Original file line numberDiff line numberDiff line change
@@ -496,51 +496,8 @@ impl<'a> Parser<'a> {
496496
}
497497

498498
/// Checks if this expression is a successfully parsed statement.
499-
///
500-
/// This determines whether to continue parsing more of an expression in a
501-
/// match arm (false) vs continue to the next arm (true).
502-
///
503-
/// ```ignore (illustrative)
504-
/// match ... {
505-
/// // Is this calling $e as a function, or is it the start of a new arm
506-
/// // with a tuple pattern?
507-
/// _ => $e (
508-
/// ^ )
509-
///
510-
/// // Is this an Index operation, or new arm with a slice pattern?
511-
/// _ => $e [
512-
/// ^ ]
513-
///
514-
/// // Is this a binary operator, or leading vert in a new arm? Same for
515-
/// // other punctuation which can either be a binary operator in
516-
/// // expression or unary operator in pattern, such as `&` and `-`.
517-
/// _ => $e |
518-
/// ^
519-
/// }
520-
/// ```
521-
///
522-
/// If $e is something like `path::to` or `(…)`, continue parsing the same
523-
/// arm.
524-
///
525-
/// If $e is something like `{}` or `if … {}`, then terminate the current
526-
/// arm and parse a new arm.
527499
fn expr_is_complete(&self, e: &Expr) -> bool {
528-
self.restrictions.contains(Restrictions::STMT_EXPR)
529-
&& match e.kind {
530-
// Surprising special case: even though braced macro calls like
531-
// `m! {}` normally introduce a statement boundary when found at
532-
// the head of a statement, in match arms they do not terminate
533-
// the arm.
534-
//
535-
// let _ = { m! {} () }; // macro call followed by unit
536-
//
537-
// match ... {
538-
// _ => m! {} (), // macro that expands to a function, which is then called
539-
// }
540-
//
541-
ExprKind::MacCall(_) => false,
542-
_ => !classify::expr_requires_semi_to_be_stmt(e),
543-
}
500+
self.restrictions.contains(Restrictions::STMT_EXPR) && classify::expr_is_complete(e)
544501
}
545502

546503
/// Parses `x..y`, `x..=y`, and `x..`/`x..=`.
@@ -3203,21 +3160,8 @@ impl<'a> Parser<'a> {
32033160
err
32043161
})?;
32053162

3206-
let require_comma = match expr.kind {
3207-
// Special case: braced macro calls require comma in a match
3208-
// arm, even though they do not require semicolon in a
3209-
// statement.
3210-
//
3211-
// m! {} // okay without semicolon
3212-
//
3213-
// match ... {
3214-
// _ => m! {}, // requires comma
3215-
// _ => ...
3216-
// }
3217-
//
3218-
ExprKind::MacCall(_) => true,
3219-
_ => classify::expr_requires_semi_to_be_stmt(&expr),
3220-
} && this.token != token::CloseDelim(Delimiter::Brace);
3163+
let require_comma = !classify::expr_is_complete(&expr)
3164+
&& this.token != token::CloseDelim(Delimiter::Brace);
32213165

32223166
if !require_comma {
32233167
arm_body = Some(expr);

0 commit comments

Comments
 (0)