Skip to content

Commit 40bf3c0

Browse files
committed
Implement edition-based macro pat feature
1 parent 2987785 commit 40bf3c0

File tree

12 files changed

+151
-76
lines changed

12 files changed

+151
-76
lines changed

compiler/rustc_ast/src/token.rs

+30-5
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use rustc_span::hygiene::ExpnKind;
1515
use rustc_span::source_map::SourceMap;
1616
use rustc_span::symbol::{kw, sym};
1717
use rustc_span::symbol::{Ident, Symbol};
18-
use rustc_span::{self, FileName, RealFileName, Span, DUMMY_SP};
18+
use rustc_span::{self, edition::Edition, FileName, RealFileName, Span, DUMMY_SP};
1919
use std::borrow::Cow;
2020
use std::{fmt, mem};
2121

@@ -690,7 +690,16 @@ pub enum NonterminalKind {
690690
Item,
691691
Block,
692692
Stmt,
693-
Pat,
693+
Pat2018 {
694+
/// Keep track of whether the user used `:pat2018` or `:pat` and we inferred it from the
695+
/// edition of the span. This is used for diagnostics.
696+
inferred: bool,
697+
},
698+
Pat2021 {
699+
/// Keep track of whether the user used `:pat2018` or `:pat` and we inferred it from the
700+
/// edition of the span. This is used for diagnostics.
701+
inferred: bool,
702+
},
694703
Expr,
695704
Ty,
696705
Ident,
@@ -703,12 +712,25 @@ pub enum NonterminalKind {
703712
}
704713

705714
impl NonterminalKind {
706-
pub fn from_symbol(symbol: Symbol) -> Option<NonterminalKind> {
715+
/// The `edition` closure is used to get the edition for the given symbol. Doing
716+
/// `span.edition()` is expensive, so we do it lazily.
717+
pub fn from_symbol(
718+
symbol: Symbol,
719+
edition: impl FnOnce() -> Edition,
720+
) -> Option<NonterminalKind> {
707721
Some(match symbol {
708722
sym::item => NonterminalKind::Item,
709723
sym::block => NonterminalKind::Block,
710724
sym::stmt => NonterminalKind::Stmt,
711-
sym::pat => NonterminalKind::Pat,
725+
sym::pat => match edition() {
726+
Edition::Edition2015 | Edition::Edition2018 => {
727+
NonterminalKind::Pat2018 { inferred: true }
728+
}
729+
// FIXME(mark-i-m): uncomment when 2021 machinery is available.
730+
//Edition::Edition2021 => NonterminalKind::Pat2021{inferred:true},
731+
},
732+
sym::pat2018 => NonterminalKind::Pat2018 { inferred: false },
733+
sym::pat2021 => NonterminalKind::Pat2021 { inferred: false },
712734
sym::expr => NonterminalKind::Expr,
713735
sym::ty => NonterminalKind::Ty,
714736
sym::ident => NonterminalKind::Ident,
@@ -726,7 +748,10 @@ impl NonterminalKind {
726748
NonterminalKind::Item => sym::item,
727749
NonterminalKind::Block => sym::block,
728750
NonterminalKind::Stmt => sym::stmt,
729-
NonterminalKind::Pat => sym::pat,
751+
NonterminalKind::Pat2018 { inferred: false } => sym::pat2018,
752+
NonterminalKind::Pat2021 { inferred: false } => sym::pat2021,
753+
NonterminalKind::Pat2018 { inferred: true }
754+
| NonterminalKind::Pat2021 { inferred: true } => sym::pat,
730755
NonterminalKind::Expr => sym::expr,
731756
NonterminalKind::Ty => sym::ty,
732757
NonterminalKind::Ident => sym::ident,

compiler/rustc_expand/src/mbe/macro_parser.rs

+5-19
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,9 @@ use TokenTreeOrTokenTreeSlice::*;
7777
use crate::mbe::{self, TokenTree};
7878

7979
use rustc_ast::token::{self, DocComment, Nonterminal, Token};
80-
use rustc_parse::parser::{OrPatNonterminalMode, Parser};
80+
use rustc_parse::parser::Parser;
8181
use rustc_session::parse::ParseSess;
82-
use rustc_span::{edition::Edition, symbol::MacroRulesNormalizedIdent};
82+
use rustc_span::symbol::MacroRulesNormalizedIdent;
8383

8484
use smallvec::{smallvec, SmallVec};
8585

@@ -419,18 +419,6 @@ fn token_name_eq(t1: &Token, t2: &Token) -> bool {
419419
}
420420
}
421421

422-
/// In edition 2015/18, `:pat` can only match `pat<no_top_alt>` because otherwise, we have
423-
/// breakage. As of edition 2021, `:pat` matches `top_pat`.
424-
///
425-
/// See <https://github.com/rust-lang/rust/issues/54883> for more info.
426-
fn or_pat_mode(edition: Edition) -> OrPatNonterminalMode {
427-
match edition {
428-
Edition::Edition2015 | Edition::Edition2018 => OrPatNonterminalMode::NoTopAlt,
429-
// FIXME(mark-i-m): uncomment this when edition 2021 machinery is added.
430-
// Edition::Edition2021 => OrPatNonterminalMode::TopPat,
431-
}
432-
}
433-
434422
/// Process the matcher positions of `cur_items` until it is empty. In the process, this will
435423
/// produce more items in `next_items`, `eof_items`, and `bb_items`.
436424
///
@@ -578,14 +566,13 @@ fn inner_parse_loop<'root, 'tt>(
578566

579567
// We need to match a metavar with a valid ident... call out to the black-box
580568
// parser by adding an item to `bb_items`.
581-
TokenTree::MetaVarDecl(span, _, Some(kind)) => {
569+
TokenTree::MetaVarDecl(_, _, Some(kind)) => {
582570
// Built-in nonterminals never start with these tokens, so we can eliminate
583571
// them from consideration.
584572
//
585573
// We use the span of the metavariable declaration to determine any
586574
// edition-specific matching behavior for non-terminals.
587-
if Parser::nonterminal_may_begin_with(kind, token, or_pat_mode(span.edition()))
588-
{
575+
if Parser::nonterminal_may_begin_with(kind, token) {
589576
bb_items.push(item);
590577
}
591578
}
@@ -749,8 +736,7 @@ pub(super) fn parse_tt(parser: &mut Cow<'_, Parser<'_>>, ms: &[TokenTree]) -> Na
749736
let match_cur = item.match_cur;
750737
// We use the span of the metavariable declaration to determine any
751738
// edition-specific matching behavior for non-terminals.
752-
let nt = match parser.to_mut().parse_nonterminal(kind, or_pat_mode(span.edition()))
753-
{
739+
let nt = match parser.to_mut().parse_nonterminal(kind) {
754740
Err(mut err) => {
755741
err.span_label(
756742
span,

compiler/rustc_expand/src/mbe/macro_rules.rs

+11-5
Original file line numberDiff line numberDiff line change
@@ -476,10 +476,15 @@ pub fn compile_declarative_macro(
476476
.map(|m| {
477477
if let MatchedNonterminal(ref nt) = *m {
478478
if let NtTT(ref tt) = **nt {
479-
let tt =
480-
mbe::quoted::parse(tt.clone().into(), true, &sess.parse_sess, def.id)
481-
.pop()
482-
.unwrap();
479+
let tt = mbe::quoted::parse(
480+
tt.clone().into(),
481+
true,
482+
&sess.parse_sess,
483+
def.id,
484+
features,
485+
)
486+
.pop()
487+
.unwrap();
483488
valid &= check_lhs_nt_follows(&sess.parse_sess, features, &def.attrs, &tt);
484489
return tt;
485490
}
@@ -501,6 +506,7 @@ pub fn compile_declarative_macro(
501506
false,
502507
&sess.parse_sess,
503508
def.id,
509+
features,
504510
)
505511
.pop()
506512
.unwrap();
@@ -1090,7 +1096,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow {
10901096
_ => IsInFollow::No(TOKENS),
10911097
}
10921098
}
1093-
NonterminalKind::Pat => {
1099+
NonterminalKind::Pat2018 { .. } | NonterminalKind::Pat2021 { .. } => {
10941100
const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`|`", "`if`", "`in`"];
10951101
match tok {
10961102
TokenTree::Token(token) => match token.kind {

compiler/rustc_expand/src/mbe/quoted.rs

+46-22
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ use rustc_ast::token::{self, Token};
55
use rustc_ast::tokenstream;
66
use rustc_ast::{NodeId, DUMMY_NODE_ID};
77
use rustc_ast_pretty::pprust;
8-
use rustc_session::parse::ParseSess;
9-
use rustc_span::symbol::{kw, Ident};
8+
use rustc_feature::Features;
9+
use rustc_session::parse::{feature_err, ParseSess};
10+
use rustc_span::symbol::{kw, sym, Ident};
1011

1112
use rustc_span::Span;
1213

@@ -29,10 +30,8 @@ const VALID_FRAGMENT_NAMES_MSG: &str = "valid fragment specifiers are \
2930
/// `ident` are "matchers". They are not present in the body of a macro rule -- just in the
3031
/// pattern, so we pass a parameter to indicate whether to expect them or not.
3132
/// - `sess`: the parsing session. Any errors will be emitted to this session.
32-
/// - `features`, `attrs`: language feature flags and attributes so that we know whether to use
33-
/// unstable features or not.
34-
/// - `edition`: which edition are we in.
35-
/// - `macro_node_id`: the NodeId of the macro we are parsing.
33+
/// - `node_id`: the NodeId of the macro we are parsing.
34+
/// - `features`: language features so we can do feature gating.
3635
///
3736
/// # Returns
3837
///
@@ -42,6 +41,7 @@ pub(super) fn parse(
4241
expect_matchers: bool,
4342
sess: &ParseSess,
4443
node_id: NodeId,
44+
features: &Features,
4545
) -> Vec<TokenTree> {
4646
// Will contain the final collection of `self::TokenTree`
4747
let mut result = Vec::new();
@@ -52,7 +52,7 @@ pub(super) fn parse(
5252
while let Some(tree) = trees.next() {
5353
// Given the parsed tree, if there is a metavar and we are expecting matchers, actually
5454
// parse out the matcher (i.e., in `$id:ident` this would parse the `:` and `ident`).
55-
let tree = parse_tree(tree, &mut trees, expect_matchers, sess, node_id);
55+
let tree = parse_tree(tree, &mut trees, expect_matchers, sess, node_id, features);
5656
match tree {
5757
TokenTree::MetaVar(start_sp, ident) if expect_matchers => {
5858
let span = match trees.next() {
@@ -61,18 +61,39 @@ pub(super) fn parse(
6161
Some(tokenstream::TokenTree::Token(token)) => match token.ident() {
6262
Some((frag, _)) => {
6363
let span = token.span.with_lo(start_sp.lo());
64-
let kind = token::NonterminalKind::from_symbol(frag.name)
65-
.unwrap_or_else(|| {
66-
let msg = format!(
67-
"invalid fragment specifier `{}`",
68-
frag.name
69-
);
70-
sess.span_diagnostic
71-
.struct_span_err(span, &msg)
72-
.help(VALID_FRAGMENT_NAMES_MSG)
64+
65+
match frag.name {
66+
sym::pat2018 | sym::pat2021 => {
67+
if !features.edition_macro_pats {
68+
feature_err(
69+
sess,
70+
sym::edition_macro_pats,
71+
frag.span,
72+
"`pat2018` and `pat2021` are unstable.",
73+
)
7374
.emit();
74-
token::NonterminalKind::Ident
75-
});
75+
}
76+
}
77+
_ => {}
78+
}
79+
80+
let kind =
81+
token::NonterminalKind::from_symbol(frag.name, || {
82+
span.edition()
83+
})
84+
.unwrap_or_else(
85+
|| {
86+
let msg = format!(
87+
"invalid fragment specifier `{}`",
88+
frag.name
89+
);
90+
sess.span_diagnostic
91+
.struct_span_err(span, &msg)
92+
.help(VALID_FRAGMENT_NAMES_MSG)
93+
.emit();
94+
token::NonterminalKind::Ident
95+
},
96+
);
7697
result.push(TokenTree::MetaVarDecl(span, ident, Some(kind)));
7798
continue;
7899
}
@@ -110,14 +131,14 @@ pub(super) fn parse(
110131
/// converting `tree`
111132
/// - `expect_matchers`: same as for `parse` (see above).
112133
/// - `sess`: the parsing session. Any errors will be emitted to this session.
113-
/// - `features`, `attrs`: language feature flags and attributes so that we know whether to use
114-
/// unstable features or not.
134+
/// - `features`: language features so we can do feature gating.
115135
fn parse_tree(
116136
tree: tokenstream::TokenTree,
117137
outer_trees: &mut impl Iterator<Item = tokenstream::TokenTree>,
118138
expect_matchers: bool,
119139
sess: &ParseSess,
120140
node_id: NodeId,
141+
features: &Features,
121142
) -> TokenTree {
122143
// Depending on what `tree` is, we could be parsing different parts of a macro
123144
match tree {
@@ -145,7 +166,7 @@ fn parse_tree(
145166
sess.span_diagnostic.span_err(span.entire(), &msg);
146167
}
147168
// Parse the contents of the sequence itself
148-
let sequence = parse(tts, expect_matchers, sess, node_id);
169+
let sequence = parse(tts, expect_matchers, sess, node_id, features);
149170
// Get the Kleene operator and optional separator
150171
let (separator, kleene) =
151172
parse_sep_and_kleene_op(&mut trees, span.entire(), sess);
@@ -196,7 +217,10 @@ fn parse_tree(
196217
// descend into the delimited set and further parse it.
197218
tokenstream::TokenTree::Delimited(span, delim, tts) => TokenTree::Delimited(
198219
span,
199-
Lrc::new(Delimited { delim, tts: parse(tts, expect_matchers, sess, node_id) }),
220+
Lrc::new(Delimited {
221+
delim,
222+
tts: parse(tts, expect_matchers, sess, node_id, features),
223+
}),
200224
),
201225
}
202226
}

compiler/rustc_feature/src/active.rs

+3
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,9 @@ declare_features! (
620620
/// Allows arbitrary expressions in key-value attributes at parse time.
621621
(active, extended_key_value_attributes, "1.50.0", Some(78835), None),
622622

623+
/// `:pat2018` and `:pat2021` macro matchers.
624+
(active, edition_macro_pats, "1.51.0", Some(54883), None),
625+
623626
// -------------------------------------------------------------------------
624627
// feature-group-end: actual feature gates
625628
// -------------------------------------------------------------------------

compiler/rustc_parse/src/parser/mod.rs

-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ mod ty;
1212
use crate::lexer::UnmatchedBrace;
1313
pub use diagnostics::AttemptLocalParseRecovery;
1414
use diagnostics::Error;
15-
pub use pat::OrPatNonterminalMode;
1615
pub use path::PathStyle;
1716

1817
use rustc_ast::ptr::P;

compiler/rustc_parse/src/parser/nonterminal.rs

+10-17
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,15 @@ use rustc_ast_pretty::pprust;
44
use rustc_errors::PResult;
55
use rustc_span::symbol::{kw, Ident};
66

7-
use crate::parser::pat::{GateOr, OrPatNonterminalMode, RecoverComma};
7+
use crate::parser::pat::{GateOr, RecoverComma};
88
use crate::parser::{FollowedByType, Parser, PathStyle};
99

1010
impl<'a> Parser<'a> {
1111
/// Checks whether a non-terminal may begin with a particular token.
1212
///
1313
/// Returning `false` is a *stability guarantee* that such a matcher will *never* begin with that
1414
/// token. Be conservative (return true) if not sure.
15-
pub fn nonterminal_may_begin_with(
16-
kind: NonterminalKind,
17-
token: &Token,
18-
or_pat_mode: OrPatNonterminalMode,
19-
) -> bool {
15+
pub fn nonterminal_may_begin_with(kind: NonterminalKind, token: &Token) -> bool {
2016
/// Checks whether the non-terminal may contain a single (non-keyword) identifier.
2117
fn may_be_ident(nt: &token::Nonterminal) -> bool {
2218
match *nt {
@@ -62,7 +58,7 @@ impl<'a> Parser<'a> {
6258
},
6359
_ => false,
6460
},
65-
NonterminalKind::Pat => match token.kind {
61+
NonterminalKind::Pat2018 { .. } | NonterminalKind::Pat2021 { .. } => match token.kind {
6662
token::Ident(..) | // box, ref, mut, and other identifiers (can stricten)
6763
token::OpenDelim(token::Paren) | // tuple pattern
6864
token::OpenDelim(token::Bracket) | // slice pattern
@@ -76,7 +72,7 @@ impl<'a> Parser<'a> {
7672
token::Lt | // path (UFCS constant)
7773
token::BinOp(token::Shl) => true, // path (double UFCS)
7874
// leading vert `|` or-pattern
79-
token::BinOp(token::Or) => matches!(or_pat_mode, OrPatNonterminalMode::TopPat),
75+
token::BinOp(token::Or) => matches!(kind, NonterminalKind::Pat2021 {..}),
8076
token::Interpolated(ref nt) => may_be_ident(nt),
8177
_ => false,
8278
},
@@ -94,11 +90,7 @@ impl<'a> Parser<'a> {
9490
}
9591

9692
/// Parse a non-terminal (e.g. MBE `:pat` or `:ident`).
97-
pub fn parse_nonterminal(
98-
&mut self,
99-
kind: NonterminalKind,
100-
or_pat_mode: OrPatNonterminalMode,
101-
) -> PResult<'a, Nonterminal> {
93+
pub fn parse_nonterminal(&mut self, kind: NonterminalKind) -> PResult<'a, Nonterminal> {
10294
// Any `Nonterminal` which stores its tokens (currently `NtItem` and `NtExpr`)
10395
// needs to have them force-captured here.
10496
// A `macro_rules!` invocation may pass a captured item/expr to a proc-macro,
@@ -141,12 +133,13 @@ impl<'a> Parser<'a> {
141133
}
142134
}
143135
}
144-
NonterminalKind::Pat => {
145-
let (mut pat, tokens) = self.collect_tokens(|this| match or_pat_mode {
146-
OrPatNonterminalMode::TopPat => {
136+
NonterminalKind::Pat2018 { .. } | NonterminalKind::Pat2021 { .. } => {
137+
let (mut pat, tokens) = self.collect_tokens(|this| match kind {
138+
NonterminalKind::Pat2018 { .. } => this.parse_pat(None),
139+
NonterminalKind::Pat2021 { .. } => {
147140
this.parse_top_pat(GateOr::Yes, RecoverComma::No)
148141
}
149-
OrPatNonterminalMode::NoTopAlt => this.parse_pat(None),
142+
_ => unreachable!(),
150143
})?;
151144
// We have have eaten an NtPat, which could already have tokens
152145
if pat.tokens.is_none() {

compiler/rustc_parse/src/parser/pat.rs

-7
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,6 @@ pub(super) enum RecoverComma {
3131
No,
3232
}
3333

34-
/// Used when parsing a non-terminal (see `parse_nonterminal`) to determine if `:pat` should match
35-
/// `top_pat` or `pat<no_top_alt>`. See issue <https://github.com/rust-lang/rust/pull/78935>.
36-
pub enum OrPatNonterminalMode {
37-
TopPat,
38-
NoTopAlt,
39-
}
40-
4134
impl<'a> Parser<'a> {
4235
/// Parses a pattern.
4336
///

0 commit comments

Comments
 (0)