diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs index 86383af1f7c83..841db8da52836 100644 --- a/compiler/rustc_ast/src/util/classify.rs +++ b/compiler/rustc_ast/src/util/classify.rs @@ -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? @@ -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 { @@ -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 @@ -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) diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 52e3e33691a13..fc479e7be5615 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -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..=`. @@ -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);