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:
@@ -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,
|
||||||
|
|||||||
@@ -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)]
|
||||||
|
|||||||
@@ -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(),
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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) => {
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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} \
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) => {
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
|
||||||
|
|
||||||
|
|||||||
@@ -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() {}
|
||||||
|
|||||||
@@ -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 | { $+ } => {
|
||||||
|
|||||||
Reference in New Issue
Block a user