From 6917fc592301ebc9f4d80d46e9820b6f9600dec2 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Mon, 22 Jan 2024 02:29:21 +0000 Subject: [PATCH] Don't make pattern nonterminals match statement nonterminals --- compiler/rustc_ast/src/token.rs | 49 ++++++++++++------- .../rustc_parse/src/parser/nonterminal.rs | 22 +-------- compiler/rustc_parse/src/parser/pat.rs | 3 +- .../patterns-dont-match-nt-statement.rs | 19 +++++++ 4 files changed, 54 insertions(+), 39 deletions(-) create mode 100644 tests/ui/pattern/patterns-dont-match-nt-statement.rs diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index d62462b1ae33b..87a0457242f49 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -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) => @@ -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, } } diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index 071d6b72f3b96..99214fccda363 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -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) => { diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 7918e03750ce3..a74cbc34e3e8c 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -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; diff --git a/tests/ui/pattern/patterns-dont-match-nt-statement.rs b/tests/ui/pattern/patterns-dont-match-nt-statement.rs new file mode 100644 index 0000000000000..2fb1ec7746175 --- /dev/null +++ b/tests/ui/pattern/patterns-dont-match-nt-statement.rs @@ -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() {}