Add classify::expr_is_complete
This commit is contained in:
parent
53521faf06
commit
10227eaee7
@ -1,12 +1,60 @@
|
|||||||
//! Routines the parser and pretty-printer use to classify AST nodes.
|
//! Routines the parser and pretty-printer use to classify AST nodes.
|
||||||
|
|
||||||
|
use crate::ast::ExprKind::*;
|
||||||
use crate::{ast, token::Delimiter};
|
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?
|
/// 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
|
/// 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
|
/// semicolon" -- is used as an early bail-out when parsing statements so that,
|
||||||
/// instance,
|
/// for instance,
|
||||||
///
|
///
|
||||||
/// ```ignore (illustrative)
|
/// ```ignore (illustrative)
|
||||||
/// if true {...} else {...}
|
/// if true {...} else {...}
|
||||||
@ -15,56 +63,26 @@
|
|||||||
///
|
///
|
||||||
/// isn't parsed as `(if true {...} else {...} | x) | 5`.
|
/// isn't parsed as `(if true {...} else {...} | x) | 5`.
|
||||||
///
|
///
|
||||||
/// Nearly the same early bail-out also occurs in the right-hand side of match
|
/// Surprising special case: even though braced macro calls like `m! {}`
|
||||||
/// arms:
|
/// 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)
|
/// ```ignore (illustrative)
|
||||||
/// match i {
|
/// match ... {
|
||||||
/// 0 => if true {...} else {...}
|
/// _ => m! {} (), // macro that expands to a function, which is then called
|
||||||
/// | x => {}
|
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
|
||||||
///
|
///
|
||||||
/// Here the `|` is a leading vert in a second match arm. It is not a binary
|
/// let _ = { m! {} () }; // macro call followed by unit
|
||||||
/// 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
|
|
||||||
/// }
|
|
||||||
/// ```
|
/// ```
|
||||||
pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool {
|
pub fn expr_requires_semi_to_be_stmt(e: &ast::Expr) -> bool {
|
||||||
use ast::ExprKind::*;
|
|
||||||
|
|
||||||
match &e.kind {
|
match &e.kind {
|
||||||
If(..)
|
|
||||||
| Match(..)
|
|
||||||
| Block(..)
|
|
||||||
| While(..)
|
|
||||||
| Loop(..)
|
|
||||||
| ForLoop { .. }
|
|
||||||
| TryBlock(..)
|
|
||||||
| ConstBlock(..) => false,
|
|
||||||
|
|
||||||
MacCall(mac_call) => mac_call.args.delim != Delimiter::Brace,
|
MacCall(mac_call) => mac_call.args.delim != Delimiter::Brace,
|
||||||
|
_ => !expr_is_complete(e),
|
||||||
_ => true,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If an expression ends with `}`, returns the innermost expression ending in the `}`
|
/// If an expression ends with `}`, returns the innermost expression ending in the `}`
|
||||||
pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> {
|
pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> {
|
||||||
use ast::ExprKind::*;
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match &expr.kind {
|
match &expr.kind {
|
||||||
AddrOf(_, _, e)
|
AddrOf(_, _, e)
|
||||||
|
@ -496,51 +496,8 @@ pub fn check_assoc_op(&self) -> Option<Spanned<AssocOp>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if this expression is a successfully parsed statement.
|
/// 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 {
|
fn expr_is_complete(&self, e: &Expr) -> bool {
|
||||||
self.restrictions.contains(Restrictions::STMT_EXPR)
|
self.restrictions.contains(Restrictions::STMT_EXPR) && classify::expr_is_complete(e)
|
||||||
&& 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),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses `x..y`, `x..=y`, and `x..`/`x..=`.
|
/// Parses `x..y`, `x..=y`, and `x..`/`x..=`.
|
||||||
@ -3203,21 +3160,8 @@ pub(super) fn parse_arm(&mut self) -> PResult<'a, Arm> {
|
|||||||
err
|
err
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let require_comma = match expr.kind {
|
let require_comma = !classify::expr_is_complete(&expr)
|
||||||
// Special case: braced macro calls require comma in a match
|
&& this.token != token::CloseDelim(Delimiter::Brace);
|
||||||
// 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);
|
|
||||||
|
|
||||||
if !require_comma {
|
if !require_comma {
|
||||||
arm_body = Some(expr);
|
arm_body = Some(expr);
|
||||||
|
Loading…
Reference in New Issue
Block a user