Skip to content

Commit

Permalink
Don't make pattern nonterminals match statement nonterminals
Browse files Browse the repository at this point in the history
  • Loading branch information
compiler-errors committed Jan 26, 2024
1 parent e7bbe8c commit 6917fc5
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 39 deletions.
49 changes: 31 additions & 18 deletions compiler/rustc_ast/src/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,9 @@ impl Token {
}

/// Returns `true` if the token can appear at the start of an expression.
///
/// **NB**: Take care when modifying this function, since it will change
/// the stable set of tokens that are allowed to match an expr nonterminal.
pub fn can_begin_expr(&self) -> bool {
match self.uninterpolate().kind {
Ident(name, is_raw) =>
Expand Down Expand Up @@ -475,24 +478,34 @@ impl Token {

/// Returns `true` if the token can appear at the start of a pattern.
///
/// Shamelessly borrowed from `can_begin_expr`, only used for diagnostics right now.
pub fn can_begin_pattern(&self) -> bool {
match self.uninterpolate().kind {
Ident(name, is_raw) =>
ident_can_begin_expr(name, self.span, is_raw), // value name or keyword
| OpenDelim(Delimiter::Bracket | Delimiter::Parenthesis) // tuple or array
| Literal(..) // literal
| BinOp(Minus) // unary minus
| BinOp(And) // reference
| AndAnd // double reference
// DotDotDot is no longer supported
| DotDot | DotDotDot | DotDotEq // ranges
| Lt | BinOp(Shl) // associated path
| ModSep => true, // global path
Interpolated(ref nt) => matches!(&nt.0, NtLiteral(..) |
NtPat(..) |
NtBlock(..) |
NtPath(..)),
/// **NB**: Take care when modifying this function, since it will change
/// the stable set of tokens that are allowed to match a pat nonterminal.
pub fn can_begin_pattern(&self, allow_leading_or: bool) -> bool {
match &self.uninterpolate().kind {
Ident(..) | // box, ref, mut, and other identifiers (can stricten)
OpenDelim(Delimiter::Parenthesis) | // tuple pattern
OpenDelim(Delimiter::Bracket) | // slice pattern
BinOp(And) | // reference
BinOp(Minus) | // negative literal
AndAnd | // double reference
Literal(_) | // literal
DotDot | // range pattern (future compat)
DotDotDot | // range pattern (future compat)
ModSep | // path
Lt | // path (UFCS constant)
BinOp(Shl) => true, // path (double UFCS)
// leading vert `|` or-pattern
BinOp(Or) => allow_leading_or,
Interpolated(nt) =>
matches!(&nt.0,
| NtBlock(..)
| NtExpr(..)
| NtLiteral(..)
| NtMeta(..)
| NtPat(..)
| NtPath(..)
| NtTy(..)
),
_ => false,
}
}
Expand Down
22 changes: 2 additions & 20 deletions compiler/rustc_parse/src/parser/nonterminal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,26 +66,8 @@ impl<'a> Parser<'a> {
token::Interpolated(nt) => may_be_ident(&nt.0),
_ => false,
},
NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr => {
match &token.kind {
token::Ident(..) | // box, ref, mut, and other identifiers (can stricten)
token::OpenDelim(Delimiter::Parenthesis) | // tuple pattern
token::OpenDelim(Delimiter::Bracket) | // slice pattern
token::BinOp(token::And) | // reference
token::BinOp(token::Minus) | // negative literal
token::AndAnd | // double reference
token::Literal(_) | // literal
token::DotDot | // range pattern (future compat)
token::DotDotDot | // range pattern (future compat)
token::ModSep | // path
token::Lt | // path (UFCS constant)
token::BinOp(token::Shl) => true, // path (double UFCS)
// leading vert `|` or-pattern
token::BinOp(token::Or) => matches!(kind, NonterminalKind::PatWithOr),
token::Interpolated(nt) => may_be_ident(&nt.0),
_ => false,
}
}
NonterminalKind::PatParam { .. } => token.can_begin_pattern(false),
NonterminalKind::PatWithOr => token.can_begin_pattern(true),
NonterminalKind::Lifetime => match &token.kind {
token::Lifetime(_) => true,
token::Interpolated(nt) => {
Expand Down
3 changes: 2 additions & 1 deletion compiler/rustc_parse/src/parser/pat.rs
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,8 @@ impl<'a> Parser<'a> {

let mut lo = self.token.span;

if self.token.is_keyword(kw::Let) && self.look_ahead(1, |tok| tok.can_begin_pattern()) {
if self.token.is_keyword(kw::Let) && self.look_ahead(1, |tok| tok.can_begin_pattern(false))
{
self.bump();
self.dcx().emit_err(RemoveLet { span: lo });
lo = self.token.span;
Expand Down
19 changes: 19 additions & 0 deletions tests/ui/pattern/patterns-dont-match-nt-statement.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// check-pass

// Make sure that a `stmt` nonterminal does not eagerly match against
// a `pat`, since this will always cause a parse error...

macro_rules! m {
($pat:pat) => {};
($stmt:stmt) => {};
}

macro_rules! m2 {
($stmt:stmt) => {
m! { $stmt }
};
}

m2! { let x = 1 }

fn main() {}

0 comments on commit 6917fc5

Please sign in to comment.