about summary refs log tree commit diff
diff options
context:
space:
mode:
authorDavid Tolnay <dtolnay@gmail.com>2024-04-19 18:46:16 -0700
committerDavid Tolnay <dtolnay@gmail.com>2024-05-11 18:18:20 -0700
commit10227eaee70fb9e042bb0b317ad562677c81661d (patch)
treeadd945ead91ec3a04a87c51c290dc4558d6d2939
parent53521faf06cb5ada4df7984fcd53b602b4ee2dd0 (diff)
downloadrust-10227eaee70fb9e042bb0b317ad562677c81661d.tar.gz
rust-10227eaee70fb9e042bb0b317ad562677c81661d.zip
Add classify::expr_is_complete
-rw-r--r--compiler/rustc_ast/src/util/classify.rs96
-rw-r--r--compiler/rustc_parse/src/parser/expr.rs62
2 files changed, 60 insertions, 98 deletions
diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs
index 86383af1f7c..f6e9e1a87c4 100644
--- a/compiler/rustc_ast/src/util/classify.rs
+++ b/compiler/rustc_ast/src/util/classify.rs
@@ -1,12 +1,60 @@
 //! Routines the parser and pretty-printer use to classify AST nodes.
 
+use crate::ast::ExprKind::*;
 use crate::{ast, token::Delimiter};
 
+/// This classification determines whether various syntactic positions break out
+/// of parsing the current expression (true) or continue parsing more of the
+/// same expression (false).
+///
+/// For example, it's relevant in the parsing of match arms:
+///
+/// ```ignore (illustrative)
+/// match ... {
+///     // Is this calling $e as a function, or is it the start of a new arm
+///     // with a tuple pattern?
+///     _ => $e (
+///             ^                                                          )
+///
+///     // Is this an Index operation, or new arm with a slice pattern?
+///     _ => $e [
+///             ^                                                          ]
+///
+///     // Is this a binary operator, or leading vert in a new arm? Same for
+///     // other punctuation which can either be a binary operator in
+///     // expression or unary operator in pattern, such as `&` and `-`.
+///     _ => $e |
+///             ^
+/// }
+/// ```
+///
+/// If $e is something like `{}` or `if … {}`, then terminate the current
+/// arm and parse a new arm.
+///
+/// If $e is something like `path::to` or `(…)`, continue parsing the same
+/// arm.
+///
+/// *Almost* the same classification is used as an early bail-out for parsing
+/// statements. See `expr_requires_semi_to_be_stmt`.
+pub fn expr_is_complete(e: &ast::Expr) -> bool {
+    matches!(
+        e.kind,
+        If(..)
+            | Match(..)
+            | Block(..)
+            | While(..)
+            | Loop(..)
+            | ForLoop { .. }
+            | TryBlock(..)
+            | ConstBlock(..)
+    )
+}
+
 /// Does this expression require a semicolon to be treated as a statement?
 ///
 /// The negation of this: "can this expression be used as a statement without a
-/// semicolon" -- is used as an early bail-out in the parser so that, for
-/// instance,
+/// semicolon" -- is used as an early bail-out when parsing statements so that,
+/// for instance,
 ///
 /// ```ignore (illustrative)
 /// if true {...} else {...}
@@ -15,56 +63,26 @@ use crate::{ast, token::Delimiter};
 ///
 /// isn't parsed as `(if true {...} else {...} | x) | 5`.
 ///
-/// Nearly the same early bail-out also occurs in the right-hand side of match
-/// arms:
+/// Surprising special case: even though braced macro calls like `m! {}`
+/// normally do not introduce a boundary when found at the head of a match arm,
+/// they do terminate the parsing of a statement.
 ///
 /// ```ignore (illustrative)
-/// match i {
-///     0 => if true {...} else {...}
-///     | x => {}
+/// match ... {
+///     _ => m! {} (),  // macro that expands to a function, which is then called
 /// }
-/// ```
-///
-/// Here the `|` is a leading vert in a second match arm. It is not a binary
-/// operator with the If as its left operand. If the first arm were some other
-/// expression for which `expr_requires_semi_to_be_stmt` returns true, then the
-/// `|` on the next line would be a binary operator (leading to a parse error).
 ///
-/// The statement case and the match-arm case are "nearly" the same early
-/// bail-out because of 1 edge case. Macro calls with brace delimiter terminate
-/// a statement without a semicolon, but do not terminate a match-arm without
-/// comma.
-///
-/// ```ignore (illustrative)
-/// m! {} - 1;  // two statements: a macro call followed by -1 literal
-///
-/// match () {
-///     _ => m! {} - 1,  // binary subtraction operator
-/// }
+/// let _ = { m! {} () };  // macro call followed by unit
 /// ```
 pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool {
-    use ast::ExprKind::*;
-
     match &e.kind {
-        If(..)
-        | Match(..)
-        | Block(..)
-        | While(..)
-        | Loop(..)
-        | ForLoop { .. }
-        | TryBlock(..)
-        | ConstBlock(..) => false,
-
         MacCall(mac_call) => mac_call.args.delim != Delimiter::Brace,
-
-        _ => true,
+        _ => !expr_is_complete(e),
     }
 }
 
 /// If an expression ends with `}`, returns the innermost expression ending in the `}`
 pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> {
-    use ast::ExprKind::*;
-
     loop {
         match &expr.kind {
             AddrOf(_, _, e)
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 52e3e33691a..441aa5b0806 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -496,51 +496,8 @@ impl<'a> Parser<'a> {
     }
 
     /// Checks if this expression is a successfully parsed statement.
-    ///
-    /// This determines whether to continue parsing more of an expression in a
-    /// match arm (false) vs continue to the next arm (true).
-    ///
-    /// ```ignore (illustrative)
-    /// match ... {
-    ///     // Is this calling $e as a function, or is it the start of a new arm
-    ///     // with a tuple pattern?
-    ///     _ => $e (
-    ///             ^                                                          )
-    ///
-    ///     // Is this an Index operation, or new arm with a slice pattern?
-    ///     _ => $e [
-    ///             ^                                                          ]
-    ///
-    ///     // Is this a binary operator, or leading vert in a new arm? Same for
-    ///     // other punctuation which can either be a binary operator in
-    ///     // expression or unary operator in pattern, such as `&` and `-`.
-    ///     _ => $e |
-    ///             ^
-    /// }
-    /// ```
-    ///
-    /// If $e is something like `path::to` or `(…)`, continue parsing the same
-    /// arm.
-    ///
-    /// If $e is something like `{}` or `if … {}`, then terminate the current
-    /// arm and parse a new arm.
     fn expr_is_complete(&self, e: &Expr) -> bool {
-        self.restrictions.contains(Restrictions::STMT_EXPR)
-            && match e.kind {
-                // Surprising special case: even though braced macro calls like
-                // `m! {}` normally introduce a statement boundary when found at
-                // the head of a statement, in match arms they do not terminate
-                // the arm.
-                //
-                //     let _ = { m! {} () };  // macro call followed by unit
-                //
-                //     match ... {
-                //         _ => m! {} (),  // macro that expands to a function, which is then called
-                //     }
-                //
-                ExprKind::MacCall(_) => false,
-                _ => !classify::expr_requires_semi_to_be_stmt(e),
-            }
+        self.restrictions.contains(Restrictions::STMT_EXPR) && classify::expr_is_complete(e)
     }
 
     /// Parses `x..y`, `x..=y`, and `x..`/`x..=`.
@@ -3203,21 +3160,8 @@ impl<'a> Parser<'a> {
                         err
                     })?;
 
-                let require_comma = match expr.kind {
-                    // Special case: braced macro calls require comma in a match
-                    // arm, even though they do not require semicolon in a
-                    // statement.
-                    //
-                    //     m! {}  // okay without semicolon
-                    //
-                    //     match ... {
-                    //         _ => m! {},  // requires comma
-                    //         _ => ...
-                    //     }
-                    //
-                    ExprKind::MacCall(_) => true,
-                    _ => classify::expr_requires_semi_to_be_stmt(&expr),
-                } && this.token != token::CloseDelim(Delimiter::Brace);
+                let require_comma = !classify::expr_is_complete(&expr)
+                    && this.token != token::CloseDelim(Delimiter::Brace);
 
                 if !require_comma {
                     arm_body = Some(expr);