use crate::base::{DummyResult, ExtCtxt, MacResult, TTMacroExpander};
use crate::base::{SyntaxExtension, SyntaxExtensionKind};
use crate::expand::{ensure_complete_parse, parse_ast_fragment, AstFragment, AstFragmentKind};
use crate::mbe;
use crate::mbe::macro_check;
use crate::mbe::macro_parser::parse_tt;
use crate::mbe::macro_parser::{Error, ErrorReported, Failure, Success};
use crate::mbe::macro_parser::{MatchedNonterminal, MatchedSeq};
use crate::mbe::transcribe::transcribe;
use rustc_ast::ast;
use rustc_ast::token::{self, NtTT, Token, TokenKind::*};
use rustc_ast::tokenstream::{DelimSpan, TokenStream};
use rustc_ast_pretty::pprust;
use rustc_attr::{self as attr, TransparencyError};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sync::Lrc;
use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_feature::Features;
use rustc_parse::parser::Parser;
use rustc_session::parse::ParseSess;
use rustc_span::edition::Edition;
use rustc_span::hygiene::Transparency;
use rustc_span::symbol::{kw, sym, MacroRulesNormalizedIdent, Symbol};
use rustc_span::Span;
use log::debug;
use std::borrow::Cow;
use std::collections::hash_map::Entry;
use std::{mem, slice};
const VALID_FRAGMENT_NAMES_MSG: &str = "valid fragment specifiers are \
`ident`, `block`, `stmt`, `expr`, `pat`, `ty`, `lifetime`, \
`literal`, `path`, `meta`, `tt`, `item` and `vis`";
crate struct ParserAnyMacro<'a> {
parser: Parser<'a>,
site_span: Span,
macro_ident: ast::Ident,
arm_span: Span,
}
crate fn annotate_err_with_kind(
err: &mut DiagnosticBuilder<'_>,
kind: AstFragmentKind,
span: Span,
) {
match kind {
AstFragmentKind::Ty => {
err.span_label(span, "this macro call doesn't expand to a type");
}
AstFragmentKind::Pat => {
err.span_label(span, "this macro call doesn't expand to a pattern");
}
_ => {}
};
}
fn suggest_slice_pat(e: &mut DiagnosticBuilder<'_>, site_span: Span, parser: &Parser<'_>) {
let mut suggestion = None;
if let Ok(code) = parser.sess.source_map().span_to_snippet(site_span) {
if let Some(bang) = code.find('!') {
suggestion = Some(code[bang + 1..].to_string());
}
}
if let Some(suggestion) = suggestion {
e.span_suggestion(
site_span,
"use a slice pattern here instead",
suggestion,
Applicability::MachineApplicable,
);
} else {
e.span_label(site_span, "use a slice pattern here instead");
}
e.help(
"for more information, see https://doc.rust-lang.org/edition-guide/\
rust-2018/slice-patterns.html",
);
}
fn emit_frag_parse_err(
mut e: DiagnosticBuilder<'_>,
parser: &Parser<'_>,
orig_parser: &mut Parser<'_>,
site_span: Span,
macro_ident: ast::Ident,
arm_span: Span,
kind: AstFragmentKind,
) {
if parser.token == token::Eof && e.message().ends_with(", found `<eof>`") {
if !e.span.is_dummy() {
e.replace_span_with(parser.sess.source_map().next_point(parser.token.span));
}
let msg = &e.message[0];
e.message[0] = (
format!(
"macro expansion ends with an incomplete expression: {}",
msg.0.replace(", found `<eof>`", ""),
),
msg.1,
);
}
if e.span.is_dummy() {
e.replace_span_with(site_span);
if !parser.sess.source_map().is_imported(arm_span) {
e.span_label(arm_span, "in this macro arm");
}
} else if parser.sess.source_map().is_imported(parser.token.span) {
e.span_label(site_span, "in this macro invocation");
}
match kind {
AstFragmentKind::Pat if macro_ident.name == sym::vec => {
suggest_slice_pat(&mut e, site_span, parser);
}
AstFragmentKind::Expr => match parse_ast_fragment(orig_parser, AstFragmentKind::Stmts) {
Err(mut err) => err.cancel(),
Ok(_) => {
e.note(
"the macro call doesn't expand to an expression, but it can expand to a statement",
);
e.span_suggestion_verbose(
site_span.shrink_to_hi(),
"add `;` to interpret the expansion as a statement",
";".to_string(),
Applicability::MaybeIncorrect,
);
}
},
_ => annotate_err_with_kind(&mut e, kind, site_span),
};
e.emit();
}
impl<'a> ParserAnyMacro<'a> {
crate fn make(mut self: Box<ParserAnyMacro<'a>>, kind: AstFragmentKind) -> AstFragment {
let ParserAnyMacro { site_span, macro_ident, ref mut parser, arm_span } = *self;
let snapshot = &mut parser.clone();
let fragment = match parse_ast_fragment(parser, kind) {
Ok(f) => f,
Err(err) => {
emit_frag_parse_err(err, parser, snapshot, site_span, macro_ident, arm_span, kind);
return kind.dummy(site_span);
}
};
if kind == AstFragmentKind::Expr && parser.token == token::Semi {
parser.bump();
}
let path = ast::Path::from_ident(macro_ident.with_span_pos(site_span));
ensure_complete_parse(parser, &path, kind.name(), site_span);
fragment
}
}
struct MacroRulesMacroExpander {
name: ast::Ident,
span: Span,
transparency: Transparency,
lhses: Vec<mbe::TokenTree>,
rhses: Vec<mbe::TokenTree>,
valid: bool,
}
impl TTMacroExpander for MacroRulesMacroExpander {
fn expand<'cx>(
&self,
cx: &'cx mut ExtCtxt<'_>,
sp: Span,
input: TokenStream,
) -> Box<dyn MacResult + 'cx> {
if !self.valid {
return DummyResult::any(sp);
}
generic_extension(
cx,
sp,
self.span,
self.name,
self.transparency,
input,
&self.lhses,
&self.rhses,
)
}
}
fn macro_rules_dummy_expander<'cx>(
_: &'cx mut ExtCtxt<'_>,
span: Span,
_: TokenStream,
) -> Box<dyn MacResult + 'cx> {
DummyResult::any(span)
}
fn trace_macros_note(cx_expansions: &mut FxHashMap<Span, Vec<String>>, sp: Span, message: String) {
let sp = sp.macro_backtrace().last().map(|trace| trace.call_site).unwrap_or(sp);
cx_expansions.entry(sp).or_default().push(message);
}
fn generic_extension<'cx>(
cx: &'cx mut ExtCtxt<'_>,
sp: Span,
def_span: Span,
name: ast::Ident,
transparency: Transparency,
arg: TokenStream,
lhses: &[mbe::TokenTree],
rhses: &[mbe::TokenTree],
) -> Box<dyn MacResult + 'cx> {
let sess = cx.parse_sess;
if cx.trace_macros() {
let msg = format!("expanding `{}! {{ {} }}`", name, pprust::tts_to_string(arg.clone()));
trace_macros_note(&mut cx.expansions, sp, msg);
}
let mut best_failure: Option<(Token, &str)> = None;
let parser = parser_from_cx(sess, arg.clone());
for (i, lhs) in lhses.iter().enumerate() {
let lhs_tt = match *lhs {
mbe::TokenTree::Delimited(_, ref delim) => &delim.tts[..],
_ => cx.span_bug(sp, "malformed macro lhs"),
};
let mut gated_spans_snapshot = mem::take(&mut *sess.gated_spans.spans.borrow_mut());
match parse_tt(&mut Cow::Borrowed(&parser), lhs_tt) {
Success(named_matches) => {
sess.gated_spans.merge(gated_spans_snapshot);
let rhs = match rhses[i] {
mbe::TokenTree::Delimited(_, ref delimed) => delimed.tts.clone(),
_ => cx.span_bug(sp, "malformed macro rhs"),
};
let arm_span = rhses[i].span();
let rhs_spans = rhs.iter().map(|t| t.span()).collect::<Vec<_>>();
let mut tts = match transcribe(cx, &named_matches, rhs, transparency) {
Ok(tts) => tts,
Err(mut err) => {
err.emit();
return DummyResult::any(arm_span);
}
};
if rhs_spans.len() == tts.len() {
tts = tts.map_enumerated(|i, mut tt| {
let mut sp = rhs_spans[i];
sp = sp.with_ctxt(tt.span().ctxt());
tt.set_span(sp);
tt
});
}
if cx.trace_macros() {
let msg = format!("to `{}`", pprust::tts_to_string(tts.clone()));
trace_macros_note(&mut cx.expansions, sp, msg);
}
let mut p = Parser::new(sess, tts, false, None);
p.last_type_ascription = cx.current_expansion.prior_type_ascription;
return Box::new(ParserAnyMacro {
parser: p,
site_span: sp,
macro_ident: name,
arm_span,
});
}
Failure(token, msg) => match best_failure {
Some((ref best_token, _)) if best_token.span.lo() >= token.span.lo() => {}
_ => best_failure = Some((token, msg)),
},
Error(err_sp, ref msg) => {
let span = err_sp.substitute_dummy(sp);
cx.struct_span_err(span, &msg).emit();
return DummyResult::any(span);
}
ErrorReported => return DummyResult::any(sp),
}
mem::swap(&mut gated_spans_snapshot, &mut sess.gated_spans.spans.borrow_mut());
}
drop(parser);
let (token, label) = best_failure.expect("ran no matchers");
let span = token.span.substitute_dummy(sp);
let mut err = cx.struct_span_err(span, &parse_failure_msg(&token));
err.span_label(span, label);
if !def_span.is_dummy() && !cx.source_map().is_imported(def_span) {
err.span_label(cx.source_map().guess_head_span(def_span), "when calling this macro");
}
if let Some((arg, comma_span)) = arg.add_comma() {
for lhs in lhses {
let lhs_tt = match *lhs {
mbe::TokenTree::Delimited(_, ref delim) => &delim.tts[..],
_ => continue,
};
if let Success(_) =
parse_tt(&mut Cow::Borrowed(&parser_from_cx(sess, arg.clone())), lhs_tt)
{
if comma_span.is_dummy() {
err.note("you might be missing a comma");
} else {
err.span_suggestion_short(
comma_span,
"missing comma here",
", ".to_string(),
Applicability::MachineApplicable,
);
}
}
}
}
err.emit();
cx.trace_macros_diag();
DummyResult::any(sp)
}
pub fn compile_declarative_macro(
sess: &ParseSess,
features: &Features,
def: &ast::Item,
edition: Edition,
) -> SyntaxExtension {
let mk_syn_ext = |expander| {
SyntaxExtension::new(
sess,
SyntaxExtensionKind::LegacyBang(expander),
def.span,
Vec::new(),
edition,
def.ident.name,
&def.attrs,
)
};
let diag = &sess.span_diagnostic;
let lhs_nm = ast::Ident::new(sym::lhs, def.span);
let rhs_nm = ast::Ident::new(sym::rhs, def.span);
let tt_spec = ast::Ident::new(sym::tt, def.span);
let (macro_rules, body) = match &def.kind {
ast::ItemKind::MacroDef(def) => (def.macro_rules, def.body.inner_tokens()),
_ => unreachable!(),
};
let argument_gram = vec![
mbe::TokenTree::Sequence(
DelimSpan::dummy(),
Lrc::new(mbe::SequenceRepetition {
tts: vec![
mbe::TokenTree::MetaVarDecl(def.span, lhs_nm, tt_spec),
mbe::TokenTree::token(token::FatArrow, def.span),
mbe::TokenTree::MetaVarDecl(def.span, rhs_nm, tt_spec),
],
separator: Some(Token::new(
if macro_rules { token::Semi } else { token::Comma },
def.span,
)),
kleene: mbe::KleeneToken::new(mbe::KleeneOp::OneOrMore, def.span),
num_captures: 2,
}),
),
mbe::TokenTree::Sequence(
DelimSpan::dummy(),
Lrc::new(mbe::SequenceRepetition {
tts: vec![mbe::TokenTree::token(
if macro_rules { token::Semi } else { token::Comma },
def.span,
)],
separator: None,
kleene: mbe::KleeneToken::new(mbe::KleeneOp::ZeroOrMore, def.span),
num_captures: 0,
}),
),
];
let parser = Parser::new(sess, body, true, rustc_parse::MACRO_ARGUMENTS);
let argument_map = match parse_tt(&mut Cow::Borrowed(&parser), &argument_gram) {
Success(m) => m,
Failure(token, msg) => {
let s = parse_failure_msg(&token);
let sp = token.span.substitute_dummy(def.span);
sess.span_diagnostic.struct_span_err(sp, &s).span_label(sp, msg).emit();
return mk_syn_ext(Box::new(macro_rules_dummy_expander));
}
Error(sp, msg) => {
sess.span_diagnostic.struct_span_err(sp.substitute_dummy(def.span), &msg).emit();
return mk_syn_ext(Box::new(macro_rules_dummy_expander));
}
ErrorReported => {
return mk_syn_ext(Box::new(macro_rules_dummy_expander));
}
};
let mut valid = true;
let lhses = match argument_map[&MacroRulesNormalizedIdent::new(lhs_nm)] {
MatchedSeq(ref s) => s
.iter()
.map(|m| {
if let MatchedNonterminal(ref nt) = *m {
if let NtTT(ref tt) = **nt {
let tt = mbe::quoted::parse(tt.clone().into(), true, sess).pop().unwrap();
valid &= check_lhs_nt_follows(sess, features, &def.attrs, &tt);
return tt;
}
}
sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs")
})
.collect::<Vec<mbe::TokenTree>>(),
_ => sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs"),
};
let rhses = match argument_map[&MacroRulesNormalizedIdent::new(rhs_nm)] {
MatchedSeq(ref s) => s
.iter()
.map(|m| {
if let MatchedNonterminal(ref nt) = *m {
if let NtTT(ref tt) = **nt {
return mbe::quoted::parse(tt.clone().into(), false, sess).pop().unwrap();
}
}
sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs")
})
.collect::<Vec<mbe::TokenTree>>(),
_ => sess.span_diagnostic.span_bug(def.span, "wrong-structured rhs"),
};
for rhs in &rhses {
valid &= check_rhs(sess, rhs);
}
for lhs in &lhses {
valid &= check_lhs_no_empty_seq(sess, slice::from_ref(lhs));
}
valid &= macro_check::check_meta_variables(sess, ast::CRATE_NODE_ID, def.span, &lhses, &rhses);
let (transparency, transparency_error) = attr::find_transparency(&def.attrs, macro_rules);
match transparency_error {
Some(TransparencyError::UnknownTransparency(value, span)) => {
diag.span_err(span, &format!("unknown macro transparency: `{}`", value))
}
Some(TransparencyError::MultipleTransparencyAttrs(old_span, new_span)) => {
diag.span_err(vec![old_span, new_span], "multiple macro transparency attributes")
}
None => {}
}
mk_syn_ext(Box::new(MacroRulesMacroExpander {
name: def.ident,
span: def.span,
transparency,
lhses,
rhses,
valid,
}))
}
fn check_lhs_nt_follows(
sess: &ParseSess,
features: &Features,
attrs: &[ast::Attribute],
lhs: &mbe::TokenTree,
) -> bool {
if let mbe::TokenTree::Delimited(_, ref tts) = *lhs {
check_matcher(sess, features, attrs, &tts.tts)
} else {
let msg = "invalid macro matcher; matchers must be contained in balanced delimiters";
sess.span_diagnostic.span_err(lhs.span(), msg);
false
}
}
fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[mbe::TokenTree]) -> bool {
use mbe::TokenTree;
for tt in tts {
match *tt {
TokenTree::Token(..) | TokenTree::MetaVar(..) | TokenTree::MetaVarDecl(..) => (),
TokenTree::Delimited(_, ref del) => {
if !check_lhs_no_empty_seq(sess, &del.tts) {
return false;
}
}
TokenTree::Sequence(span, ref seq) => {
if seq.separator.is_none()
&& seq.tts.iter().all(|seq_tt| match *seq_tt {
TokenTree::MetaVarDecl(_, _, id) => id.name == sym::vis,
TokenTree::Sequence(_, ref sub_seq) => {
sub_seq.kleene.op == mbe::KleeneOp::ZeroOrMore
|| sub_seq.kleene.op == mbe::KleeneOp::ZeroOrOne
}
_ => false,
})
{
let sp = span.entire();
sess.span_diagnostic.span_err(sp, "repetition matches empty token tree");
return false;
}
if !check_lhs_no_empty_seq(sess, &seq.tts) {
return false;
}
}
}
}
true
}
fn check_rhs(sess: &ParseSess, rhs: &mbe::TokenTree) -> bool {
match *rhs {
mbe::TokenTree::Delimited(..) => return true,
_ => sess.span_diagnostic.span_err(rhs.span(), "macro rhs must be delimited"),
}
false
}
fn check_matcher(
sess: &ParseSess,
features: &Features,
attrs: &[ast::Attribute],
matcher: &[mbe::TokenTree],
) -> bool {
let first_sets = FirstSets::new(matcher);
let empty_suffix = TokenSet::empty();
let err = sess.span_diagnostic.err_count();
check_matcher_core(sess, features, attrs, &first_sets, matcher, &empty_suffix);
err == sess.span_diagnostic.err_count()
}
struct FirstSets {
first: FxHashMap<Span, Option<TokenSet>>,
}
impl FirstSets {
fn new(tts: &[mbe::TokenTree]) -> FirstSets {
use mbe::TokenTree;
let mut sets = FirstSets { first: FxHashMap::default() };
build_recur(&mut sets, tts);
return sets;
fn build_recur(sets: &mut FirstSets, tts: &[TokenTree]) -> TokenSet {
let mut first = TokenSet::empty();
for tt in tts.iter().rev() {
match *tt {
TokenTree::Token(..) | TokenTree::MetaVar(..) | TokenTree::MetaVarDecl(..) => {
first.replace_with(tt.clone());
}
TokenTree::Delimited(span, ref delimited) => {
build_recur(sets, &delimited.tts[..]);
first.replace_with(delimited.open_tt(span));
}
TokenTree::Sequence(sp, ref seq_rep) => {
let subfirst = build_recur(sets, &seq_rep.tts[..]);
match sets.first.entry(sp.entire()) {
Entry::Vacant(vac) => {
vac.insert(Some(subfirst.clone()));
}
Entry::Occupied(mut occ) => {
occ.insert(None);
}
}
if let (Some(sep), true) = (&seq_rep.separator, subfirst.maybe_empty) {
first.add_one_maybe(TokenTree::Token(sep.clone()));
}
if subfirst.maybe_empty
|| seq_rep.kleene.op == mbe::KleeneOp::ZeroOrMore
|| seq_rep.kleene.op == mbe::KleeneOp::ZeroOrOne
{
first.add_all(&TokenSet { maybe_empty: true, ..subfirst });
} else {
first = subfirst;
}
}
}
}
first
}
}
fn first(&self, tts: &[mbe::TokenTree]) -> TokenSet {
use mbe::TokenTree;
let mut first = TokenSet::empty();
for tt in tts.iter() {
assert!(first.maybe_empty);
match *tt {
TokenTree::Token(..) | TokenTree::MetaVar(..) | TokenTree::MetaVarDecl(..) => {
first.add_one(tt.clone());
return first;
}
TokenTree::Delimited(span, ref delimited) => {
first.add_one(delimited.open_tt(span));
return first;
}
TokenTree::Sequence(sp, ref seq_rep) => {
let subfirst_owned;
let subfirst = match self.first.get(&sp.entire()) {
Some(&Some(ref subfirst)) => subfirst,
Some(&None) => {
subfirst_owned = self.first(&seq_rep.tts[..]);
&subfirst_owned
}
None => {
panic!("We missed a sequence during FirstSets construction");
}
};
if let (Some(sep), true) = (&seq_rep.separator, subfirst.maybe_empty) {
first.add_one_maybe(TokenTree::Token(sep.clone()));
}
assert!(first.maybe_empty);
first.add_all(subfirst);
if subfirst.maybe_empty
|| seq_rep.kleene.op == mbe::KleeneOp::ZeroOrMore
|| seq_rep.kleene.op == mbe::KleeneOp::ZeroOrOne
{
first.maybe_empty = true;
continue;
} else {
return first;
}
}
}
}
assert!(first.maybe_empty);
first
}
}
#[derive(Clone, Debug)]
struct TokenSet {
tokens: Vec<mbe::TokenTree>,
maybe_empty: bool,
}
impl TokenSet {
fn empty() -> Self {
TokenSet { tokens: Vec::new(), maybe_empty: true }
}
fn singleton(tok: mbe::TokenTree) -> Self {
TokenSet { tokens: vec![tok], maybe_empty: false }
}
fn replace_with(&mut self, tok: mbe::TokenTree) {
self.tokens.clear();
self.tokens.push(tok);
self.maybe_empty = false;
}
fn replace_with_irrelevant(&mut self) {
self.tokens.clear();
self.maybe_empty = false;
}
fn add_one(&mut self, tok: mbe::TokenTree) {
if !self.tokens.contains(&tok) {
self.tokens.push(tok);
}
self.maybe_empty = false;
}
fn add_one_maybe(&mut self, tok: mbe::TokenTree) {
if !self.tokens.contains(&tok) {
self.tokens.push(tok);
}
}
fn add_all(&mut self, other: &Self) {
for tok in &other.tokens {
if !self.tokens.contains(tok) {
self.tokens.push(tok.clone());
}
}
if !other.maybe_empty {
self.maybe_empty = false;
}
}
}
fn check_matcher_core(
sess: &ParseSess,
features: &Features,
attrs: &[ast::Attribute],
first_sets: &FirstSets,
matcher: &[mbe::TokenTree],
follow: &TokenSet,
) -> TokenSet {
use mbe::TokenTree;
let mut last = TokenSet::empty();
'each_token: for i in 0..matcher.len() {
let token = &matcher[i];
let suffix = &matcher[i + 1..];
let build_suffix_first = || {
let mut s = first_sets.first(suffix);
if s.maybe_empty {
s.add_all(follow);
}
s
};
let suffix_first;
match *token {
TokenTree::Token(..) | TokenTree::MetaVar(..) | TokenTree::MetaVarDecl(..) => {
let can_be_followed_by_any;
if let Err(bad_frag) = has_legal_fragment_specifier(sess, features, attrs, token) {
let msg = format!("invalid fragment specifier `{}`", bad_frag);
sess.span_diagnostic
.struct_span_err(token.span(), &msg)
.help(VALID_FRAGMENT_NAMES_MSG)
.emit();
can_be_followed_by_any = true;
} else {
can_be_followed_by_any = token_can_be_followed_by_any(token);
}
if can_be_followed_by_any {
last.replace_with_irrelevant();
continue 'each_token;
} else {
last.replace_with(token.clone());
suffix_first = build_suffix_first();
}
}
TokenTree::Delimited(span, ref d) => {
let my_suffix = TokenSet::singleton(d.close_tt(span));
check_matcher_core(sess, features, attrs, first_sets, &d.tts, &my_suffix);
last.replace_with_irrelevant();
continue 'each_token;
}
TokenTree::Sequence(_, ref seq_rep) => {
suffix_first = build_suffix_first();
let mut new;
let my_suffix = if let Some(sep) = &seq_rep.separator {
new = suffix_first.clone();
new.add_one_maybe(TokenTree::Token(sep.clone()));
&new
} else {
&suffix_first
};
let next =
check_matcher_core(sess, features, attrs, first_sets, &seq_rep.tts, my_suffix);
if next.maybe_empty {
last.add_all(&next);
} else {
last = next;
}
continue 'each_token;
}
}
'each_last: for token in &last.tokens {
if let TokenTree::MetaVarDecl(_, name, frag_spec) = *token {
for next_token in &suffix_first.tokens {
match is_in_follow(next_token, frag_spec.name) {
IsInFollow::Invalid(msg, help) => {
sess.span_diagnostic
.struct_span_err(next_token.span(), &msg)
.help(help)
.emit();
continue 'each_last;
}
IsInFollow::Yes => {}
IsInFollow::No(possible) => {
let may_be = if last.tokens.len() == 1 && suffix_first.tokens.len() == 1
{
"is"
} else {
"may be"
};
let sp = next_token.span();
let mut err = sess.span_diagnostic.struct_span_err(
sp,
&format!(
"`${name}:{frag}` {may_be} followed by `{next}`, which \
is not allowed for `{frag}` fragments",
name = name,
frag = frag_spec,
next = quoted_tt_to_string(next_token),
may_be = may_be
),
);
err.span_label(
sp,
format!("not allowed after `{}` fragments", frag_spec),
);
let msg = "allowed there are: ";
match possible {
&[] => {}
&[t] => {
err.note(&format!(
"only {} is allowed after `{}` fragments",
t, frag_spec,
));
}
ts => {
err.note(&format!(
"{}{} or {}",
msg,
ts[..ts.len() - 1]
.iter()
.copied()
.collect::<Vec<_>>()
.join(", "),
ts[ts.len() - 1],
));
}
}
err.emit();
}
}
}
}
}
}
last
}
fn token_can_be_followed_by_any(tok: &mbe::TokenTree) -> bool {
if let mbe::TokenTree::MetaVarDecl(_, _, frag_spec) = *tok {
frag_can_be_followed_by_any(frag_spec.name)
} else {
true
}
}
fn frag_can_be_followed_by_any(frag: Symbol) -> bool {
match frag {
sym::item |
sym::block |
sym::ident |
sym::literal |
sym::meta |
sym::lifetime |
sym::tt =>
true,
_ =>
false,
}
}
enum IsInFollow {
Yes,
No(&'static [&'static str]),
Invalid(String, &'static str),
}
fn is_in_follow(tok: &mbe::TokenTree, frag: Symbol) -> IsInFollow {
use mbe::TokenTree;
if let TokenTree::Token(Token { kind: token::CloseDelim(_), .. }) = *tok {
IsInFollow::Yes
} else {
match frag {
sym::item => {
IsInFollow::Yes
}
sym::block => {
IsInFollow::Yes
}
sym::stmt | sym::expr => {
const TOKENS: &[&str] = &["`=>`", "`,`", "`;`"];
match tok {
TokenTree::Token(token) => match token.kind {
FatArrow | Comma | Semi => IsInFollow::Yes,
_ => IsInFollow::No(TOKENS),
},
_ => IsInFollow::No(TOKENS),
}
}
sym::pat => {
const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`|`", "`if`", "`in`"];
match tok {
TokenTree::Token(token) => match token.kind {
FatArrow | Comma | Eq | BinOp(token::Or) => IsInFollow::Yes,
Ident(name, false) if name == kw::If || name == kw::In => IsInFollow::Yes,
_ => IsInFollow::No(TOKENS),
},
_ => IsInFollow::No(TOKENS),
}
}
sym::path | sym::ty => {
const TOKENS: &[&str] = &[
"`{`", "`[`", "`=>`", "`,`", "`>`", "`=`", "`:`", "`;`", "`|`", "`as`",
"`where`",
];
match tok {
TokenTree::Token(token) => match token.kind {
OpenDelim(token::DelimToken::Brace)
| OpenDelim(token::DelimToken::Bracket)
| Comma
| FatArrow
| Colon
| Eq
| Gt
| BinOp(token::Shr)
| Semi
| BinOp(token::Or) => IsInFollow::Yes,
Ident(name, false) if name == kw::As || name == kw::Where => {
IsInFollow::Yes
}
_ => IsInFollow::No(TOKENS),
},
TokenTree::MetaVarDecl(_, _, frag) if frag.name == sym::block => {
IsInFollow::Yes
}
_ => IsInFollow::No(TOKENS),
}
}
sym::ident | sym::lifetime => {
IsInFollow::Yes
}
sym::literal => {
IsInFollow::Yes
}
sym::meta | sym::tt => {
IsInFollow::Yes
}
sym::vis => {
const TOKENS: &[&str] = &["`,`", "an ident", "a type"];
match tok {
TokenTree::Token(token) => match token.kind {
Comma => IsInFollow::Yes,
Ident(name, is_raw) if is_raw || name != kw::Priv => IsInFollow::Yes,
_ => {
if token.can_begin_type() {
IsInFollow::Yes
} else {
IsInFollow::No(TOKENS)
}
}
},
TokenTree::MetaVarDecl(_, _, frag)
if frag.name == sym::ident
|| frag.name == sym::ty
|| frag.name == sym::path =>
{
IsInFollow::Yes
}
_ => IsInFollow::No(TOKENS),
}
}
kw::Invalid => IsInFollow::Yes,
_ => IsInFollow::Invalid(
format!("invalid fragment specifier `{}`", frag),
VALID_FRAGMENT_NAMES_MSG,
),
}
}
}
fn has_legal_fragment_specifier(
sess: &ParseSess,
features: &Features,
attrs: &[ast::Attribute],
tok: &mbe::TokenTree,
) -> Result<(), String> {
debug!("has_legal_fragment_specifier({:?})", tok);
if let mbe::TokenTree::MetaVarDecl(_, _, ref frag_spec) = *tok {
let frag_span = tok.span();
if !is_legal_fragment_specifier(sess, features, attrs, frag_spec.name, frag_span) {
return Err(frag_spec.to_string());
}
}
Ok(())
}
fn is_legal_fragment_specifier(
_sess: &ParseSess,
_features: &Features,
_attrs: &[ast::Attribute],
frag_name: Symbol,
_frag_span: Span,
) -> bool {
match frag_name {
sym::item
| sym::block
| sym::stmt
| sym::expr
| sym::pat
| sym::lifetime
| sym::path
| sym::ty
| sym::ident
| sym::meta
| sym::tt
| sym::vis
| sym::literal
| kw::Invalid => true,
_ => false,
}
}
fn quoted_tt_to_string(tt: &mbe::TokenTree) -> String {
match *tt {
mbe::TokenTree::Token(ref token) => pprust::token_to_string(&token),
mbe::TokenTree::MetaVar(_, name) => format!("${}", name),
mbe::TokenTree::MetaVarDecl(_, name, kind) => format!("${}:{}", name, kind),
_ => panic!(
"unexpected mbe::TokenTree::{{Sequence or Delimited}} \
in follow set checker"
),
}
}
fn parser_from_cx(sess: &ParseSess, tts: TokenStream) -> Parser<'_> {
Parser::new(sess, tts, true, rustc_parse::MACRO_ARGUMENTS)
}
fn parse_failure_msg(tok: &Token) -> String {
match tok.kind {
token::Eof => "unexpected end of macro invocation".to_string(),
_ => format!("no rules expected the token `{}`", pprust::token_to_string(tok),),
}
}