rust/compiler/rustc_expand/src/mbe/macro_rules.rs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1355 lines
53 KiB
Rust
Raw Normal View History

2020-03-08 13:36:20 +01:00
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::{Error, ErrorReported, Failure, Success, TtParser};
use crate::mbe::macro_parser::{MatchedSeq, MatchedTokenTree, MatcherLoc};
use crate::mbe::transcribe::transcribe;
2020-04-27 23:26:11 +05:30
use rustc_ast as ast;
use rustc_ast::token::{self, Delimiter, NonterminalKind, Token, TokenKind, TokenKind::*};
use rustc_ast::tokenstream::{DelimSpan, TokenStream};
use rustc_ast::{NodeId, DUMMY_NODE_ID};
use rustc_ast_pretty::pprust;
use rustc_attr::{self as attr, TransparencyError};
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder};
use rustc_feature::Features;
use rustc_lint_defs::builtin::{
RUST_2021_INCOMPATIBLE_OR_PATTERNS, SEMICOLON_IN_EXPRESSIONS_FROM_MACROS,
};
use rustc_lint_defs::BuiltinLintDiagnostics;
use rustc_parse::parser::Parser;
use rustc_session::parse::ParseSess;
use rustc_session::Session;
2020-01-01 19:40:49 +01:00
use rustc_span::edition::Edition;
use rustc_span::hygiene::Transparency;
use rustc_span::symbol::{kw, sym, Ident, MacroRulesNormalizedIdent};
use rustc_span::Span;
2019-02-07 02:33:01 +09:00
2018-07-02 19:44:01 -05:00
use std::borrow::Cow;
use std::collections::hash_map::Entry;
use std::{mem, slice};
2020-08-13 23:05:01 -07:00
use tracing::debug;
2018-02-27 17:11:14 +01:00
pub(crate) struct ParserAnyMacro<'a> {
parser: Parser<'a>,
/// Span of the expansion site of the macro this parser is for
site_span: Span,
/// The ident of the macro we're parsing
2020-04-19 13:00:18 +02:00
macro_ident: Ident,
lint_node_id: NodeId,
Display an extra note for trailing semicolon lint with trailing macro Currently, we parse macros at the end of a block (e.g. `fn foo() { my_macro!() }`) as expressions, rather than statements. This means that a macro invoked in this position cannot expand to items or semicolon-terminated expressions. In the future, we might want to start parsing these kinds of macros as statements. This would make expansion more 'token-based' (i.e. macro expansion behaves (almost) as if you just textually replaced the macro invocation with its output). However, this is a breaking change (see PR #78991), so it will require further discussion. Since the current behavior will not be changing any time soon, we need to address the interaction with the `SEMICOLON_IN_EXPRESSIONS_FROM_MACROS` lint. Since we are parsing the result of macro expansion as an expression, we will emit a lint if there's a trailing semicolon in the macro output. However, this results in a somewhat confusing message for users, since it visually looks like there should be no problem with having a semicolon at the end of a block (e.g. `fn foo() { my_macro!() }` => `fn foo() { produced_expr; }`) To help reduce confusion, this commit adds a note explaining that the macro is being interpreted as an expression. Additionally, we suggest adding a semicolon after the macro *invocation* - this will cause us to parse the macro call as a statement. We do *not* use a structured suggestion for this, since the user may actually want to remove the semicolon from the macro definition (allowing the block to evaluate to the expression produced by the macro).
2021-07-22 11:24:42 -05:00
is_trailing_mac: bool,
arm_span: Span,
2021-07-24 17:44:57 -05:00
/// Whether or not this macro is defined in the current crate
is_local: bool,
}
pub(crate) fn annotate_err_with_kind(err: &mut Diagnostic, 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");
}
_ => {}
};
}
2020-03-17 08:55:28 +01:00
fn emit_frag_parse_err(
mut e: DiagnosticBuilder<'_, rustc_errors::ErrorGuaranteed>,
2020-03-17 08:55:28 +01:00
parser: &Parser<'_>,
orig_parser: &mut Parser<'_>,
2020-03-17 08:55:28 +01:00
site_span: Span,
arm_span: Span,
kind: AstFragmentKind,
) {
// FIXME(davidtwco): avoid depending on the error message text
if parser.token == token::Eof && e.message[0].0.expect_str().ends_with(", found `<eof>`") {
2020-03-17 08:55:28 +01:00
if !e.span.is_dummy() {
// early end of macro arm (#52866)
e.replace_span_with(parser.sess.source_map().next_point(parser.token.span));
}
let msg = &e.message[0];
e.message[0] = (
rustc_errors::DiagnosticMessage::Str(format!(
2020-03-17 08:55:28 +01:00
"macro expansion ends with an incomplete expression: {}",
msg.0.expect_str().replace(", found `<eof>`", ""),
)),
2020-03-17 08:55:28 +01:00
msg.1,
);
}
if e.span.is_dummy() {
// Get around lack of span in error (#30128)
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 {
// Try a statement if an expression is wanted but failed and suggest adding `;` to call.
AstFragmentKind::Expr => match parse_ast_fragment(orig_parser, AstFragmentKind::Stmts) {
Err(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,
);
}
},
2020-03-17 08:55:28 +01:00
_ => annotate_err_with_kind(&mut e, kind, site_span),
};
e.emit();
}
2014-03-09 16:54:34 +02:00
impl<'a> ParserAnyMacro<'a> {
pub(crate) fn make(mut self: Box<ParserAnyMacro<'a>>, kind: AstFragmentKind) -> AstFragment {
Display an extra note for trailing semicolon lint with trailing macro Currently, we parse macros at the end of a block (e.g. `fn foo() { my_macro!() }`) as expressions, rather than statements. This means that a macro invoked in this position cannot expand to items or semicolon-terminated expressions. In the future, we might want to start parsing these kinds of macros as statements. This would make expansion more 'token-based' (i.e. macro expansion behaves (almost) as if you just textually replaced the macro invocation with its output). However, this is a breaking change (see PR #78991), so it will require further discussion. Since the current behavior will not be changing any time soon, we need to address the interaction with the `SEMICOLON_IN_EXPRESSIONS_FROM_MACROS` lint. Since we are parsing the result of macro expansion as an expression, we will emit a lint if there's a trailing semicolon in the macro output. However, this results in a somewhat confusing message for users, since it visually looks like there should be no problem with having a semicolon at the end of a block (e.g. `fn foo() { my_macro!() }` => `fn foo() { produced_expr; }`) To help reduce confusion, this commit adds a note explaining that the macro is being interpreted as an expression. Additionally, we suggest adding a semicolon after the macro *invocation* - this will cause us to parse the macro call as a statement. We do *not* use a structured suggestion for this, since the user may actually want to remove the semicolon from the macro definition (allowing the block to evaluate to the expression produced by the macro).
2021-07-22 11:24:42 -05:00
let ParserAnyMacro {
site_span,
macro_ident,
ref mut parser,
lint_node_id,
arm_span,
is_trailing_mac,
2021-07-24 17:44:57 -05:00
is_local,
Display an extra note for trailing semicolon lint with trailing macro Currently, we parse macros at the end of a block (e.g. `fn foo() { my_macro!() }`) as expressions, rather than statements. This means that a macro invoked in this position cannot expand to items or semicolon-terminated expressions. In the future, we might want to start parsing these kinds of macros as statements. This would make expansion more 'token-based' (i.e. macro expansion behaves (almost) as if you just textually replaced the macro invocation with its output). However, this is a breaking change (see PR #78991), so it will require further discussion. Since the current behavior will not be changing any time soon, we need to address the interaction with the `SEMICOLON_IN_EXPRESSIONS_FROM_MACROS` lint. Since we are parsing the result of macro expansion as an expression, we will emit a lint if there's a trailing semicolon in the macro output. However, this results in a somewhat confusing message for users, since it visually looks like there should be no problem with having a semicolon at the end of a block (e.g. `fn foo() { my_macro!() }` => `fn foo() { produced_expr; }`) To help reduce confusion, this commit adds a note explaining that the macro is being interpreted as an expression. Additionally, we suggest adding a semicolon after the macro *invocation* - this will cause us to parse the macro call as a statement. We do *not* use a structured suggestion for this, since the user may actually want to remove the semicolon from the macro definition (allowing the block to evaluate to the expression produced by the macro).
2021-07-22 11:24:42 -05:00
} = *self;
let snapshot = &mut parser.clone();
2020-03-17 08:55:28 +01:00
let fragment = match parse_ast_fragment(parser, kind) {
Ok(f) => f,
Err(err) => {
2020-12-18 12:27:36 +01:00
emit_frag_parse_err(err, parser, snapshot, site_span, arm_span, kind);
2020-03-17 08:55:28 +01:00
return kind.dummy(site_span);
}
2020-03-17 08:55:28 +01:00
};
// We allow semicolons at the end of expressions -- e.g., the semicolon in
2016-09-23 23:09:23 +00:00
// `macro_rules! m { () => { panic!(); } }` isn't parsed by `.parse_expr()`,
// but `m!()` is allowed in expression positions (cf. issue #34706).
if kind == AstFragmentKind::Expr && parser.token == token::Semi {
2021-07-24 17:44:57 -05:00
if is_local {
parser.sess.buffer_lint_with_diagnostic(
SEMICOLON_IN_EXPRESSIONS_FROM_MACROS,
parser.token.span,
lint_node_id,
"trailing semicolon in macro used in expression position",
BuiltinLintDiagnostics::TrailingMacro(is_trailing_mac, macro_ident),
);
}
2016-09-23 23:09:23 +00:00
parser.bump();
}
// Make sure we don't have any tokens left to parse so we don't silently drop anything.
2018-03-19 03:54:56 +03:00
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 {
2022-04-17 04:01:17 +02:00
node_id: NodeId,
2020-04-19 13:00:18 +02:00
name: Ident,
span: Span,
transparency: Transparency,
lhses: Vec<Vec<MatcherLoc>>,
2019-09-22 19:17:30 +03:00
rhses: Vec<mbe::TokenTree>,
valid: bool,
}
impl TTMacroExpander for MacroRulesMacroExpander {
fn expand<'cx>(
&self,
2019-02-07 02:33:01 +09:00
cx: &'cx mut ExtCtxt<'_>,
sp: Span,
input: TokenStream,
2019-05-29 20:21:26 +02:00
) -> Box<dyn MacResult + 'cx> {
if !self.valid {
return DummyResult::any(sp);
}
expand_macro(
cx,
sp,
self.span,
2022-04-17 04:01:17 +02:00
self.node_id,
self.name,
self.transparency,
input,
&self.lhses,
&self.rhses,
)
}
}
2020-03-18 13:34:11 +01:00
fn macro_rules_dummy_expander<'cx>(
_: &'cx mut ExtCtxt<'_>,
span: Span,
_: TokenStream,
) -> Box<dyn MacResult + 'cx> {
DummyResult::any(span)
2020-03-17 12:54:57 +01:00
}
fn trace_macros_note(cx_expansions: &mut FxHashMap<Span, Vec<String>>, sp: Span, message: String) {
let sp = sp.macro_backtrace().last().map_or(sp, |trace| trace.call_site);
cx_expansions.entry(sp).or_default().push(message);
}
/// Expands the rules based macro defined by `lhses` and `rhses` for a given
/// input `arg`.
fn expand_macro<'cx, 'tt>(
2019-05-29 20:21:26 +02:00
cx: &'cx mut ExtCtxt<'_>,
sp: Span,
def_span: Span,
2022-04-17 04:01:17 +02:00
node_id: NodeId,
2020-04-19 13:00:18 +02:00
name: Ident,
transparency: Transparency,
2019-05-29 20:21:26 +02:00
arg: TokenStream,
lhses: &'tt [Vec<MatcherLoc>],
rhses: &'tt [mbe::TokenTree],
2019-05-29 20:21:26 +02:00
) -> Box<dyn MacResult + 'cx> {
let sess = &cx.sess.parse_sess;
2022-04-17 04:01:17 +02:00
// Macros defined in the current crate have a real node id,
// whereas macros from an external crate have a dummy id.
let is_local = node_id != DUMMY_NODE_ID;
2020-03-08 13:36:20 +01:00
if cx.trace_macros() {
let msg = format!("expanding `{}! {{ {} }}`", name, pprust::tts_to_string(&arg));
trace_macros_note(&mut cx.expansions, sp, msg);
}
// Which arm's failure should we report? (the one furthest along)
2019-06-05 22:04:52 +03:00
let mut best_failure: Option<(Token, &str)> = None;
// We create a base parser that can be used for the "black box" parts.
2020-02-05 09:44:03 +11:00
// Every iteration needs a fresh copy of that parser. However, the parser
// is not mutated on many of the iterations, particularly when dealing with
// macros like this:
//
// macro_rules! foo {
// ("a") => (A);
// ("b") => (B);
// ("c") => (C);
// // ... etc. (maybe hundreds more)
// }
//
// as seen in the `html5ever` benchmark. We use a `Cow` so that the base
// parser is only cloned when necessary (upon mutation). Furthermore, we
// reinitialize the `Cow` with the base parser at the start of every
// iteration, so that any mutated parsers are not reused. This is all quite
// hacky, but speeds up the `html5ever` benchmark significantly. (Issue
// 68836 suggests a more comprehensive but more complex change to deal with
// this situation.)
2020-03-08 13:36:20 +01:00
let parser = parser_from_cx(sess, arg.clone());
// Try each arm's matchers.
let mut tt_parser = TtParser::new(name);
for (i, lhs) in lhses.iter().enumerate() {
// Take a snapshot of the state of pre-expansion gating at this point.
// This is used so that if a matcher is not `Success(..)`ful,
2019-11-26 22:19:54 -05:00
// then the spans which became gated when parsing the unsuccessful matcher
// are not recorded. On the first `Success(..)`ful matcher, the spans are merged.
2020-03-08 13:36:20 +01:00
let mut gated_spans_snapshot = mem::take(&mut *sess.gated_spans.spans.borrow_mut());
match tt_parser.parse_tt(&mut Cow::Borrowed(&parser), lhs) {
2015-11-14 19:18:32 +09:00
Success(named_matches) => {
// The matcher was `Success(..)`ful.
// Merge the gated spans from parsing the matcher with the pre-existing ones.
2020-03-08 13:36:20 +01:00
sess.gated_spans.merge(gated_spans_snapshot);
let (rhs, rhs_span): (&mbe::Delimited, DelimSpan) = match &rhses[i] {
mbe::TokenTree::Delimited(span, delimited) => (&delimited, *span),
_ => cx.span_bug(sp, "malformed macro rhs"),
};
let arm_span = rhses[i].span();
let rhs_spans = rhs.tts.iter().map(|t| t.span()).collect::<Vec<_>>();
// rhs has holes ( `$id` and `$(...)` that need filled)
let mut tts = match transcribe(cx, &named_matches, &rhs, rhs_span, transparency) {
2020-03-17 12:12:57 +01:00
Ok(tts) => tts,
Err(mut err) => {
err.emit();
return DummyResult::any(arm_span);
}
};
// Replace all the tokens for the corresponding positions in the macro, to maintain
// proper positions in error reporting, while maintaining the macro_backtrace.
2017-07-21 16:44:23 -07:00
if rhs_spans.len() == tts.len() {
tts = tts.map_enumerated(|i, tt| {
let mut tt = tt.clone();
2017-07-21 16:44:23 -07:00
let mut sp = rhs_spans[i];
2017-07-31 23:04:34 +03:00
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));
trace_macros_note(&mut cx.expansions, sp, msg);
}
2020-03-09 10:35:35 +01:00
let mut p = Parser::new(sess, tts, false, None);
p.last_type_ascription = cx.current_expansion.prior_type_ascription;
2022-04-17 04:01:17 +02:00
if is_local {
cx.resolver.record_macro_rule_usage(node_id, i);
}
// Let the context choose how to interpret the result.
// Weird, but useful for X-macros.
return Box::new(ParserAnyMacro {
parser: p,
// Pass along the original expansion site and the name of the macro
// so we can print a useful error message if the parse of the expanded
// macro leaves unparsed tokens.
site_span: sp,
macro_ident: name,
lint_node_id: cx.current_expansion.lint_node_id,
Display an extra note for trailing semicolon lint with trailing macro Currently, we parse macros at the end of a block (e.g. `fn foo() { my_macro!() }`) as expressions, rather than statements. This means that a macro invoked in this position cannot expand to items or semicolon-terminated expressions. In the future, we might want to start parsing these kinds of macros as statements. This would make expansion more 'token-based' (i.e. macro expansion behaves (almost) as if you just textually replaced the macro invocation with its output). However, this is a breaking change (see PR #78991), so it will require further discussion. Since the current behavior will not be changing any time soon, we need to address the interaction with the `SEMICOLON_IN_EXPRESSIONS_FROM_MACROS` lint. Since we are parsing the result of macro expansion as an expression, we will emit a lint if there's a trailing semicolon in the macro output. However, this results in a somewhat confusing message for users, since it visually looks like there should be no problem with having a semicolon at the end of a block (e.g. `fn foo() { my_macro!() }` => `fn foo() { produced_expr; }`) To help reduce confusion, this commit adds a note explaining that the macro is being interpreted as an expression. Additionally, we suggest adding a semicolon after the macro *invocation* - this will cause us to parse the macro call as a statement. We do *not* use a structured suggestion for this, since the user may actually want to remove the semicolon from the macro definition (allowing the block to evaluate to the expression produced by the macro).
2021-07-22 11:24:42 -05:00
is_trailing_mac: cx.current_expansion.is_trailing_mac,
arm_span,
2021-07-24 17:44:57 -05:00
is_local,
2019-05-29 20:21:26 +02:00
});
2015-11-14 19:18:32 +09:00
}
2019-06-05 22:04:52 +03:00
Failure(token, msg) => match best_failure {
Some((ref best_token, _)) if best_token.span.lo() >= token.span.lo() => {}
2019-05-29 20:21:26 +02:00
_ => 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);
}
2020-03-17 14:13:32 +01:00
ErrorReported => return DummyResult::any(sp),
}
// The matcher was not `Success(..)`ful.
// Restore to the state before snapshotting and maybe try again.
2020-03-08 13:36:20 +01:00
mem::swap(&mut gated_spans_snapshot, &mut sess.gated_spans.spans.borrow_mut());
}
2020-02-05 09:44:03 +11:00
drop(parser);
2019-06-05 22:04:52 +03:00
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));
2019-06-05 22:04:52 +03:00
err.span_label(span, label);
if !def_span.is_dummy() && !cx.source_map().is_imported(def_span) {
2020-03-09 11:42:37 -07:00
err.span_label(cx.source_map().guess_head_span(def_span), "when calling this macro");
}
// Check whether there's a missing comma in this macro call, like `println!("{}" a);`
if let Some((arg, comma_span)) = arg.add_comma() {
2019-05-29 20:21:26 +02:00
for lhs in lhses {
let parser = parser_from_cx(sess, arg.clone());
if let Success(_) = tt_parser.parse_tt(&mut Cow::Borrowed(&parser), lhs) {
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();
2017-09-02 17:21:13 +02:00
cx.trace_macros_diag();
DummyResult::any(sp)
}
2014-10-07 00:18:24 +01:00
// Note that macro-by-example's input is also matched against a token tree:
// $( $lhs:tt => $rhs:tt );+
//
// Holy self-referential!
/// Converts a macro item into a syntax extension.
pub fn compile_declarative_macro(
sess: &Session,
2019-01-14 19:14:02 -06:00
features: &Features,
def: &ast::Item,
2019-05-29 20:21:26 +02:00
edition: Edition,
2022-04-17 04:01:17 +02:00
) -> (SyntaxExtension, Vec<Span>) {
debug!("compile_declarative_macro: {:?}", def);
2020-03-17 12:54:57 +01:00
let mk_syn_ext = |expander| {
SyntaxExtension::new(
sess,
SyntaxExtensionKind::LegacyBang(expander),
def.span,
Vec::new(),
edition,
def.ident.name,
&def.attrs,
)
};
2022-04-17 04:01:17 +02:00
let dummy_syn_ext = || (mk_syn_ext(Box::new(macro_rules_dummy_expander)), Vec::new());
2020-03-17 12:54:57 +01:00
let diag = &sess.parse_sess.span_diagnostic;
2020-04-19 13:00:18 +02:00
let lhs_nm = Ident::new(sym::lhs, def.span);
let rhs_nm = Ident::new(sym::rhs, def.span);
let tt_spec = Some(NonterminalKind::TT);
2012-07-18 16:18:02 -07:00
// Parse the macro_rules! invocation
let (macro_rules, body) = match &def.kind {
ast::ItemKind::MacroDef(def) => (def.macro_rules, def.body.inner_tokens()),
_ => unreachable!(),
};
2013-05-29 16:21:04 -07:00
// The pattern that macro_rules matches.
2012-07-27 17:32:15 -07:00
// The grammar for macro_rules! is:
2014-10-06 23:00:56 +01:00
// $( $lhs:tt => $rhs:tt );+
2012-07-27 17:32:15 -07:00
// ...quasiquoting this would be nice.
2014-10-06 23:00:56 +01:00
// These spans won't matter, anyways
let argument_gram = vec![
2019-09-22 19:17:30 +03:00
mbe::TokenTree::Sequence(
2019-05-29 20:21:26 +02:00
DelimSpan::dummy(),
mbe::SequenceRepetition {
2019-05-29 20:21:26 +02:00
tts: vec![
2019-09-22 19:17:30 +03:00
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),
2019-05-29 20:21:26 +02:00
],
separator: Some(Token::new(
if macro_rules { token::Semi } else { token::Comma },
2019-05-29 20:21:26 +02:00
def.span,
)),
2019-09-22 19:17:30 +03:00
kleene: mbe::KleeneToken::new(mbe::KleeneOp::OneOrMore, def.span),
2019-05-29 20:21:26 +02:00
num_captures: 2,
},
2019-05-29 20:21:26 +02:00
),
// to phase into semicolon-termination instead of semicolon-separation
2019-09-22 19:17:30 +03:00
mbe::TokenTree::Sequence(
2019-05-29 20:21:26 +02:00
DelimSpan::dummy(),
mbe::SequenceRepetition {
2019-09-22 19:17:30 +03:00
tts: vec![mbe::TokenTree::token(
if macro_rules { token::Semi } else { token::Comma },
def.span,
)],
2019-05-29 20:21:26 +02:00
separator: None,
2019-09-22 19:17:30 +03:00
kleene: mbe::KleeneToken::new(mbe::KleeneOp::ZeroOrMore, def.span),
2019-05-29 20:21:26 +02:00
num_captures: 0,
},
2019-05-29 20:21:26 +02:00
),
];
// Convert it into `MatcherLoc` form.
let argument_gram = mbe::macro_parser::compute_locs(&argument_gram);
2012-07-27 17:32:15 -07:00
let parser = Parser::new(&sess.parse_sess, body, true, rustc_parse::MACRO_ARGUMENTS);
let mut tt_parser =
TtParser::new(Ident::with_dummy_span(if macro_rules { kw::MacroRules } else { kw::Macro }));
let argument_map = match tt_parser.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.parse_sess.span_diagnostic.struct_span_err(sp, &s).span_label(sp, msg).emit();
2022-04-17 04:01:17 +02:00
return dummy_syn_ext();
}
Error(sp, msg) => {
sess.parse_sess
.span_diagnostic
.struct_span_err(sp.substitute_dummy(def.span), &msg)
.emit();
2022-04-17 04:01:17 +02:00
return dummy_syn_ext();
}
ErrorReported => {
2022-04-17 04:01:17 +02:00
return dummy_syn_ext();
}
};
let mut valid = true;
2012-07-27 17:32:15 -07:00
// Extract the arguments:
let lhses = match argument_map[&MacroRulesNormalizedIdent::new(lhs_nm)] {
MatchedSeq(ref s) => s
2019-05-29 20:21:26 +02:00
.iter()
.map(|m| {
if let MatchedTokenTree(ref tt) = *m {
let tt = mbe::quoted::parse(
tt.clone().into(),
true,
&sess.parse_sess,
def.id,
features,
edition,
)
.pop()
.unwrap();
valid &= check_lhs_nt_follows(&sess.parse_sess, &def, &tt);
return tt;
}
sess.parse_sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs")
2019-05-29 20:21:26 +02:00
})
2019-09-22 19:17:30 +03:00
.collect::<Vec<mbe::TokenTree>>(),
_ => sess.parse_sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs"),
};
let rhses = match argument_map[&MacroRulesNormalizedIdent::new(rhs_nm)] {
MatchedSeq(ref s) => s
2019-05-29 20:21:26 +02:00
.iter()
.map(|m| {
if let MatchedTokenTree(ref tt) = *m {
return mbe::quoted::parse(
tt.clone().into(),
false,
&sess.parse_sess,
def.id,
features,
edition,
)
.pop()
.unwrap();
}
sess.parse_sess.span_diagnostic.span_bug(def.span, "wrong-structured lhs")
2019-05-29 20:21:26 +02:00
})
2019-09-22 19:17:30 +03:00
.collect::<Vec<mbe::TokenTree>>(),
_ => sess.parse_sess.span_diagnostic.span_bug(def.span, "wrong-structured rhs"),
};
for rhs in &rhses {
valid &= check_rhs(&sess.parse_sess, rhs);
}
2016-09-25 18:55:04 +02:00
// don't abort iteration early, so that errors for multiple lhses can be reported
for lhs in &lhses {
valid &= check_lhs_no_empty_seq(&sess.parse_sess, slice::from_ref(lhs));
2016-09-25 18:55:04 +02:00
}
valid &= macro_check::check_meta_variables(&sess.parse_sess, def.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));
2019-12-22 17:42:04 -05:00
}
Some(TransparencyError::MultipleTransparencyAttrs(old_span, new_span)) => {
diag.span_err(vec![old_span, new_span], "multiple macro transparency attributes");
2019-12-22 17:42:04 -05:00
}
None => {}
}
2022-04-17 04:01:17 +02:00
// Compute the spans of the macro rules
// We only take the span of the lhs here,
// so that the spans of created warnings are smaller.
let rule_spans = if def.id != DUMMY_NODE_ID {
lhses.iter().map(|lhs| lhs.span()).collect::<Vec<_>>()
} else {
Vec::new()
};
// Convert the lhses into `MatcherLoc` form, which is better for doing the
// actual matching. Unless the matcher is invalid.
let lhses = if valid {
lhses
.iter()
.map(|lhs| {
// Ignore the delimiters around the matcher.
match lhs {
mbe::TokenTree::Delimited(_, delimited) => {
mbe::macro_parser::compute_locs(&delimited.tts)
}
_ => sess.parse_sess.span_diagnostic.span_bug(def.span, "malformed macro lhs"),
}
})
.collect()
} else {
vec![]
};
2022-04-17 04:01:17 +02:00
let expander = Box::new(MacroRulesMacroExpander {
name: def.ident,
span: def.span,
2022-04-17 04:01:17 +02:00
node_id: def.id,
transparency,
lhses,
rhses,
valid,
2022-04-17 04:01:17 +02:00
});
(mk_syn_ext(expander), rule_spans)
}
fn check_lhs_nt_follows(sess: &ParseSess, def: &ast::Item, lhs: &mbe::TokenTree) -> bool {
// lhs is going to be like TokenTree::Delimited(...), where the
// entire lhs is those tts. Or, it can be a "bare sequence", not wrapped in parens.
if let mbe::TokenTree::Delimited(_, delimited) = lhs {
check_matcher(sess, def, &delimited.tts)
} else {
let msg = "invalid macro matcher; matchers must be contained in balanced delimiters";
sess.span_diagnostic.span_err(lhs.span(), msg);
false
}
// we don't abort on errors on rejection, the driver will do that for us
// after parsing/expansion. we can report every error in every macro this way.
}
2019-02-08 14:53:55 +01:00
/// Checks that the lhs contains no repetition which could match an empty token
2016-09-25 18:55:04 +02:00
/// tree, because then the matcher would hang indefinitely.
2019-09-22 19:17:30 +03:00
fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[mbe::TokenTree]) -> bool {
use mbe::TokenTree;
2016-09-25 18:55:04 +02:00
for tt in tts {
match *tt {
TokenTree::Token(..)
| TokenTree::MetaVar(..)
| TokenTree::MetaVarDecl(..)
| TokenTree::MetaVarExpr(..) => (),
2019-05-29 20:21:26 +02:00
TokenTree::Delimited(_, ref del) => {
if !check_lhs_no_empty_seq(sess, &del.tts) {
2019-05-29 20:21:26 +02:00
return false;
}
}
2016-09-25 18:55:04 +02:00
TokenTree::Sequence(span, ref seq) => {
2019-05-29 20:21:26 +02:00
if seq.separator.is_none()
&& seq.tts.iter().all(|seq_tt| match *seq_tt {
TokenTree::MetaVarDecl(_, _, Some(NonterminalKind::Vis)) => true,
2019-05-29 20:21:26 +02:00
TokenTree::Sequence(_, ref sub_seq) => {
2019-09-22 19:17:30 +03:00
sub_seq.kleene.op == mbe::KleeneOp::ZeroOrMore
|| sub_seq.kleene.op == mbe::KleeneOp::ZeroOrOne
2019-05-29 20:21:26 +02:00
}
2018-07-02 19:44:01 -05:00
_ => false,
2019-05-29 20:21:26 +02:00
})
{
let sp = span.entire();
sess.span_diagnostic.span_err(sp, "repetition matches empty token tree");
return false;
2016-09-25 18:55:04 +02:00
}
if !check_lhs_no_empty_seq(sess, &seq.tts) {
return false;
}
}
}
}
true
}
2019-09-22 19:17:30 +03:00
fn check_rhs(sess: &ParseSess, rhs: &mbe::TokenTree) -> bool {
match *rhs {
2019-09-22 19:17:30 +03:00
mbe::TokenTree::Delimited(..) => return true,
_ => {
sess.span_diagnostic.span_err(rhs.span(), "macro rhs must be delimited");
}
}
false
}
fn check_matcher(sess: &ParseSess, def: &ast::Item, 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, def, &first_sets, matcher, &empty_suffix);
err == sess.span_diagnostic.err_count()
}
// `The FirstSets` for a matcher is a mapping from subsequences in the
// matcher to the FIRST set for that subsequence.
//
// This mapping is partially precomputed via a backwards scan over the
// token trees of the matcher, which provides a mapping from each
// repetition sequence to its *first* set.
//
// (Hypothetically, sequences should be uniquely identifiable via their
// spans, though perhaps that is false, e.g., for macro-generated macros
// that do not try to inject artificial span information. My plan is
// to try to catch such cases ahead of time and not include them in
// the precomputed mapping.)
struct FirstSets<'tt> {
// this maps each TokenTree::Sequence `$(tt ...) SEP OP` that is uniquely identified by its
// span in the original matcher to the First set for the inner sequence `tt ...`.
//
// If two sequences have the same span in a matcher, then map that
// span to None (invalidating the mapping here and forcing the code to
// use a slow path).
first: FxHashMap<Span, Option<TokenSet<'tt>>>,
}
impl<'tt> FirstSets<'tt> {
fn new(tts: &'tt [mbe::TokenTree]) -> FirstSets<'tt> {
2019-09-22 19:17:30 +03:00
use mbe::TokenTree;
let mut sets = FirstSets { first: FxHashMap::default() };
build_recur(&mut sets, tts);
return sets;
// walks backward over `tts`, returning the FIRST for `tts`
// and updating `sets` at the same time for all sequence
// substructure we find within `tts`.
fn build_recur<'tt>(sets: &mut FirstSets<'tt>, tts: &'tt [TokenTree]) -> TokenSet<'tt> {
let mut first = TokenSet::empty();
for tt in tts.iter().rev() {
match *tt {
TokenTree::Token(..)
| TokenTree::MetaVar(..)
| TokenTree::MetaVarDecl(..)
| TokenTree::MetaVarExpr(..) => {
first.replace_with(TtHandle::TtRef(tt));
}
TokenTree::Delimited(span, ref delimited) => {
build_recur(sets, &delimited.tts);
first.replace_with(TtHandle::from_token_kind(
token::OpenDelim(delimited.delim),
span.open,
));
}
TokenTree::Sequence(sp, ref seq_rep) => {
2021-12-03 03:06:36 +01:00
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) => {
// if there is already an entry, then a span must have collided.
// This should not happen with typical macro_rules macros,
// but syntax extensions need not maintain distinct spans,
// so distinct syntax trees can be assigned the same span.
// In such a case, the map cannot be trusted; so mark this
// entry as unusable.
occ.insert(None);
}
}
// If the sequence contents can be empty, then the first
// token could be the separator token itself.
if let (Some(sep), true) = (&seq_rep.separator, subfirst.maybe_empty) {
first.add_one_maybe(TtHandle::from_token(sep.clone()));
}
// Reverse scan: Sequence comes before `first`.
2019-01-14 15:50:33 -06:00
if subfirst.maybe_empty
2019-09-22 19:17:30 +03:00
|| seq_rep.kleene.op == mbe::KleeneOp::ZeroOrMore
|| seq_rep.kleene.op == mbe::KleeneOp::ZeroOrOne
2019-01-14 15:50:33 -06:00
{
// If sequence is potentially empty, then
// union them (preserving first emptiness).
2018-07-02 19:44:01 -05:00
first.add_all(&TokenSet { maybe_empty: true, ..subfirst });
} else {
// Otherwise, sequence guaranteed
// non-empty; replace first.
first = subfirst;
}
}
}
}
first
}
}
// walks forward over `tts` until all potential FIRST tokens are
// identified.
fn first(&self, tts: &'tt [mbe::TokenTree]) -> TokenSet<'tt> {
2019-09-22 19:17:30 +03:00
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(..)
| TokenTree::MetaVarExpr(..) => {
first.add_one(TtHandle::TtRef(tt));
return first;
}
TokenTree::Delimited(span, ref delimited) => {
first.add_one(TtHandle::from_token_kind(
token::OpenDelim(delimited.delim),
span.open,
));
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) => {
2021-12-03 03:06:36 +01:00
subfirst_owned = self.first(&seq_rep.tts);
&subfirst_owned
}
None => {
panic!("We missed a sequence during FirstSets construction");
}
};
// If the sequence contents can be empty, then the first
// token could be the separator token itself.
if let (Some(sep), true) = (&seq_rep.separator, subfirst.maybe_empty) {
first.add_one_maybe(TtHandle::from_token(sep.clone()));
}
assert!(first.maybe_empty);
first.add_all(subfirst);
if subfirst.maybe_empty
2019-09-22 19:17:30 +03:00
|| seq_rep.kleene.op == mbe::KleeneOp::ZeroOrMore
|| seq_rep.kleene.op == mbe::KleeneOp::ZeroOrOne
{
// Continue scanning for more first
// tokens, but also make sure we
// restore empty-tracking state.
first.maybe_empty = true;
continue;
} else {
return first;
}
}
}
}
// we only exit the loop if `tts` was empty or if every
// element of `tts` matches the empty sequence.
assert!(first.maybe_empty);
first
}
}
// Most `mbe::TokenTree`s are pre-existing in the matcher, but some are defined
// implicitly, such as opening/closing delimiters and sequence repetition ops.
// This type encapsulates both kinds. It implements `Clone` while avoiding the
// need for `mbe::TokenTree` to implement `Clone`.
#[derive(Debug)]
enum TtHandle<'tt> {
/// This is used in most cases.
TtRef(&'tt mbe::TokenTree),
/// This is only used for implicit token trees. The `mbe::TokenTree` *must*
/// be `mbe::TokenTree::Token`. No other variants are allowed. We store an
/// `mbe::TokenTree` rather than a `Token` so that `get()` can return a
/// `&mbe::TokenTree`.
Token(mbe::TokenTree),
}
impl<'tt> TtHandle<'tt> {
fn from_token(tok: Token) -> Self {
TtHandle::Token(mbe::TokenTree::Token(tok))
}
fn from_token_kind(kind: TokenKind, span: Span) -> Self {
TtHandle::from_token(Token::new(kind, span))
}
// Get a reference to a token tree.
fn get(&'tt self) -> &'tt mbe::TokenTree {
match self {
TtHandle::TtRef(tt) => tt,
TtHandle::Token(token_tt) => &token_tt,
}
}
}
impl<'tt> PartialEq for TtHandle<'tt> {
fn eq(&self, other: &TtHandle<'tt>) -> bool {
self.get() == other.get()
}
}
impl<'tt> Clone for TtHandle<'tt> {
fn clone(&self) -> Self {
match self {
TtHandle::TtRef(tt) => TtHandle::TtRef(tt),
// This variant *must* contain a `mbe::TokenTree::Token`, and not
// any other variant of `mbe::TokenTree`.
TtHandle::Token(mbe::TokenTree::Token(tok)) => {
TtHandle::Token(mbe::TokenTree::Token(tok.clone()))
}
_ => unreachable!(),
}
}
}
2019-09-22 19:17:30 +03:00
// A set of `mbe::TokenTree`s, which may include `TokenTree::Match`s
2017-01-30 23:48:14 +00:00
// (for macro-by-example syntactic variables). It also carries the
// `maybe_empty` flag; that is true if and only if the matcher can
// match an empty token sequence.
//
// The First set is computed on submatchers like `$($a:expr b),* $(c)* d`,
// which has corresponding FIRST = {$a:expr, c, d}.
// Likewise, `$($a:expr b),* $(c)+ d` has FIRST = {$a:expr, c}.
//
// (Notably, we must allow for *-op to occur zero times.)
#[derive(Clone, Debug)]
struct TokenSet<'tt> {
tokens: Vec<TtHandle<'tt>>,
maybe_empty: bool,
}
impl<'tt> TokenSet<'tt> {
// Returns a set for the empty sequence.
2019-05-29 20:21:26 +02:00
fn empty() -> Self {
TokenSet { tokens: Vec::new(), maybe_empty: true }
}
// Returns the set `{ tok }` for the single-token (and thus
// non-empty) sequence [tok].
fn singleton(tt: TtHandle<'tt>) -> Self {
TokenSet { tokens: vec![tt], maybe_empty: false }
}
// Changes self to be the set `{ tok }`.
// Since `tok` is always present, marks self as non-empty.
fn replace_with(&mut self, tt: TtHandle<'tt>) {
self.tokens.clear();
self.tokens.push(tt);
self.maybe_empty = false;
}
// Changes self to be the empty set `{}`; meant for use when
// the particular token does not matter, but we want to
// record that it occurs.
fn replace_with_irrelevant(&mut self) {
self.tokens.clear();
self.maybe_empty = false;
}
// Adds `tok` to the set for `self`, marking sequence as non-empy.
fn add_one(&mut self, tt: TtHandle<'tt>) {
if !self.tokens.contains(&tt) {
self.tokens.push(tt);
}
self.maybe_empty = false;
}
// Adds `tok` to the set for `self`. (Leaves `maybe_empty` flag alone.)
fn add_one_maybe(&mut self, tt: TtHandle<'tt>) {
if !self.tokens.contains(&tt) {
self.tokens.push(tt);
}
}
// Adds all elements of `other` to this.
//
// (Since this is a set, we filter out duplicates.)
//
// If `other` is potentially empty, then preserves the previous
// setting of the empty flag of `self`. If `other` is guaranteed
// non-empty, then `self` is marked non-empty.
fn add_all(&mut self, other: &Self) {
for tt in &other.tokens {
if !self.tokens.contains(tt) {
self.tokens.push(tt.clone());
}
}
if !other.maybe_empty {
self.maybe_empty = false;
}
}
}
// Checks that `matcher` is internally consistent and that it
2019-07-24 23:21:25 +02:00
// can legally be followed by a token `N`, for all `N` in `follow`.
// (If `follow` is empty, then it imposes no constraint on
// the `matcher`.)
//
// Returns the set of NT tokens that could possibly come last in
// `matcher`. (If `matcher` matches the empty sequence, then
// `maybe_empty` will be set to true.)
//
// Requires that `first_sets` is pre-computed for `matcher`;
// see `FirstSets::new`.
fn check_matcher_core<'tt>(
2019-05-29 20:21:26 +02:00
sess: &ParseSess,
def: &ast::Item,
first_sets: &FirstSets<'tt>,
matcher: &'tt [mbe::TokenTree],
follow: &TokenSet<'tt>,
) -> TokenSet<'tt> {
2019-09-22 19:17:30 +03:00
use mbe::TokenTree;
let mut last = TokenSet::empty();
// 2. For each token and suffix [T, SUFFIX] in M:
// ensure that T can be followed by SUFFIX, and if SUFFIX may be empty,
// then ensure T can also be followed by any element of FOLLOW.
'each_token: for i in 0..matcher.len() {
let token = &matcher[i];
2019-05-29 20:21:26 +02:00
let suffix = &matcher[i + 1..];
let build_suffix_first = || {
let mut s = first_sets.first(suffix);
2019-05-29 20:21:26 +02:00
if s.maybe_empty {
s.add_all(follow);
}
s
};
// (we build `suffix_first` on demand below; you can tell
// which cases are supposed to fall through by looking for the
// initialization of this variable.)
let suffix_first;
// First, update `last` so that it corresponds to the set
// of NT tokens that might end the sequence `... token`.
match *token {
TokenTree::Token(..)
| TokenTree::MetaVar(..)
| TokenTree::MetaVarDecl(..)
| TokenTree::MetaVarExpr(..) => {
if token_can_be_followed_by_any(token) {
// don't need to track tokens that work with any,
last.replace_with_irrelevant();
// ... and don't need to check tokens that can be
// followed by anything against SUFFIX.
continue 'each_token;
} else {
last.replace_with(TtHandle::TtRef(token));
suffix_first = build_suffix_first();
}
}
TokenTree::Delimited(span, ref d) => {
let my_suffix = TokenSet::singleton(TtHandle::from_token_kind(
token::CloseDelim(d.delim),
span.close,
));
check_matcher_core(sess, def, first_sets, &d.tts, &my_suffix);
// don't track non NT tokens
last.replace_with_irrelevant();
// also, we don't need to check delimited sequences
// against SUFFIX
continue 'each_token;
}
TokenTree::Sequence(_, ref seq_rep) => {
suffix_first = build_suffix_first();
// The trick here: when we check the interior, we want
// to include the separator (if any) as a potential
// (but not guaranteed) element of FOLLOW. So in that
// case, we make a temp copy of suffix and stuff
// delimiter in there.
//
// FIXME: Should I first scan suffix_first to see if
// delimiter is already in it before I go through the
// work of cloning it? But then again, this way I may
// get a "tighter" span?
let mut new;
let my_suffix = if let Some(sep) = &seq_rep.separator {
new = suffix_first.clone();
new.add_one_maybe(TtHandle::from_token(sep.clone()));
&new
} else {
&suffix_first
};
// At this point, `suffix_first` is built, and
// `my_suffix` is some TokenSet that we can use
// for checking the interior of `seq_rep`.
let next = check_matcher_core(sess, def, first_sets, &seq_rep.tts, my_suffix);
if next.maybe_empty {
last.add_all(&next);
} else {
last = next;
}
// the recursive call to check_matcher_core already ran the 'each_last
// check below, so we can just keep going forward here.
continue 'each_token;
}
}
// (`suffix_first` guaranteed initialized once reaching here.)
// Now `last` holds the complete set of NT tokens that could
// end the sequence before SUFFIX. Check that every one works with `suffix`.
for tt in &last.tokens {
if let &TokenTree::MetaVarDecl(span, name, Some(kind)) = tt.get() {
2017-01-30 23:48:14 +00:00
for next_token in &suffix_first.tokens {
let next_token = next_token.get();
// Check if the old pat is used and the next token is `|`
// to warn about incompatibility with Rust 2021.
// We only emit this lint if we're parsing the original
// definition of this macro_rules, not while (re)parsing
// the macro when compiling another crate that is using the
// macro. (See #86567.)
// Macros defined in the current crate have a real node id,
// whereas macros from an external crate have a dummy id.
if def.id != DUMMY_NODE_ID
&& matches!(kind, NonterminalKind::PatParam { inferred: true })
&& matches!(next_token, TokenTree::Token(token) if token.kind == BinOp(token::BinOpToken::Or))
{
// It is suggestion to use pat_param, for example: $x:pat -> $x:pat_param.
let suggestion = quoted_tt_to_string(&TokenTree::MetaVarDecl(
span,
name,
Some(NonterminalKind::PatParam { inferred: false }),
));
sess.buffer_lint_with_diagnostic(
&RUST_2021_INCOMPATIBLE_OR_PATTERNS,
span,
ast::CRATE_NODE_ID,
"the meaning of the `pat` fragment specifier is changing in Rust 2021, which may affect this macro",
BuiltinLintDiagnostics::OrPatternsBackCompat(span, suggestion),
);
}
match is_in_follow(next_token, kind) {
IsInFollow::Yes => {}
IsInFollow::No(possible) => {
2019-05-29 20:21:26 +02:00
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,
2019-05-29 20:21:26 +02:00
&format!(
"`${name}:{frag}` {may_be} followed by `{next}`, which \
is not allowed for `{frag}` fragments",
name = name,
frag = kind,
2019-05-29 20:21:26 +02:00
next = quoted_tt_to_string(next_token),
may_be = may_be
),
);
err.span_label(sp, format!("not allowed after `{}` fragments", kind));
if kind == NonterminalKind::PatWithOr
2022-02-28 18:13:24 -05:00
&& sess.edition.rust_2021()
&& next_token.is_token(&BinOp(token::BinOpToken::Or))
{
let suggestion = quoted_tt_to_string(&TokenTree::MetaVarDecl(
span,
name,
Some(NonterminalKind::PatParam { inferred: false }),
));
err.span_suggestion(
span,
"try a `pat_param` fragment specifier instead",
suggestion,
Applicability::MaybeIncorrect,
);
}
let msg = "allowed there are: ";
match possible {
&[] => {}
&[t] => {
err.note(&format!(
"only {} is allowed after `{}` fragments",
t, kind,
));
}
ts => {
err.note(&format!(
"{}{} or {}",
msg,
2019-05-29 20:21:26 +02:00
ts[..ts.len() - 1]
.iter()
.copied()
2019-05-29 20:21:26 +02:00
.collect::<Vec<_>>()
.join(", "),
ts[ts.len() - 1],
));
}
}
err.emit();
}
}
}
}
}
}
last
}
2019-09-22 19:17:30 +03:00
fn token_can_be_followed_by_any(tok: &mbe::TokenTree) -> bool {
if let mbe::TokenTree::MetaVarDecl(_, _, Some(kind)) = *tok {
frag_can_be_followed_by_any(kind)
} else {
2020-03-06 12:13:55 +01:00
// (Non NT's can always be followed by anything in matchers.)
true
}
}
2019-02-08 14:53:55 +01:00
/// Returns `true` if a fragment of type `frag` can be followed by any sort of
/// token. We use this (among other things) as a useful approximation
/// for when `frag` can be followed by a repetition like `$(...)*` or
/// `$(...)+`. In general, these can be a bit tricky to reason about,
/// so we adopt a conservative position that says that any fragment
/// specifier which consumes at most one token tree can be followed by
/// a fragment specifier (indeed, these fragments can be followed by
/// ANYTHING without fear of future compatibility hazards).
fn frag_can_be_followed_by_any(kind: NonterminalKind) -> bool {
2020-10-26 20:02:06 -04:00
matches!(
kind,
NonterminalKind::Item // always terminated by `}` or `;`
| NonterminalKind::Block // exactly one token tree
| NonterminalKind::Ident // exactly one token tree
| NonterminalKind::Literal // exactly one token tree
| NonterminalKind::Meta // exactly one token tree
| NonterminalKind::Lifetime // exactly one token tree
2020-10-26 20:02:06 -04:00
| NonterminalKind::TT // exactly one token tree
)
}
enum IsInFollow {
Yes,
No(&'static [&'static str]),
}
2019-02-08 14:53:55 +01:00
/// Returns `true` if `frag` can legally be followed by the token `tok`. For
/// fragments that can consume an unbounded number of tokens, `tok`
/// must be within a well-defined follow set. This is intended to
/// guarantee future compatibility: for example, without this rule, if
/// we expanded `expr` to include a new binary operator, we might
/// break macros that were relying on that binary operator as a
/// separator.
2016-01-27 13:26:47 -05:00
// when changing this do not forget to update doc/book/macros.md!
fn is_in_follow(tok: &mbe::TokenTree, kind: NonterminalKind) -> IsInFollow {
2019-09-22 19:17:30 +03:00
use mbe::TokenTree;
2017-01-30 23:48:14 +00:00
if let TokenTree::Token(Token { kind: token::CloseDelim(_), .. }) = *tok {
// closing a token tree can never be matched by any fragment;
// iow, we always require that `(` and `)` match, etc.
IsInFollow::Yes
2015-01-19 22:43:15 +01:00
} else {
match kind {
NonterminalKind::Item => {
2015-01-19 22:43:15 +01:00
// since items *must* be followed by either a `;` or a `}`, we can
// accept anything after them
IsInFollow::Yes
2019-05-29 20:21:26 +02:00
}
NonterminalKind::Block => {
// anything can follow block, the braces provide an easy boundary to
2015-01-19 22:43:15 +01:00
// maintain
IsInFollow::Yes
2019-05-29 20:21:26 +02:00
}
NonterminalKind::Stmt | NonterminalKind::Expr => {
const TOKENS: &[&str] = &["`=>`", "`,`", "`;`"];
match tok {
TokenTree::Token(token) => match token.kind {
FatArrow | Comma | Semi => IsInFollow::Yes,
_ => IsInFollow::No(TOKENS),
},
_ => IsInFollow::No(TOKENS),
}
2019-05-29 20:21:26 +02:00
}
2021-04-14 20:34:51 -05:00
NonterminalKind::PatParam { .. } => {
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),
}
}
2021-04-27 21:15:59 -05:00
NonterminalKind::PatWithOr { .. } => {
const TOKENS: &[&str] = &["`=>`", "`,`", "`=`", "`if`", "`in`"];
match tok {
TokenTree::Token(token) => match token.kind {
FatArrow | Comma | Eq => IsInFollow::Yes,
Ident(name, false) if name == kw::If || name == kw::In => IsInFollow::Yes,
_ => IsInFollow::No(TOKENS),
},
_ => IsInFollow::No(TOKENS),
}
2019-05-29 20:21:26 +02:00
}
NonterminalKind::Path | NonterminalKind::Ty => {
const TOKENS: &[&str] = &[
2019-05-29 20:21:26 +02:00
"`{`", "`[`", "`=>`", "`,`", "`>`", "`=`", "`:`", "`;`", "`|`", "`as`",
"`where`",
];
match tok {
TokenTree::Token(token) => match token.kind {
OpenDelim(Delimiter::Brace)
| OpenDelim(Delimiter::Bracket)
2019-05-29 20:21:26 +02:00
| 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(_, _, Some(NonterminalKind::Block)) => IsInFollow::Yes,
_ => IsInFollow::No(TOKENS),
}
2019-05-29 20:21:26 +02:00
}
NonterminalKind::Ident | NonterminalKind::Lifetime => {
// being a single token, idents and lifetimes are harmless
IsInFollow::Yes
2019-05-29 20:21:26 +02:00
}
NonterminalKind::Literal => {
// literals may be of a single token, or two tokens (negative numbers)
IsInFollow::Yes
2019-05-29 20:21:26 +02:00
}
NonterminalKind::Meta | NonterminalKind::TT => {
2015-01-19 22:43:15 +01:00
// being either a single token or a delimited sequence, tt is
// harmless
IsInFollow::Yes
2019-05-29 20:21:26 +02:00
}
NonterminalKind::Vis => {
// Explicitly disallow `priv`, on the off chance it comes back.
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,
2019-05-29 20:21:26 +02:00
_ => {
if token.can_begin_type() {
IsInFollow::Yes
} else {
IsInFollow::No(TOKENS)
}
}
},
TokenTree::MetaVarDecl(
_,
_,
Some(NonterminalKind::Ident | NonterminalKind::Ty | NonterminalKind::Path),
) => IsInFollow::Yes,
_ => IsInFollow::No(TOKENS),
}
2019-05-29 20:21:26 +02:00
}
2015-01-19 22:43:15 +01:00
}
}
}
2019-09-22 19:17:30 +03:00
fn quoted_tt_to_string(tt: &mbe::TokenTree) -> String {
2017-01-30 23:48:14 +00:00
match *tt {
mbe::TokenTree::Token(ref token) => pprust::token_to_string(&token).into(),
2019-09-22 19:17:30 +03:00
mbe::TokenTree::MetaVar(_, name) => format!("${}", name),
mbe::TokenTree::MetaVarDecl(_, name, Some(kind)) => format!("${}:{}", name, kind),
mbe::TokenTree::MetaVarDecl(_, name, None) => format!("${}:", name),
2019-05-29 20:21:26 +02:00
_ => panic!(
"{}",
"unexpected mbe::TokenTree::{Sequence or Delimited} \
2019-05-29 20:21:26 +02:00
in follow set checker"
),
2017-01-30 23:48:14 +00:00
}
}
2019-09-22 19:41:04 +03:00
2020-03-09 10:35:35 +01:00
fn parser_from_cx(sess: &ParseSess, tts: TokenStream) -> Parser<'_> {
2020-03-08 13:36:20 +01:00
Parser::new(sess, tts, true, rustc_parse::MACRO_ARGUMENTS)
2019-09-22 19:41:04 +03:00
}
2019-09-25 10:17:59 +03:00
/// Generates an appropriate parsing failure message. For EOF, this is "unexpected end...". For
/// other tokens, this is "unexpected token...".
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),),
}
}