Skip to content

Commit

Permalink
Add classify::expr_requires_comma_to_be_match_arm
Browse files Browse the repository at this point in the history
  • Loading branch information
dtolnay committed May 11, 2024
1 parent 53521fa commit 5d5954c
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 51 deletions.
48 changes: 27 additions & 21 deletions compiler/rustc_ast/src/util/classify.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Routines the parser and pretty-printer use to classify AST nodes.
use crate::ast::ExprKind::*;
use crate::{ast, token::Delimiter};

/// Does this expression require a semicolon to be treated as a statement?
Expand All @@ -14,9 +15,27 @@ use crate::{ast, token::Delimiter};
/// ```
///
/// isn't parsed as `(if true {...} else {...} | x) | 5`.
pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool {
match &e.kind {
If(..)
| Match(..)
| Block(..)
| While(..)
| Loop(..)
| ForLoop { .. }
| TryBlock(..)
| ConstBlock(..) => false,

MacCall(mac_call) => mac_call.args.delim != Delimiter::Brace,

_ => true,
}
}

/// Does this expression require a comma to separate it from the next match arm?
///
/// Nearly the same early bail-out also occurs in the right-hand side of match
/// arms:
/// The right-hand side of match arms use almost the same early bail-out as
/// statements do (see `expr_requires_semi_to_be_stmt`):
///
/// ```ignore (illustrative)
/// match i {
Expand All @@ -27,8 +46,9 @@ use crate::{ast, token::Delimiter};
///
/// 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).
/// expression for which `expr_requires_comma_to_be_match_arm` returns true,
/// then the `|` on the next line would instead 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
Expand All @@ -42,29 +62,15 @@ use crate::{ast, token::Delimiter};
/// _ => m! {} - 1, // binary subtraction operator
/// }
/// ```
pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool {
use ast::ExprKind::*;

pub fn expr_requires_comma_to_be_match_arm(e: &ast::Expr) -> bool {
match &e.kind {
If(..)
| Match(..)
| Block(..)
| While(..)
| Loop(..)
| ForLoop { .. }
| TryBlock(..)
| ConstBlock(..) => false,

MacCall(mac_call) => mac_call.args.delim != Delimiter::Brace,

_ => true,
MacCall(_) => true,
_ => expr_requires_semi_to_be_stmt(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)
Expand Down
55 changes: 25 additions & 30 deletions compiler/rustc_parse/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -525,22 +525,19 @@ impl<'a> Parser<'a> {
/// 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 {
// 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
// }
//
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),
}
&& !classify::expr_requires_comma_to_be_match_arm(e)
}

/// Parses `x..y`, `x..=y`, and `x..`/`x..=`.
Expand Down Expand Up @@ -3203,21 +3200,19 @@ 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);
// 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
// _ => ...
// }
//
let require_comma = classify::expr_requires_comma_to_be_match_arm(&expr)
&& this.token != token::CloseDelim(Delimiter::Brace);

if !require_comma {
arm_body = Some(expr);
Expand Down

0 comments on commit 5d5954c

Please sign in to comment.