Rollup merge of #142657 - tgross35:nonoptional-fragment-specifiers-cleanup, r=petrochenkov

mbe: Clean up code with non-optional `NonterminalKind`

Since [rust-lang/rust#128425], the fragment specifier is unconditionally required in all
editions. This means `NonTerminalKind` no longer needs to be optional,
as we can reject this code during the expansion of `macro_rules!` rather
than handling it throughout the code. Do this cleanup here.

[rust-lang/rust#128425]: https://github.com/rust-lang/rust/pull/128425
This commit is contained in:
Matthias Krüger
2025-06-24 20:46:03 +02:00
committed by GitHub
17 changed files with 215 additions and 163 deletions

View File

@@ -1085,6 +1085,7 @@ pub enum NtExprKind {
Expr2021 { inferred: bool }, Expr2021 { inferred: bool },
} }
/// A macro nonterminal, known in documentation as a fragment specifier.
#[derive(Debug, Copy, Clone, PartialEq, Eq, Encodable, Decodable, Hash, HashStable_Generic)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Encodable, Decodable, Hash, HashStable_Generic)]
pub enum NonterminalKind { pub enum NonterminalKind {
Item, Item,

View File

@@ -444,7 +444,7 @@ pub(crate) struct InvalidFragmentSpecifier {
#[primary_span] #[primary_span]
pub span: Span, pub span: Span,
pub fragment: Ident, pub fragment: Ident,
pub help: String, pub help: &'static str,
} }
#[derive(Diagnostic)] #[derive(Diagnostic)]

View File

@@ -78,7 +78,13 @@ enum TokenTree {
/// only covers the ident, e.g. `var`.) /// only covers the ident, e.g. `var`.)
MetaVar(Span, Ident), MetaVar(Span, Ident),
/// e.g., `$var:expr`. Only appears on the LHS. /// e.g., `$var:expr`. Only appears on the LHS.
MetaVarDecl(Span, Ident /* name to bind */, Option<NonterminalKind>), MetaVarDecl {
span: Span,
/// Name to bind.
name: Ident,
/// The fragment specifier.
kind: NonterminalKind,
},
/// A meta-variable expression inside `${...}`. /// A meta-variable expression inside `${...}`.
MetaVarExpr(DelimSpan, MetaVarExpr), MetaVarExpr(DelimSpan, MetaVarExpr),
} }
@@ -102,7 +108,7 @@ impl TokenTree {
match *self { match *self {
TokenTree::Token(Token { span, .. }) TokenTree::Token(Token { span, .. })
| TokenTree::MetaVar(span, _) | TokenTree::MetaVar(span, _)
| TokenTree::MetaVarDecl(span, _, _) => span, | TokenTree::MetaVarDecl { span, .. } => span,
TokenTree::Delimited(span, ..) TokenTree::Delimited(span, ..)
| TokenTree::MetaVarExpr(span, _) | TokenTree::MetaVarExpr(span, _)
| TokenTree::Sequence(span, _) => span.entire(), | TokenTree::Sequence(span, _) => span.entire(),

View File

@@ -24,6 +24,7 @@ pub(super) fn failed_to_match_macro(
arg: TokenStream, arg: TokenStream,
lhses: &[Vec<MatcherLoc>], lhses: &[Vec<MatcherLoc>],
) -> (Span, ErrorGuaranteed) { ) -> (Span, ErrorGuaranteed) {
debug!("failed to match macro");
// An error occurred, try the expansion again, tracking the expansion closely for better // An error occurred, try the expansion again, tracking the expansion closely for better
// diagnostics. // diagnostics.
let mut tracker = CollectTrackerAndEmitter::new(psess.dcx(), sp); let mut tracker = CollectTrackerAndEmitter::new(psess.dcx(), sp);

View File

@@ -117,7 +117,6 @@ use rustc_session::parse::ParseSess;
use rustc_span::{ErrorGuaranteed, MacroRulesNormalizedIdent, Span, kw}; use rustc_span::{ErrorGuaranteed, MacroRulesNormalizedIdent, Span, kw};
use smallvec::SmallVec; use smallvec::SmallVec;
use super::quoted::VALID_FRAGMENT_NAMES_MSG;
use crate::errors; use crate::errors;
use crate::mbe::{KleeneToken, TokenTree}; use crate::mbe::{KleeneToken, TokenTree};
@@ -263,14 +262,7 @@ fn check_binders(
} }
} }
// Similarly, this can only happen when checking a toplevel macro. // Similarly, this can only happen when checking a toplevel macro.
TokenTree::MetaVarDecl(span, name, kind) => { TokenTree::MetaVarDecl { span, name, .. } => {
if kind.is_none() && node_id != DUMMY_NODE_ID {
psess.dcx().emit_err(errors::MissingFragmentSpecifier {
span,
add_span: span.shrink_to_hi(),
valid: VALID_FRAGMENT_NAMES_MSG,
});
}
if !macros.is_empty() { if !macros.is_empty() {
psess.dcx().span_bug(span, "unexpected MetaVarDecl in nested lhs"); psess.dcx().span_bug(span, "unexpected MetaVarDecl in nested lhs");
} }
@@ -339,7 +331,7 @@ fn check_occurrences(
) { ) {
match *rhs { match *rhs {
TokenTree::Token(..) => {} TokenTree::Token(..) => {}
TokenTree::MetaVarDecl(span, _name, _kind) => { TokenTree::MetaVarDecl { span, .. } => {
psess.dcx().span_bug(span, "unexpected MetaVarDecl in rhs") psess.dcx().span_bug(span, "unexpected MetaVarDecl in rhs")
} }
TokenTree::MetaVar(span, name) => { TokenTree::MetaVar(span, name) => {

View File

@@ -122,7 +122,7 @@ pub(crate) enum MatcherLoc {
MetaVarDecl { MetaVarDecl {
span: Span, span: Span,
bind: Ident, bind: Ident,
kind: Option<NonterminalKind>, kind: NonterminalKind,
next_metavar: usize, next_metavar: usize,
seq_depth: usize, seq_depth: usize,
}, },
@@ -151,12 +151,7 @@ impl Display for MatcherLoc {
write!(f, "{}", token_descr(token)) write!(f, "{}", token_descr(token))
} }
MatcherLoc::MetaVarDecl { bind, kind, .. } => { MatcherLoc::MetaVarDecl { bind, kind, .. } => {
write!(f, "meta-variable `${bind}")?; write!(f, "meta-variable `${bind}:{kind}`")
if let Some(kind) = kind {
write!(f, ":{kind}")?;
}
write!(f, "`")?;
Ok(())
} }
MatcherLoc::Eof => f.write_str("end of macro"), MatcherLoc::Eof => f.write_str("end of macro"),
@@ -220,7 +215,7 @@ pub(super) fn compute_locs(matcher: &[TokenTree]) -> Vec<MatcherLoc> {
seq_depth, seq_depth,
}; };
} }
&TokenTree::MetaVarDecl(span, bind, kind) => { &TokenTree::MetaVarDecl { span, name: bind, kind } => {
locs.push(MatcherLoc::MetaVarDecl { locs.push(MatcherLoc::MetaVarDecl {
span, span,
bind, bind,
@@ -330,7 +325,7 @@ pub(super) fn count_metavar_decls(matcher: &[TokenTree]) -> usize {
matcher matcher
.iter() .iter()
.map(|tt| match tt { .map(|tt| match tt {
TokenTree::MetaVarDecl(..) => 1, TokenTree::MetaVarDecl { .. } => 1,
TokenTree::Sequence(_, seq) => seq.num_captures, TokenTree::Sequence(_, seq) => seq.num_captures,
TokenTree::Delimited(.., delim) => count_metavar_decls(&delim.tts), TokenTree::Delimited(.., delim) => count_metavar_decls(&delim.tts),
TokenTree::Token(..) => 0, TokenTree::Token(..) => 0,
@@ -551,19 +546,13 @@ impl TtParser {
mp.idx = idx_first; mp.idx = idx_first;
self.cur_mps.push(mp); self.cur_mps.push(mp);
} }
&MatcherLoc::MetaVarDecl { span, kind, .. } => { &MatcherLoc::MetaVarDecl { kind, .. } => {
// Built-in nonterminals never start with these tokens, so we can eliminate // Built-in nonterminals never start with these tokens, so we can eliminate
// them from consideration. We use the span of the metavariable declaration // them from consideration. We use the span of the metavariable declaration
// to determine any edition-specific matching behavior for non-terminals. // to determine any edition-specific matching behavior for non-terminals.
if let Some(kind) = kind {
if Parser::nonterminal_may_begin_with(kind, token) { if Parser::nonterminal_may_begin_with(kind, token) {
self.bb_mps.push(mp); self.bb_mps.push(mp);
} }
} else {
// E.g. `$e` instead of `$e:expr`, reported as a hard error if actually used.
// Both this check and the one in `nameize` are necessary, surprisingly.
return Some(Error(span, "missing fragment specifier".to_string()));
}
} }
MatcherLoc::Eof => { MatcherLoc::Eof => {
// We are past the matcher's end, and not in a sequence. Try to end things. // We are past the matcher's end, and not in a sequence. Try to end things.
@@ -666,11 +655,7 @@ impl TtParser {
let mut mp = self.bb_mps.pop().unwrap(); let mut mp = self.bb_mps.pop().unwrap();
let loc = &matcher[mp.idx]; let loc = &matcher[mp.idx];
if let &MatcherLoc::MetaVarDecl { if let &MatcherLoc::MetaVarDecl {
span, span, kind, next_metavar, seq_depth, ..
kind: Some(kind),
next_metavar,
seq_depth,
..
} = loc } = loc
{ {
// We use the span of the metavariable declaration to determine any // We use the span of the metavariable declaration to determine any
@@ -715,7 +700,7 @@ impl TtParser {
.bb_mps .bb_mps
.iter() .iter()
.map(|mp| match &matcher[mp.idx] { .map(|mp| match &matcher[mp.idx] {
MatcherLoc::MetaVarDecl { bind, kind: Some(kind), .. } => { MatcherLoc::MetaVarDecl { bind, kind, .. } => {
format!("{kind} ('{bind}')") format!("{kind} ('{bind}')")
} }
_ => unreachable!(), _ => unreachable!(),
@@ -745,19 +730,13 @@ impl TtParser {
// `NamedParseResult`. Otherwise, it's an error. // `NamedParseResult`. Otherwise, it's an error.
let mut ret_val = FxHashMap::default(); let mut ret_val = FxHashMap::default();
for loc in matcher { for loc in matcher {
if let &MatcherLoc::MetaVarDecl { span, bind, kind, .. } = loc { if let &MatcherLoc::MetaVarDecl { span, bind, .. } = loc {
if kind.is_some() {
match ret_val.entry(MacroRulesNormalizedIdent::new(bind)) { match ret_val.entry(MacroRulesNormalizedIdent::new(bind)) {
Vacant(spot) => spot.insert(res.next().unwrap()), Vacant(spot) => spot.insert(res.next().unwrap()),
Occupied(..) => { Occupied(..) => {
return Error(span, format!("duplicated bind name: {bind}")); return Error(span, format!("duplicated bind name: {bind}"));
} }
}; };
} else {
// E.g. `$e` instead of `$e:expr`, reported as a hard error if actually used.
// Both this check and the one in `parse_tt_inner` are necessary, surprisingly.
return Error(span, "missing fragment specifier".to_string());
}
} }
} }
Success(ret_val) Success(ret_val)

View File

@@ -392,7 +392,7 @@ pub fn compile_declarative_macro(
let lhs_nm = Ident::new(sym::lhs, span); let lhs_nm = Ident::new(sym::lhs, span);
let rhs_nm = Ident::new(sym::rhs, span); let rhs_nm = Ident::new(sym::rhs, span);
let tt_spec = Some(NonterminalKind::TT); let tt_spec = NonterminalKind::TT;
let macro_rules = macro_def.macro_rules; let macro_rules = macro_def.macro_rules;
// Parse the macro_rules! invocation // Parse the macro_rules! invocation
@@ -407,9 +407,9 @@ pub fn compile_declarative_macro(
DelimSpan::dummy(), DelimSpan::dummy(),
mbe::SequenceRepetition { mbe::SequenceRepetition {
tts: vec![ tts: vec![
mbe::TokenTree::MetaVarDecl(span, lhs_nm, tt_spec), mbe::TokenTree::MetaVarDecl { span, name: lhs_nm, kind: tt_spec },
mbe::TokenTree::token(token::FatArrow, span), mbe::TokenTree::token(token::FatArrow, span),
mbe::TokenTree::MetaVarDecl(span, rhs_nm, tt_spec), mbe::TokenTree::MetaVarDecl { span, name: rhs_nm, kind: tt_spec },
], ],
separator: Some(Token::new( separator: Some(Token::new(
if macro_rules { token::Semi } else { token::Comma }, if macro_rules { token::Semi } else { token::Comma },
@@ -448,6 +448,7 @@ pub fn compile_declarative_macro(
match tt_parser.parse_tt(&mut Cow::Owned(parser), &argument_gram, &mut NoopTracker) { match tt_parser.parse_tt(&mut Cow::Owned(parser), &argument_gram, &mut NoopTracker) {
Success(m) => m, Success(m) => m,
Failure(()) => { Failure(()) => {
debug!("failed to parse macro tt");
// The fast `NoopTracker` doesn't have any info on failure, so we need to retry it // The fast `NoopTracker` doesn't have any info on failure, so we need to retry it
// with another one that gives us the information we need. // with another one that gives us the information we need.
// For this we need to reclone the macro body as the previous parser consumed it. // For this we need to reclone the macro body as the previous parser consumed it.
@@ -616,7 +617,7 @@ fn is_empty_token_tree(sess: &Session, seq: &mbe::SequenceRepetition) -> bool {
let mut iter = seq.tts.iter().peekable(); let mut iter = seq.tts.iter().peekable();
while let Some(tt) = iter.next() { while let Some(tt) = iter.next() {
match tt { match tt {
mbe::TokenTree::MetaVarDecl(_, _, Some(NonterminalKind::Vis)) => {} mbe::TokenTree::MetaVarDecl { kind: NonterminalKind::Vis, .. } => {}
mbe::TokenTree::Token(t @ Token { kind: DocComment(..), .. }) => { mbe::TokenTree::Token(t @ Token { kind: DocComment(..), .. }) => {
let mut now = t; let mut now = t;
while let Some(&mbe::TokenTree::Token( while let Some(&mbe::TokenTree::Token(
@@ -651,7 +652,7 @@ fn check_redundant_vis_repetition(
) { ) {
let is_zero_or_one: bool = seq.kleene.op == KleeneOp::ZeroOrOne; let is_zero_or_one: bool = seq.kleene.op == KleeneOp::ZeroOrOne;
let is_vis = seq.tts.first().map_or(false, |tt| { let is_vis = seq.tts.first().map_or(false, |tt| {
matches!(tt, mbe::TokenTree::MetaVarDecl(_, _, Some(NonterminalKind::Vis))) matches!(tt, mbe::TokenTree::MetaVarDecl { kind: NonterminalKind::Vis, .. })
}); });
if is_vis && is_zero_or_one { if is_vis && is_zero_or_one {
@@ -678,7 +679,7 @@ fn check_lhs_no_empty_seq(sess: &Session, tts: &[mbe::TokenTree]) -> Result<(),
match tt { match tt {
TokenTree::Token(..) TokenTree::Token(..)
| TokenTree::MetaVar(..) | TokenTree::MetaVar(..)
| TokenTree::MetaVarDecl(..) | TokenTree::MetaVarDecl { .. }
| TokenTree::MetaVarExpr(..) => (), | TokenTree::MetaVarExpr(..) => (),
TokenTree::Delimited(.., del) => check_lhs_no_empty_seq(sess, &del.tts)?, TokenTree::Delimited(.., del) => check_lhs_no_empty_seq(sess, &del.tts)?,
TokenTree::Sequence(span, seq) => { TokenTree::Sequence(span, seq) => {
@@ -777,7 +778,7 @@ impl<'tt> FirstSets<'tt> {
match tt { match tt {
TokenTree::Token(..) TokenTree::Token(..)
| TokenTree::MetaVar(..) | TokenTree::MetaVar(..)
| TokenTree::MetaVarDecl(..) | TokenTree::MetaVarDecl { .. }
| TokenTree::MetaVarExpr(..) => { | TokenTree::MetaVarExpr(..) => {
first.replace_with(TtHandle::TtRef(tt)); first.replace_with(TtHandle::TtRef(tt));
} }
@@ -845,7 +846,7 @@ impl<'tt> FirstSets<'tt> {
match tt { match tt {
TokenTree::Token(..) TokenTree::Token(..)
| TokenTree::MetaVar(..) | TokenTree::MetaVar(..)
| TokenTree::MetaVarDecl(..) | TokenTree::MetaVarDecl { .. }
| TokenTree::MetaVarExpr(..) => { | TokenTree::MetaVarExpr(..) => {
first.add_one(TtHandle::TtRef(tt)); first.add_one(TtHandle::TtRef(tt));
return first; return first;
@@ -1084,7 +1085,7 @@ fn check_matcher_core<'tt>(
match token { match token {
TokenTree::Token(..) TokenTree::Token(..)
| TokenTree::MetaVar(..) | TokenTree::MetaVar(..)
| TokenTree::MetaVarDecl(..) | TokenTree::MetaVarDecl { .. }
| TokenTree::MetaVarExpr(..) => { | TokenTree::MetaVarExpr(..) => {
if token_can_be_followed_by_any(token) { if token_can_be_followed_by_any(token) {
// don't need to track tokens that work with any, // don't need to track tokens that work with any,
@@ -1152,7 +1153,7 @@ fn check_matcher_core<'tt>(
// Now `last` holds the complete set of NT tokens that could // Now `last` holds the complete set of NT tokens that could
// end the sequence before SUFFIX. Check that every one works with `suffix`. // end the sequence before SUFFIX. Check that every one works with `suffix`.
for tt in &last.tokens { for tt in &last.tokens {
if let &TokenTree::MetaVarDecl(span, name, Some(kind)) = tt.get() { if let &TokenTree::MetaVarDecl { span, name, kind } = tt.get() {
for next_token in &suffix_first.tokens { for next_token in &suffix_first.tokens {
let next_token = next_token.get(); let next_token = next_token.get();
@@ -1172,11 +1173,11 @@ fn check_matcher_core<'tt>(
) )
{ {
// It is suggestion to use pat_param, for example: $x:pat -> $x:pat_param. // It is suggestion to use pat_param, for example: $x:pat -> $x:pat_param.
let suggestion = quoted_tt_to_string(&TokenTree::MetaVarDecl( let suggestion = quoted_tt_to_string(&TokenTree::MetaVarDecl {
span, span,
name, name,
Some(NonterminalKind::Pat(PatParam { inferred: false })), kind: NonterminalKind::Pat(PatParam { inferred: false }),
)); });
sess.psess.buffer_lint( sess.psess.buffer_lint(
RUST_2021_INCOMPATIBLE_OR_PATTERNS, RUST_2021_INCOMPATIBLE_OR_PATTERNS,
span, span,
@@ -1212,11 +1213,11 @@ fn check_matcher_core<'tt>(
&& sess.psess.edition.at_least_rust_2021() && sess.psess.edition.at_least_rust_2021()
&& next_token.is_token(&token::Or) && next_token.is_token(&token::Or)
{ {
let suggestion = quoted_tt_to_string(&TokenTree::MetaVarDecl( let suggestion = quoted_tt_to_string(&TokenTree::MetaVarDecl {
span, span,
name, name,
Some(NonterminalKind::Pat(PatParam { inferred: false })), kind: NonterminalKind::Pat(PatParam { inferred: false }),
)); });
err.span_suggestion( err.span_suggestion(
span, span,
"try a `pat_param` fragment specifier instead", "try a `pat_param` fragment specifier instead",
@@ -1254,7 +1255,7 @@ fn check_matcher_core<'tt>(
} }
fn token_can_be_followed_by_any(tok: &mbe::TokenTree) -> bool { fn token_can_be_followed_by_any(tok: &mbe::TokenTree) -> bool {
if let mbe::TokenTree::MetaVarDecl(_, _, Some(kind)) = *tok { if let mbe::TokenTree::MetaVarDecl { kind, .. } = *tok {
frag_can_be_followed_by_any(kind) frag_can_be_followed_by_any(kind)
} else { } else {
// (Non NT's can always be followed by anything in matchers.) // (Non NT's can always be followed by anything in matchers.)
@@ -1367,7 +1368,7 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow {
} }
_ => IsInFollow::No(TOKENS), _ => IsInFollow::No(TOKENS),
}, },
TokenTree::MetaVarDecl(_, _, Some(NonterminalKind::Block)) => IsInFollow::Yes, TokenTree::MetaVarDecl { kind: NonterminalKind::Block, .. } => IsInFollow::Yes,
_ => IsInFollow::No(TOKENS), _ => IsInFollow::No(TOKENS),
} }
} }
@@ -1400,11 +1401,10 @@ fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow {
} }
} }
}, },
TokenTree::MetaVarDecl( TokenTree::MetaVarDecl {
_, kind: NonterminalKind::Ident | NonterminalKind::Ty | NonterminalKind::Path,
_, ..
Some(NonterminalKind::Ident | NonterminalKind::Ty | NonterminalKind::Path), } => IsInFollow::Yes,
) => IsInFollow::Yes,
_ => IsInFollow::No(TOKENS), _ => IsInFollow::No(TOKENS),
} }
} }
@@ -1416,8 +1416,7 @@ fn quoted_tt_to_string(tt: &mbe::TokenTree) -> String {
match tt { match tt {
mbe::TokenTree::Token(token) => pprust::token_to_string(token).into(), mbe::TokenTree::Token(token) => pprust::token_to_string(token).into(),
mbe::TokenTree::MetaVar(_, name) => format!("${name}"), mbe::TokenTree::MetaVar(_, name) => format!("${name}"),
mbe::TokenTree::MetaVarDecl(_, name, Some(kind)) => format!("${name}:{kind}"), mbe::TokenTree::MetaVarDecl { name, kind, .. } => format!("${name}:{kind}"),
mbe::TokenTree::MetaVarDecl(_, name, None) => format!("${name}:"),
_ => panic!( _ => panic!(
"{}", "{}",
"unexpected mbe::TokenTree::{Sequence or Delimited} \ "unexpected mbe::TokenTree::{Sequence or Delimited} \

View File

@@ -54,22 +54,53 @@ pub(super) fn parse(
// Given the parsed tree, if there is a metavar and we are expecting matchers, actually // Given the parsed tree, if there is a metavar and we are expecting matchers, actually
// parse out the matcher (i.e., in `$id:ident` this would parse the `:` and `ident`). // parse out the matcher (i.e., in `$id:ident` this would parse the `:` and `ident`).
let tree = parse_tree(tree, &mut iter, parsing_patterns, sess, node_id, features, edition); let tree = parse_tree(tree, &mut iter, parsing_patterns, sess, node_id, features, edition);
match tree {
TokenTree::MetaVar(start_sp, ident) if parsing_patterns => { if !parsing_patterns {
// No matchers allowed, nothing to process here
result.push(tree);
continue;
}
let TokenTree::MetaVar(start_sp, ident) = tree else {
// Not a metavariable, just return the tree
result.push(tree);
continue;
};
// Push a metavariable with no fragment specifier at the given span
let mut missing_fragment_specifier = |span| {
sess.dcx().emit_err(errors::MissingFragmentSpecifier {
span,
add_span: span.shrink_to_hi(),
valid: VALID_FRAGMENT_NAMES_MSG,
});
// Fall back to a `TokenTree` since that will match anything if we continue expanding.
result.push(TokenTree::MetaVarDecl { span, name: ident, kind: NonterminalKind::TT });
};
// Not consuming the next token immediately, as it may not be a colon // Not consuming the next token immediately, as it may not be a colon
let span = match iter.peek() { if let Some(peek) = iter.peek()
Some(&tokenstream::TokenTree::Token( && let tokenstream::TokenTree::Token(token, _spacing) = peek
Token { kind: token::Colon, span: colon_span }, && let Token { kind: token::Colon, span: colon_span } = token
_, {
)) => { // Next token is a colon; consume it
// Consume the colon first
iter.next(); iter.next();
// It's ok to consume the next tree no matter how, // It's ok to consume the next tree no matter how,
// since if it's not a token then it will be an invalid declaration. // since if it's not a token then it will be an invalid declaration.
match iter.next() { let Some(tokenstream::TokenTree::Token(token, _)) = iter.next() else {
Some(tokenstream::TokenTree::Token(token, _)) => match token.ident() { // Invalid, return a nice source location as `var:`
Some((fragment, _)) => { missing_fragment_specifier(colon_span.with_lo(start_sp.lo()));
continue;
};
let Some((fragment, _)) = token.ident() else {
// No identifier for the fragment specifier;
missing_fragment_specifier(token.span);
continue;
};
let span = token.span.with_lo(start_sp.lo()); let span = token.span.with_lo(start_sp.lo());
let edition = || { let edition = || {
// FIXME(#85708) - once we properly decode a foreign // FIXME(#85708) - once we properly decode a foreign
@@ -80,40 +111,21 @@ pub(super) fn parse(
// `SyntaxContext::root()` from a foreign crate will // `SyntaxContext::root()` from a foreign crate will
// have the edition of that crate (which we manually // have the edition of that crate (which we manually
// retrieve via the `edition` parameter). // retrieve via the `edition` parameter).
if !span.from_expansion() { if !span.from_expansion() { edition } else { span.edition() }
edition
} else {
span.edition()
}
}; };
let kind = NonterminalKind::from_symbol(fragment.name, edition) let kind = NonterminalKind::from_symbol(fragment.name, edition).unwrap_or_else(|| {
.unwrap_or_else(|| {
sess.dcx().emit_err(errors::InvalidFragmentSpecifier { sess.dcx().emit_err(errors::InvalidFragmentSpecifier {
span, span,
fragment, fragment,
help: VALID_FRAGMENT_NAMES_MSG.into(), help: VALID_FRAGMENT_NAMES_MSG,
}); });
NonterminalKind::Ident NonterminalKind::TT
}); });
result.push(TokenTree::MetaVarDecl(span, ident, Some(kind))); result.push(TokenTree::MetaVarDecl { span, name: ident, kind });
continue; } else {
}
_ => token.span,
},
// Invalid, return a nice source location
_ => colon_span.with_lo(start_sp.lo()),
}
}
// Whether it's none or some other tree, it doesn't belong to // Whether it's none or some other tree, it doesn't belong to
// the current meta variable, returning the original span. // the current meta variable, returning the original span.
_ => start_sp, missing_fragment_specifier(start_sp);
};
result.push(TokenTree::MetaVarDecl(span, ident, None));
}
// Not a metavar or no matchers allowed, so just return the tree
_ => result.push(tree),
} }
} }
result result

View File

@@ -283,7 +283,7 @@ pub(super) fn transcribe<'a>(
} }
// There should be no meta-var declarations in the invocation of a macro. // There should be no meta-var declarations in the invocation of a macro.
mbe::TokenTree::MetaVarDecl(..) => panic!("unexpected `TokenTree::MetaVarDecl`"), mbe::TokenTree::MetaVarDecl { .. } => panic!("unexpected `TokenTree::MetaVarDecl`"),
} }
} }
} }
@@ -776,7 +776,7 @@ fn lockstep_iter_size(
size.with(lockstep_iter_size(tt, interpolations, repeats)) size.with(lockstep_iter_size(tt, interpolations, repeats))
}) })
} }
TokenTree::MetaVar(_, name) | TokenTree::MetaVarDecl(_, name, _) => { TokenTree::MetaVar(_, name) | TokenTree::MetaVarDecl { name, .. } => {
let name = MacroRulesNormalizedIdent::new(*name); let name = MacroRulesNormalizedIdent::new(*name);
match lookup_cur_matched(name, interpolations, repeats) { match lookup_cur_matched(name, interpolations, repeats) {
Some(matched) => match matched { Some(matched) => match matched {

View File

@@ -2,11 +2,10 @@ macro_rules! test {
($a, $b) => { ($a, $b) => {
//~^ ERROR missing fragment //~^ ERROR missing fragment
//~| ERROR missing fragment //~| ERROR missing fragment
//~| ERROR missing fragment
() ()
}; };
} }
fn main() { fn main() {
test!() test!() //~ ERROR unexpected end of macro invocation
} }

View File

@@ -24,7 +24,16 @@ help: try adding a specifier here
LL | ($a, $b:spec) => { LL | ($a, $b:spec) => {
| +++++ | +++++
error: missing fragment specifier error: unexpected end of macro invocation
--> $DIR/macro-match-nonterminal.rs:10:5
|
LL | macro_rules! test {
| ----------------- when calling this macro
...
LL | test!()
| ^^^^^^^ missing tokens in macro arguments
|
note: while trying to match meta-variable `$a:tt`
--> $DIR/macro-match-nonterminal.rs:2:6 --> $DIR/macro-match-nonterminal.rs:2:6
| |
LL | ($a, $b) => { LL | ($a, $b) => {

View File

@@ -2,12 +2,11 @@
macro_rules! m { macro_rules! m {
($name) => {}; //~ ERROR missing fragment ($name) => {}; //~ ERROR missing fragment
//~| ERROR missing fragment
} }
fn main() { fn main() {
m!(); m!(); //~ ERROR unexpected end
m!(); m!(); //~ ERROR unexpected end
m!(); m!(); //~ ERROR unexpected end
m!(); m!(); //~ ERROR unexpected end
} }

View File

@@ -11,11 +11,65 @@ help: try adding a specifier here
LL | ($name:spec) => {}; LL | ($name:spec) => {};
| +++++ | +++++
error: missing fragment specifier error: unexpected end of macro invocation
--> $DIR/macro-missing-fragment-deduplication.rs:8:5
|
LL | macro_rules! m {
| -------------- when calling this macro
...
LL | m!();
| ^^^^ missing tokens in macro arguments
|
note: while trying to match meta-variable `$name:tt`
--> $DIR/macro-missing-fragment-deduplication.rs:4:6 --> $DIR/macro-missing-fragment-deduplication.rs:4:6
| |
LL | ($name) => {}; LL | ($name) => {};
| ^^^^^ | ^^^^^
error: aborting due to 2 previous errors error: unexpected end of macro invocation
--> $DIR/macro-missing-fragment-deduplication.rs:9:5
|
LL | macro_rules! m {
| -------------- when calling this macro
...
LL | m!();
| ^^^^ missing tokens in macro arguments
|
note: while trying to match meta-variable `$name:tt`
--> $DIR/macro-missing-fragment-deduplication.rs:4:6
|
LL | ($name) => {};
| ^^^^^
error: unexpected end of macro invocation
--> $DIR/macro-missing-fragment-deduplication.rs:10:5
|
LL | macro_rules! m {
| -------------- when calling this macro
...
LL | m!();
| ^^^^ missing tokens in macro arguments
|
note: while trying to match meta-variable `$name:tt`
--> $DIR/macro-missing-fragment-deduplication.rs:4:6
|
LL | ($name) => {};
| ^^^^^
error: unexpected end of macro invocation
--> $DIR/macro-missing-fragment-deduplication.rs:11:5
|
LL | macro_rules! m {
| -------------- when calling this macro
...
LL | m!();
| ^^^^ missing tokens in macro arguments
|
note: while trying to match meta-variable `$name:tt`
--> $DIR/macro-missing-fragment-deduplication.rs:4:6
|
LL | ($name) => {};
| ^^^^^
error: aborting due to 5 previous errors

View File

@@ -2,7 +2,6 @@
macro_rules! used_arm { macro_rules! used_arm {
( $( any_token $field_rust_type )* ) => {}; //~ ERROR missing fragment ( $( any_token $field_rust_type )* ) => {}; //~ ERROR missing fragment
//~| ERROR missing fragment
} }
macro_rules! used_macro_unused_arm { macro_rules! used_macro_unused_arm {

View File

@@ -12,7 +12,7 @@ LL | ( $( any_token $field_rust_type:spec )* ) => {};
| +++++ | +++++
error: missing fragment specifier error: missing fragment specifier
--> $DIR/macro-missing-fragment.rs:10:7 --> $DIR/macro-missing-fragment.rs:9:7
| |
LL | ( $name ) => {}; LL | ( $name ) => {};
| ^^^^^ | ^^^^^
@@ -25,7 +25,7 @@ LL | ( $name:spec ) => {};
| +++++ | +++++
error: missing fragment specifier error: missing fragment specifier
--> $DIR/macro-missing-fragment.rs:14:7 --> $DIR/macro-missing-fragment.rs:13:7
| |
LL | ( $name ) => {}; LL | ( $name ) => {};
| ^^^^^ | ^^^^^
@@ -37,11 +37,5 @@ help: try adding a specifier here
LL | ( $name:spec ) => {}; LL | ( $name:spec ) => {};
| +++++ | +++++
error: missing fragment specifier error: aborting due to 3 previous errors
--> $DIR/macro-missing-fragment.rs:4:20
|
LL | ( $( any_token $field_rust_type )* ) => {};
| ^^^^^^^^^^^^^^^^
error: aborting due to 4 previous errors

View File

@@ -1,11 +1,10 @@
macro_rules! foo { macro_rules! foo {
{ $+ } => { //~ ERROR expected identifier, found `+` { $+ } => { //~ ERROR expected identifier, found `+`
//~^ ERROR missing fragment specifier //~^ ERROR missing fragment specifier
//~| ERROR missing fragment specifier
$(x)(y) //~ ERROR expected one of: `*`, `+`, or `?` $(x)(y) //~ ERROR expected one of: `*`, `+`, or `?`
} }
} }
foo!(); foo!(); //~ ERROR unexpected end
fn main() {} fn main() {}

View File

@@ -4,12 +4,6 @@ error: expected identifier, found `+`
LL | { $+ } => { LL | { $+ } => {
| ^ | ^
error: expected one of: `*`, `+`, or `?`
--> $DIR/issue-33569.rs:5:13
|
LL | $(x)(y)
| ^^^
error: missing fragment specifier error: missing fragment specifier
--> $DIR/issue-33569.rs:2:8 --> $DIR/issue-33569.rs:2:8
| |
@@ -23,7 +17,22 @@ help: try adding a specifier here
LL | { $+:spec } => { LL | { $+:spec } => {
| +++++ | +++++
error: missing fragment specifier error: expected one of: `*`, `+`, or `?`
--> $DIR/issue-33569.rs:4:13
|
LL | $(x)(y)
| ^^^
error: unexpected end of macro invocation
--> $DIR/issue-33569.rs:8:1
|
LL | macro_rules! foo {
| ---------------- when calling this macro
...
LL | foo!();
| ^^^^^^ missing tokens in macro arguments
|
note: while trying to match meta-variable `$<!dummy!>:tt`
--> $DIR/issue-33569.rs:2:8 --> $DIR/issue-33569.rs:2:8
| |
LL | { $+ } => { LL | { $+ } => {