Rework pattern and expression nonterminal kinds.

Merge `PatParam`/`PatWithOr`, and `Expr`/`Expr2021`, for a few reasons.

- It's conceptually nice, because the two pattern kinds and the two
  expression kinds are very similar.

- With expressions in particular, there are several places where both
  expression kinds get the same treatment.

- It removes one unreachable match arm.

- Most importantly, for #124141 I will need to introduce a new type
  `MetaVarKind` that is very similar to `NonterminalKind`, but records a
  couple of extra fields for expression metavars. It's nicer to have a
  single `MetaVarKind::Expr` expression variant to hold those extra
  fields instead of duplicating them across two variants
  `MetaVarKind::{Expr,Expr2021}`. And then it makes sense for patterns
  to be treated the same way, and for `NonterminalKind` to also be
  treated the same way.

I also clarified the comments, because I have long found them a little
hard to understand.
This commit is contained in:
Nicholas Nethercote 2024-06-23 08:13:56 +10:00
parent 70fa67c0b2
commit e2aa38e6ab
5 changed files with 71 additions and 60 deletions

View File

@ -1,6 +1,8 @@
pub use BinOpToken::*; pub use BinOpToken::*;
pub use LitKind::*; pub use LitKind::*;
pub use Nonterminal::*; pub use Nonterminal::*;
pub use NtExprKind::*;
pub use NtPatKind::*;
pub use TokenKind::*; pub use TokenKind::*;
use crate::ast; use crate::ast;
@ -871,6 +873,27 @@ fn eq(&self, rhs: &TokenKind) -> bool {
} }
} }
#[derive(Debug, Copy, Clone, PartialEq, Eq, Encodable, Decodable)]
pub enum NtPatKind {
// Matches or-patterns. Was written using `pat` in edition 2021 or later.
PatWithOr,
// Doesn't match or-patterns.
// - `inferred`: was written using `pat` in edition 2015 or 2018.
// - `!inferred`: was written using `pat_param`.
PatParam { inferred: bool },
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Encodable, Decodable)]
pub enum NtExprKind {
// Matches expressions using the post-edition 2024. Was written using
// `expr` in edition 2024 or later.
Expr,
// Matches expressions using the pre-edition 2024 rules.
// - `inferred`: was written using `expr` in edition 2021 or earlier.
// - `!inferred`: was written using `expr_2021`.
Expr2021 { inferred: bool },
}
#[derive(Clone, Encodable, Decodable)] #[derive(Clone, Encodable, Decodable)]
/// For interpolation during macro expansion. /// For interpolation during macro expansion.
pub enum Nonterminal { pub enum Nonterminal {
@ -892,19 +915,8 @@ pub enum NonterminalKind {
Item, Item,
Block, Block,
Stmt, Stmt,
PatParam { Pat(NtPatKind),
/// Keep track of whether the user used `:pat_param` or `:pat` and we inferred it from the Expr(NtExprKind),
/// edition of the span. This is used for diagnostics.
inferred: bool,
},
PatWithOr,
Expr,
/// Matches an expression using the rules from edition 2021 and earlier.
Expr2021 {
/// Keep track of whether the user used `:expr` or `:expr_2021` and we inferred it from the
/// edition of the span. This is used for diagnostics AND feature gating.
inferred: bool,
},
Ty, Ty,
Ident, Ident,
Lifetime, Lifetime,
@ -926,20 +938,22 @@ pub fn from_symbol(
sym::item => NonterminalKind::Item, sym::item => NonterminalKind::Item,
sym::block => NonterminalKind::Block, sym::block => NonterminalKind::Block,
sym::stmt => NonterminalKind::Stmt, sym::stmt => NonterminalKind::Stmt,
sym::pat => match edition() { sym::pat => {
Edition::Edition2015 | Edition::Edition2018 => { if edition().at_least_rust_2021() {
NonterminalKind::PatParam { inferred: true } NonterminalKind::Pat(PatWithOr)
} else {
NonterminalKind::Pat(PatParam { inferred: true })
} }
Edition::Edition2021 | Edition::Edition2024 => NonterminalKind::PatWithOr, }
}, sym::pat_param => NonterminalKind::Pat(PatParam { inferred: false }),
sym::pat_param => NonterminalKind::PatParam { inferred: false }, sym::expr => {
sym::expr => match edition() { if edition().at_least_rust_2024() {
Edition::Edition2015 | Edition::Edition2018 | Edition::Edition2021 => { NonterminalKind::Expr(Expr)
NonterminalKind::Expr2021 { inferred: true } } else {
NonterminalKind::Expr(Expr2021 { inferred: true })
} }
Edition::Edition2024 => NonterminalKind::Expr, }
}, sym::expr_2021 => NonterminalKind::Expr(Expr2021 { inferred: false }),
sym::expr_2021 => NonterminalKind::Expr2021 { inferred: false },
sym::ty => NonterminalKind::Ty, sym::ty => NonterminalKind::Ty,
sym::ident => NonterminalKind::Ident, sym::ident => NonterminalKind::Ident,
sym::lifetime => NonterminalKind::Lifetime, sym::lifetime => NonterminalKind::Lifetime,
@ -951,15 +965,16 @@ pub fn from_symbol(
_ => return None, _ => return None,
}) })
} }
fn symbol(self) -> Symbol { fn symbol(self) -> Symbol {
match self { match self {
NonterminalKind::Item => sym::item, NonterminalKind::Item => sym::item,
NonterminalKind::Block => sym::block, NonterminalKind::Block => sym::block,
NonterminalKind::Stmt => sym::stmt, NonterminalKind::Stmt => sym::stmt,
NonterminalKind::PatParam { inferred: false } => sym::pat_param, NonterminalKind::Pat(PatParam { inferred: true } | PatWithOr) => sym::pat,
NonterminalKind::PatParam { inferred: true } | NonterminalKind::PatWithOr => sym::pat, NonterminalKind::Pat(PatParam { inferred: false }) => sym::pat_param,
NonterminalKind::Expr | NonterminalKind::Expr2021 { inferred: true } => sym::expr, NonterminalKind::Expr(Expr2021 { inferred: true } | Expr) => sym::expr,
NonterminalKind::Expr2021 { inferred: false } => sym::expr_2021, NonterminalKind::Expr(Expr2021 { inferred: false }) => sym::expr_2021,
NonterminalKind::Ty => sym::ty, NonterminalKind::Ty => sym::ty,
NonterminalKind::Ident => sym::ident, NonterminalKind::Ident => sym::ident,
NonterminalKind::Lifetime => sym::lifetime, NonterminalKind::Lifetime => sym::lifetime,

View File

@ -10,7 +10,9 @@
use ast::token::IdentIsRaw; use ast::token::IdentIsRaw;
use rustc_ast as ast; use rustc_ast as ast;
use rustc_ast::token::{self, Delimiter, NonterminalKind, Token, TokenKind, TokenKind::*}; use rustc_ast::token::{
self, Delimiter, NonterminalKind, NtPatKind::*, Token, TokenKind, TokenKind::*,
};
use rustc_ast::tokenstream::{DelimSpan, TokenStream}; use rustc_ast::tokenstream::{DelimSpan, TokenStream};
use rustc_ast::{NodeId, DUMMY_NODE_ID}; use rustc_ast::{NodeId, DUMMY_NODE_ID};
use rustc_ast_pretty::pprust; use rustc_ast_pretty::pprust;
@ -1145,7 +1147,7 @@ fn check_matcher_core<'tt>(
// Macros defined in the current crate have a real node id, // Macros defined in the current crate have a real node id,
// whereas macros from an external crate have a dummy id. // whereas macros from an external crate have a dummy id.
if def.id != DUMMY_NODE_ID if def.id != DUMMY_NODE_ID
&& matches!(kind, NonterminalKind::PatParam { inferred: true }) && matches!(kind, NonterminalKind::Pat(PatParam { inferred: true }))
&& matches!( && matches!(
next_token, next_token,
TokenTree::Token(token) if token.kind == BinOp(token::BinOpToken::Or) TokenTree::Token(token) if token.kind == BinOp(token::BinOpToken::Or)
@ -1155,7 +1157,7 @@ fn check_matcher_core<'tt>(
let suggestion = quoted_tt_to_string(&TokenTree::MetaVarDecl( let suggestion = quoted_tt_to_string(&TokenTree::MetaVarDecl(
span, span,
name, name,
Some(NonterminalKind::PatParam { inferred: false }), Some(NonterminalKind::Pat(PatParam { inferred: false })),
)); ));
sess.psess.buffer_lint( sess.psess.buffer_lint(
RUST_2021_INCOMPATIBLE_OR_PATTERNS, RUST_2021_INCOMPATIBLE_OR_PATTERNS,
@ -1188,14 +1190,14 @@ fn check_matcher_core<'tt>(
); );
err.span_label(sp, format!("not allowed after `{kind}` fragments")); err.span_label(sp, format!("not allowed after `{kind}` fragments"));
if kind == NonterminalKind::PatWithOr if kind == NonterminalKind::Pat(PatWithOr)
&& sess.psess.edition.at_least_rust_2021() && sess.psess.edition.at_least_rust_2021()
&& next_token.is_token(&BinOp(token::BinOpToken::Or)) && next_token.is_token(&BinOp(token::BinOpToken::Or))
{ {
let suggestion = quoted_tt_to_string(&TokenTree::MetaVarDecl( let suggestion = quoted_tt_to_string(&TokenTree::MetaVarDecl(
span, span,
name, name,
Some(NonterminalKind::PatParam { inferred: false }), Some(NonterminalKind::Pat(PatParam { inferred: false })),
)); ));
err.span_suggestion( err.span_suggestion(
span, span,
@ -1295,9 +1297,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow {
// maintain // maintain
IsInFollow::Yes IsInFollow::Yes
} }
NonterminalKind::Stmt NonterminalKind::Stmt | NonterminalKind::Expr(_) => {
| NonterminalKind::Expr
| NonterminalKind::Expr2021 { inferred: _ } => {
const TOKENS: &[&str] = &["`=>`", "`,`", "`;`"]; const TOKENS: &[&str] = &["`=>`", "`,`", "`;`"];
match tok { match tok {
TokenTree::Token(token) => match token.kind { TokenTree::Token(token) => match token.kind {
@ -1307,7 +1307,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow {
_ => IsInFollow::No(TOKENS), _ => IsInFollow::No(TOKENS),
} }
} }
NonterminalKind::PatParam { .. } => { NonterminalKind::Pat(PatParam { .. }) => {
const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`|`", "`if`", "`in`"]; const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`|`", "`if`", "`in`"];
match tok { match tok {
TokenTree::Token(token) => match token.kind { TokenTree::Token(token) => match token.kind {
@ -1320,7 +1320,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow {
_ => IsInFollow::No(TOKENS), _ => IsInFollow::No(TOKENS),
} }
} }
NonterminalKind::PatWithOr => { NonterminalKind::Pat(PatWithOr) => {
const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`if`", "`in`"]; const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`if`", "`in`"];
match tok { match tok {
TokenTree::Token(token) => match token.kind { TokenTree::Token(token) => match token.kind {

View File

@ -2,7 +2,7 @@
use crate::mbe::macro_parser::count_metavar_decls; use crate::mbe::macro_parser::count_metavar_decls;
use crate::mbe::{Delimited, KleeneOp, KleeneToken, MetaVarExpr, SequenceRepetition, TokenTree}; use crate::mbe::{Delimited, KleeneOp, KleeneToken, MetaVarExpr, SequenceRepetition, TokenTree};
use rustc_ast::token::{self, Delimiter, IdentIsRaw, NonterminalKind, Token}; use rustc_ast::token::{self, Delimiter, IdentIsRaw, NonterminalKind, NtExprKind::*, Token};
use rustc_ast::{tokenstream, NodeId}; use rustc_ast::{tokenstream, NodeId};
use rustc_ast_pretty::pprust; use rustc_ast_pretty::pprust;
use rustc_feature::Features; use rustc_feature::Features;
@ -109,7 +109,7 @@ pub(super) fn parse(
}); });
NonterminalKind::Ident NonterminalKind::Ident
}); });
if kind == (NonterminalKind::Expr2021 { inferred: false }) if kind == NonterminalKind::Expr(Expr2021 { inferred: false })
&& !features.expr_fragment_specifier_2024 && !features.expr_fragment_specifier_2024
{ {
rustc_session::parse::feature_err( rustc_session::parse::feature_err(

View File

@ -1,5 +1,7 @@
use rustc_ast::ptr::P; use rustc_ast::ptr::P;
use rustc_ast::token::{self, Delimiter, Nonterminal::*, NonterminalKind, Token}; use rustc_ast::token::{
self, Delimiter, Nonterminal::*, NonterminalKind, NtExprKind::*, NtPatKind::*, Token,
};
use rustc_ast::HasTokens; use rustc_ast::HasTokens;
use rustc_ast_pretty::pprust; use rustc_ast_pretty::pprust;
use rustc_data_structures::sync::Lrc; use rustc_data_structures::sync::Lrc;
@ -36,14 +38,14 @@ fn may_be_ident(nt: &token::Nonterminal) -> bool {
} }
match kind { match kind {
NonterminalKind::Expr2021 { inferred: _ } => { NonterminalKind::Expr(Expr2021 { .. }) => {
token.can_begin_expr() token.can_begin_expr()
// This exception is here for backwards compatibility. // This exception is here for backwards compatibility.
&& !token.is_keyword(kw::Let) && !token.is_keyword(kw::Let)
// This exception is here for backwards compatibility. // This exception is here for backwards compatibility.
&& !token.is_keyword(kw::Const) && !token.is_keyword(kw::Const)
} }
NonterminalKind::Expr => { NonterminalKind::Expr(Expr) => {
token.can_begin_expr() token.can_begin_expr()
// This exception is here for backwards compatibility. // This exception is here for backwards compatibility.
&& !token.is_keyword(kw::Let) && !token.is_keyword(kw::Let)
@ -74,7 +76,7 @@ fn may_be_ident(nt: &token::Nonterminal) -> bool {
token::Interpolated(nt) => may_be_ident(nt), token::Interpolated(nt) => may_be_ident(nt),
_ => false, _ => false,
}, },
NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr => match &token.kind { NonterminalKind::Pat(pat_kind) => match &token.kind {
// box, ref, mut, and other identifiers (can stricten) // box, ref, mut, and other identifiers (can stricten)
token::Ident(..) | token::NtIdent(..) | token::Ident(..) | token::NtIdent(..) |
token::OpenDelim(Delimiter::Parenthesis) | // tuple pattern token::OpenDelim(Delimiter::Parenthesis) | // tuple pattern
@ -89,7 +91,7 @@ fn may_be_ident(nt: &token::Nonterminal) -> bool {
token::Lt | // path (UFCS constant) token::Lt | // path (UFCS constant)
token::BinOp(token::Shl) => true, // path (double UFCS) token::BinOp(token::Shl) => true, // path (double UFCS)
// leading vert `|` or-pattern // leading vert `|` or-pattern
token::BinOp(token::Or) => matches!(kind, NonterminalKind::PatWithOr), token::BinOp(token::Or) => matches!(pat_kind, PatWithOr),
token::Interpolated(nt) => may_be_ident(nt), token::Interpolated(nt) => may_be_ident(nt),
_ => false, _ => false,
}, },
@ -135,31 +137,25 @@ pub fn parse_nonterminal(&mut self, kind: NonterminalKind) -> PResult<'a, ParseN
.create_err(UnexpectedNonterminal::Statement(self.token.span))); .create_err(UnexpectedNonterminal::Statement(self.token.span)));
} }
}, },
NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr => { NonterminalKind::Pat(pat_kind) => {
NtPat(self.collect_tokens_no_attrs(|this| match kind { NtPat(self.collect_tokens_no_attrs(|this| match pat_kind {
NonterminalKind::PatParam { .. } => this.parse_pat_no_top_alt(None, None), PatParam { .. } => this.parse_pat_no_top_alt(None, None),
NonterminalKind::PatWithOr => this.parse_pat_allow_top_alt( PatWithOr => this.parse_pat_allow_top_alt(
None, None,
RecoverComma::No, RecoverComma::No,
RecoverColon::No, RecoverColon::No,
CommaRecoveryMode::EitherTupleOrPipe, CommaRecoveryMode::EitherTupleOrPipe,
), ),
_ => unreachable!(),
})?) })?)
} }
NonterminalKind::Expr(_) => NtExpr(self.parse_expr_force_collect()?),
NonterminalKind::Expr | NonterminalKind::Expr2021 { inferred: _ } => {
NtExpr(self.parse_expr_force_collect()?)
}
NonterminalKind::Literal => { NonterminalKind::Literal => {
// The `:literal` matcher does not support attributes // The `:literal` matcher does not support attributes
NtLiteral(self.collect_tokens_no_attrs(|this| this.parse_literal_maybe_minus())?) NtLiteral(self.collect_tokens_no_attrs(|this| this.parse_literal_maybe_minus())?)
} }
NonterminalKind::Ty => { NonterminalKind::Ty => {
NtTy(self.collect_tokens_no_attrs(|this| this.parse_ty_no_question_mark_recover())?) NtTy(self.collect_tokens_no_attrs(|this| this.parse_ty_no_question_mark_recover())?)
} }
// this could be handled like a token, since it is one // this could be handled like a token, since it is one
NonterminalKind::Ident => { NonterminalKind::Ident => {
return if let Some((ident, is_raw)) = get_macro_ident(&self.token) { return if let Some((ident, is_raw)) = get_macro_ident(&self.token) {

View File

@ -1,4 +1,4 @@
use rustc_ast::token::{Delimiter, NonterminalKind, TokenKind}; use rustc_ast::token::{Delimiter, NonterminalKind, NtExprKind::*, NtPatKind::*, TokenKind};
use rustc_ast::tokenstream::TokenStream; use rustc_ast::tokenstream::TokenStream;
use rustc_ast::{ast, ptr}; use rustc_ast::{ast, ptr};
use rustc_parse::parser::{ForceCollect, Parser, Recovery}; use rustc_parse::parser::{ForceCollect, Parser, Recovery};
@ -48,7 +48,7 @@ macro_rules! parse_macro_arg {
parse_macro_arg!( parse_macro_arg!(
Expr, Expr,
NonterminalKind::Expr, NonterminalKind::Expr(Expr),
|parser: &mut Parser<'b>| parser.parse_expr(), |parser: &mut Parser<'b>| parser.parse_expr(),
|x: ptr::P<ast::Expr>| Some(x) |x: ptr::P<ast::Expr>| Some(x)
); );
@ -60,7 +60,7 @@ macro_rules! parse_macro_arg {
); );
parse_macro_arg!( parse_macro_arg!(
Pat, Pat,
NonterminalKind::PatParam { inferred: false }, NonterminalKind::Pat(PatParam { inferred: false }),
|parser: &mut Parser<'b>| parser.parse_pat_no_top_alt(None, None), |parser: &mut Parser<'b>| parser.parse_pat_no_top_alt(None, None),
|x: ptr::P<ast::Pat>| Some(x) |x: ptr::P<ast::Pat>| Some(x)
); );