Rollup merge of #145208 - joshtriplett:mbe-derive, r=petrochenkov
Implement declarative (`macro_rules!`) derive macros (RFC 3698) This is a draft for review, and should not be merged yet. This is layered atop https://github.com/rust-lang/rust/pull/145153 , and has only two additional commits atop that. The first handles parsing and provides a test for various parse errors. The second implements expansion and handles application. This implements RFC 3698, "Declarative (`macro_rules!`) derive macros". Tracking issue: https://github.com/rust-lang/rust/issues/143549 This has one remaining issue, which I could use some help debugging: in `tests/ui/macros/macro-rules-derive-error.rs`, the diagnostics for `derive(fn_only)` (for a `fn_only` with no `derive` rules) and `derive(ForwardReferencedDerive)` both get emitted twice, as a duplicate diagnostic. From what I can tell via adding some debugging code, `unresolved_macro_suggestions` is getting called twice from `finalize_macro_resolutions` for each of them, because `self.single_segment_macro_resolutions` has two entries for the macro, with two different `parent_scope` values. I'm not clear on why that happened; it doesn't happen with the equivalent code using attrs. I'd welcome any suggestions for fixing this.
This commit is contained in:
@@ -70,7 +70,7 @@ expand_invalid_fragment_specifier =
|
|||||||
invalid fragment specifier `{$fragment}`
|
invalid fragment specifier `{$fragment}`
|
||||||
.help = {$help}
|
.help = {$help}
|
||||||
|
|
||||||
expand_macro_args_bad_delim = macro attribute argument matchers require parentheses
|
expand_macro_args_bad_delim = `{$rule_kw}` rule argument matchers require parentheses
|
||||||
expand_macro_args_bad_delim_sugg = the delimiters should be `(` and `)`
|
expand_macro_args_bad_delim_sugg = the delimiters should be `(` and `)`
|
||||||
|
|
||||||
expand_macro_body_stability =
|
expand_macro_body_stability =
|
||||||
|
|||||||
@@ -490,6 +490,7 @@ pub(crate) struct MacroArgsBadDelim {
|
|||||||
pub span: Span,
|
pub span: Span,
|
||||||
#[subdiagnostic]
|
#[subdiagnostic]
|
||||||
pub sugg: MacroArgsBadDelimSugg,
|
pub sugg: MacroArgsBadDelimSugg,
|
||||||
|
pub rule_kw: Symbol,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Subdiagnostic)]
|
#[derive(Subdiagnostic)]
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ use rustc_attr_parsing::{EvalConfigResult, ShouldEmit};
|
|||||||
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
|
use rustc_data_structures::flat_map_in_place::FlatMapInPlace;
|
||||||
use rustc_errors::PResult;
|
use rustc_errors::PResult;
|
||||||
use rustc_feature::Features;
|
use rustc_feature::Features;
|
||||||
|
use rustc_hir::def::MacroKinds;
|
||||||
use rustc_parse::parser::{
|
use rustc_parse::parser::{
|
||||||
AttemptLocalParseRecovery, CommaRecoveryMode, ForceCollect, Parser, RecoverColon, RecoverComma,
|
AttemptLocalParseRecovery, CommaRecoveryMode, ForceCollect, Parser, RecoverColon, RecoverComma,
|
||||||
token_descr,
|
token_descr,
|
||||||
@@ -565,6 +566,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
|||||||
.map(|DeriveResolution { path, item, exts: _, is_const }| {
|
.map(|DeriveResolution { path, item, exts: _, is_const }| {
|
||||||
// FIXME: Consider using the derive resolutions (`_exts`)
|
// FIXME: Consider using the derive resolutions (`_exts`)
|
||||||
// instead of enqueuing the derives to be resolved again later.
|
// instead of enqueuing the derives to be resolved again later.
|
||||||
|
// Note that this can result in duplicate diagnostics.
|
||||||
let expn_id = LocalExpnId::fresh_empty();
|
let expn_id = LocalExpnId::fresh_empty();
|
||||||
derive_invocations.push((
|
derive_invocations.push((
|
||||||
Invocation {
|
Invocation {
|
||||||
@@ -922,6 +924,35 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
|
|||||||
}
|
}
|
||||||
fragment
|
fragment
|
||||||
}
|
}
|
||||||
|
SyntaxExtensionKind::MacroRules(expander)
|
||||||
|
if expander.kinds().contains(MacroKinds::DERIVE) =>
|
||||||
|
{
|
||||||
|
if is_const {
|
||||||
|
let guar = self
|
||||||
|
.cx
|
||||||
|
.dcx()
|
||||||
|
.span_err(span, "macro `derive` does not support const derives");
|
||||||
|
return ExpandResult::Ready(fragment_kind.dummy(span, guar));
|
||||||
|
}
|
||||||
|
let body = item.to_tokens();
|
||||||
|
match expander.expand_derive(self.cx, span, &body) {
|
||||||
|
Ok(tok_result) => {
|
||||||
|
let fragment =
|
||||||
|
self.parse_ast_fragment(tok_result, fragment_kind, &path, span);
|
||||||
|
if macro_stats {
|
||||||
|
update_derive_macro_stats(
|
||||||
|
self.cx,
|
||||||
|
fragment_kind,
|
||||||
|
span,
|
||||||
|
&path,
|
||||||
|
&fragment,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
fragment
|
||||||
|
}
|
||||||
|
Err(guar) => return ExpandResult::Ready(fragment_kind.dummy(span, guar)),
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
InvocationKind::GlobDelegation { item, of_trait } => {
|
InvocationKind::GlobDelegation { item, of_trait } => {
|
||||||
|
|||||||
@@ -14,14 +14,22 @@ use super::macro_rules::{MacroRule, NoopTracker, parser_from_cx};
|
|||||||
use crate::expand::{AstFragmentKind, parse_ast_fragment};
|
use crate::expand::{AstFragmentKind, parse_ast_fragment};
|
||||||
use crate::mbe::macro_parser::ParseResult::*;
|
use crate::mbe::macro_parser::ParseResult::*;
|
||||||
use crate::mbe::macro_parser::{MatcherLoc, NamedParseResult, TtParser};
|
use crate::mbe::macro_parser::{MatcherLoc, NamedParseResult, TtParser};
|
||||||
use crate::mbe::macro_rules::{Tracker, try_match_macro, try_match_macro_attr};
|
use crate::mbe::macro_rules::{
|
||||||
|
Tracker, try_match_macro, try_match_macro_attr, try_match_macro_derive,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub(super) enum FailedMacro<'a> {
|
||||||
|
Func,
|
||||||
|
Attr(&'a TokenStream),
|
||||||
|
Derive,
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn failed_to_match_macro(
|
pub(super) fn failed_to_match_macro(
|
||||||
psess: &ParseSess,
|
psess: &ParseSess,
|
||||||
sp: Span,
|
sp: Span,
|
||||||
def_span: Span,
|
def_span: Span,
|
||||||
name: Ident,
|
name: Ident,
|
||||||
attr_args: Option<&TokenStream>,
|
args: FailedMacro<'_>,
|
||||||
body: &TokenStream,
|
body: &TokenStream,
|
||||||
rules: &[MacroRule],
|
rules: &[MacroRule],
|
||||||
) -> (Span, ErrorGuaranteed) {
|
) -> (Span, ErrorGuaranteed) {
|
||||||
@@ -36,10 +44,12 @@ pub(super) fn failed_to_match_macro(
|
|||||||
// diagnostics.
|
// diagnostics.
|
||||||
let mut tracker = CollectTrackerAndEmitter::new(psess.dcx(), sp);
|
let mut tracker = CollectTrackerAndEmitter::new(psess.dcx(), sp);
|
||||||
|
|
||||||
let try_success_result = if let Some(attr_args) = attr_args {
|
let try_success_result = match args {
|
||||||
|
FailedMacro::Func => try_match_macro(psess, name, body, rules, &mut tracker),
|
||||||
|
FailedMacro::Attr(attr_args) => {
|
||||||
try_match_macro_attr(psess, name, attr_args, body, rules, &mut tracker)
|
try_match_macro_attr(psess, name, attr_args, body, rules, &mut tracker)
|
||||||
} else {
|
}
|
||||||
try_match_macro(psess, name, body, rules, &mut tracker)
|
FailedMacro::Derive => try_match_macro_derive(psess, name, body, rules, &mut tracker),
|
||||||
};
|
};
|
||||||
|
|
||||||
if try_success_result.is_ok() {
|
if try_success_result.is_ok() {
|
||||||
@@ -90,7 +100,7 @@ pub(super) fn failed_to_match_macro(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check whether there's a missing comma in this macro call, like `println!("{}" a);`
|
// Check whether there's a missing comma in this macro call, like `println!("{}" a);`
|
||||||
if attr_args.is_none()
|
if let FailedMacro::Func = args
|
||||||
&& let Some((body, comma_span)) = body.add_comma()
|
&& let Some((body, comma_span)) = body.add_comma()
|
||||||
{
|
{
|
||||||
for rule in rules {
|
for rule in rules {
|
||||||
|
|||||||
@@ -27,10 +27,10 @@ use rustc_session::Session;
|
|||||||
use rustc_session::parse::{ParseSess, feature_err};
|
use rustc_session::parse::{ParseSess, feature_err};
|
||||||
use rustc_span::edition::Edition;
|
use rustc_span::edition::Edition;
|
||||||
use rustc_span::hygiene::Transparency;
|
use rustc_span::hygiene::Transparency;
|
||||||
use rustc_span::{Ident, Span, kw, sym};
|
use rustc_span::{Ident, Span, Symbol, kw, sym};
|
||||||
use tracing::{debug, instrument, trace, trace_span};
|
use tracing::{debug, instrument, trace, trace_span};
|
||||||
|
|
||||||
use super::diagnostics::failed_to_match_macro;
|
use super::diagnostics::{FailedMacro, failed_to_match_macro};
|
||||||
use super::macro_parser::{NamedMatches, NamedParseResult};
|
use super::macro_parser::{NamedMatches, NamedParseResult};
|
||||||
use super::{SequenceRepetition, diagnostics};
|
use super::{SequenceRepetition, diagnostics};
|
||||||
use crate::base::{
|
use crate::base::{
|
||||||
@@ -138,6 +138,8 @@ pub(super) enum MacroRule {
|
|||||||
body_span: Span,
|
body_span: Span,
|
||||||
rhs: mbe::TokenTree,
|
rhs: mbe::TokenTree,
|
||||||
},
|
},
|
||||||
|
/// A derive rule, for use with `#[m]`
|
||||||
|
Derive { body: Vec<MatcherLoc>, body_span: Span, rhs: mbe::TokenTree },
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MacroRulesMacroExpander {
|
pub struct MacroRulesMacroExpander {
|
||||||
@@ -157,6 +159,7 @@ impl MacroRulesMacroExpander {
|
|||||||
MacroRule::Attr { args_span, body_span, ref rhs, .. } => {
|
MacroRule::Attr { args_span, body_span, ref rhs, .. } => {
|
||||||
(MultiSpan::from_spans(vec![args_span, body_span]), rhs)
|
(MultiSpan::from_spans(vec![args_span, body_span]), rhs)
|
||||||
}
|
}
|
||||||
|
MacroRule::Derive { body_span, ref rhs, .. } => (MultiSpan::from_span(body_span), rhs),
|
||||||
};
|
};
|
||||||
if has_compile_error_macro(rhs) { None } else { Some((&self.name, span)) }
|
if has_compile_error_macro(rhs) { None } else { Some((&self.name, span)) }
|
||||||
}
|
}
|
||||||
@@ -164,6 +167,63 @@ impl MacroRulesMacroExpander {
|
|||||||
pub fn kinds(&self) -> MacroKinds {
|
pub fn kinds(&self) -> MacroKinds {
|
||||||
self.kinds
|
self.kinds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn expand_derive(
|
||||||
|
&self,
|
||||||
|
cx: &mut ExtCtxt<'_>,
|
||||||
|
sp: Span,
|
||||||
|
body: &TokenStream,
|
||||||
|
) -> Result<TokenStream, ErrorGuaranteed> {
|
||||||
|
// This is similar to `expand_macro`, but they have very different signatures, and will
|
||||||
|
// diverge further once derives support arguments.
|
||||||
|
let Self { name, ref rules, node_id, .. } = *self;
|
||||||
|
let psess = &cx.sess.psess;
|
||||||
|
|
||||||
|
if cx.trace_macros() {
|
||||||
|
let msg = format!("expanding `#[derive({name})] {}`", pprust::tts_to_string(body));
|
||||||
|
trace_macros_note(&mut cx.expansions, sp, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
match try_match_macro_derive(psess, name, body, rules, &mut NoopTracker) {
|
||||||
|
Ok((rule_index, rule, named_matches)) => {
|
||||||
|
let MacroRule::Derive { rhs, .. } = rule else {
|
||||||
|
panic!("try_match_macro_derive returned non-derive rule");
|
||||||
|
};
|
||||||
|
let mbe::TokenTree::Delimited(rhs_span, _, rhs) = rhs else {
|
||||||
|
cx.dcx().span_bug(sp, "malformed macro derive rhs");
|
||||||
|
};
|
||||||
|
|
||||||
|
let id = cx.current_expansion.id;
|
||||||
|
let tts = transcribe(psess, &named_matches, rhs, *rhs_span, self.transparency, id)
|
||||||
|
.map_err(|e| e.emit())?;
|
||||||
|
|
||||||
|
if cx.trace_macros() {
|
||||||
|
let msg = format!("to `{}`", pprust::tts_to_string(&tts));
|
||||||
|
trace_macros_note(&mut cx.expansions, sp, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if is_defined_in_current_crate(node_id) {
|
||||||
|
cx.resolver.record_macro_rule_usage(node_id, rule_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(tts)
|
||||||
|
}
|
||||||
|
Err(CanRetry::No(guar)) => Err(guar),
|
||||||
|
Err(CanRetry::Yes) => {
|
||||||
|
let (_, guar) = failed_to_match_macro(
|
||||||
|
cx.psess(),
|
||||||
|
sp,
|
||||||
|
self.span,
|
||||||
|
name,
|
||||||
|
FailedMacro::Derive,
|
||||||
|
body,
|
||||||
|
rules,
|
||||||
|
);
|
||||||
|
cx.macro_error_and_trace_macros_diag();
|
||||||
|
Err(guar)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TTMacroExpander for MacroRulesMacroExpander {
|
impl TTMacroExpander for MacroRulesMacroExpander {
|
||||||
@@ -325,8 +385,15 @@ fn expand_macro<'cx>(
|
|||||||
}
|
}
|
||||||
Err(CanRetry::Yes) => {
|
Err(CanRetry::Yes) => {
|
||||||
// Retry and emit a better error.
|
// Retry and emit a better error.
|
||||||
let (span, guar) =
|
let (span, guar) = failed_to_match_macro(
|
||||||
failed_to_match_macro(cx.psess(), sp, def_span, name, None, &arg, rules);
|
cx.psess(),
|
||||||
|
sp,
|
||||||
|
def_span,
|
||||||
|
name,
|
||||||
|
FailedMacro::Func,
|
||||||
|
&arg,
|
||||||
|
rules,
|
||||||
|
);
|
||||||
cx.macro_error_and_trace_macros_diag();
|
cx.macro_error_and_trace_macros_diag();
|
||||||
DummyResult::any(span, guar)
|
DummyResult::any(span, guar)
|
||||||
}
|
}
|
||||||
@@ -388,8 +455,15 @@ fn expand_macro_attr(
|
|||||||
Err(CanRetry::No(guar)) => Err(guar),
|
Err(CanRetry::No(guar)) => Err(guar),
|
||||||
Err(CanRetry::Yes) => {
|
Err(CanRetry::Yes) => {
|
||||||
// Retry and emit a better error.
|
// Retry and emit a better error.
|
||||||
let (_, guar) =
|
let (_, guar) = failed_to_match_macro(
|
||||||
failed_to_match_macro(cx.psess(), sp, def_span, name, Some(&args), &body, rules);
|
cx.psess(),
|
||||||
|
sp,
|
||||||
|
def_span,
|
||||||
|
name,
|
||||||
|
FailedMacro::Attr(&args),
|
||||||
|
&body,
|
||||||
|
rules,
|
||||||
|
);
|
||||||
cx.trace_macros_diag();
|
cx.trace_macros_diag();
|
||||||
Err(guar)
|
Err(guar)
|
||||||
}
|
}
|
||||||
@@ -536,6 +610,44 @@ pub(super) fn try_match_macro_attr<'matcher, T: Tracker<'matcher>>(
|
|||||||
Err(CanRetry::Yes)
|
Err(CanRetry::Yes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Try expanding the macro derive. Returns the index of the successful arm and its
|
||||||
|
/// named_matches if it was successful, and nothing if it failed. On failure, it's the caller's job
|
||||||
|
/// to use `track` accordingly to record all errors correctly.
|
||||||
|
#[instrument(level = "debug", skip(psess, body, rules, track), fields(tracking = %T::description()))]
|
||||||
|
pub(super) fn try_match_macro_derive<'matcher, T: Tracker<'matcher>>(
|
||||||
|
psess: &ParseSess,
|
||||||
|
name: Ident,
|
||||||
|
body: &TokenStream,
|
||||||
|
rules: &'matcher [MacroRule],
|
||||||
|
track: &mut T,
|
||||||
|
) -> Result<(usize, &'matcher MacroRule, NamedMatches), CanRetry> {
|
||||||
|
// This uses the same strategy as `try_match_macro`
|
||||||
|
let body_parser = parser_from_cx(psess, body.clone(), T::recovery());
|
||||||
|
let mut tt_parser = TtParser::new(name);
|
||||||
|
for (i, rule) in rules.iter().enumerate() {
|
||||||
|
let MacroRule::Derive { body, .. } = rule else { continue };
|
||||||
|
|
||||||
|
let mut gated_spans_snapshot = mem::take(&mut *psess.gated_spans.spans.borrow_mut());
|
||||||
|
|
||||||
|
let result = tt_parser.parse_tt(&mut Cow::Borrowed(&body_parser), body, track);
|
||||||
|
track.after_arm(true, &result);
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Success(named_matches) => {
|
||||||
|
psess.gated_spans.merge(gated_spans_snapshot);
|
||||||
|
return Ok((i, rule, named_matches));
|
||||||
|
}
|
||||||
|
Failure(_) => {
|
||||||
|
mem::swap(&mut gated_spans_snapshot, &mut psess.gated_spans.spans.borrow_mut())
|
||||||
|
}
|
||||||
|
Error(_, _) => return Err(CanRetry::Yes),
|
||||||
|
ErrorReported(guar) => return Err(CanRetry::No(guar)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(CanRetry::Yes)
|
||||||
|
}
|
||||||
|
|
||||||
/// Converts a macro item into a syntax extension.
|
/// Converts a macro item into a syntax extension.
|
||||||
pub fn compile_declarative_macro(
|
pub fn compile_declarative_macro(
|
||||||
sess: &Session,
|
sess: &Session,
|
||||||
@@ -569,7 +681,7 @@ pub fn compile_declarative_macro(
|
|||||||
let mut rules = Vec::new();
|
let mut rules = Vec::new();
|
||||||
|
|
||||||
while p.token != token::Eof {
|
while p.token != token::Eof {
|
||||||
let args = if p.eat_keyword_noexpect(sym::attr) {
|
let (args, is_derive) = if p.eat_keyword_noexpect(sym::attr) {
|
||||||
kinds |= MacroKinds::ATTR;
|
kinds |= MacroKinds::ATTR;
|
||||||
if !features.macro_attr() {
|
if !features.macro_attr() {
|
||||||
feature_err(sess, sym::macro_attr, span, "`macro_rules!` attributes are unstable")
|
feature_err(sess, sym::macro_attr, span, "`macro_rules!` attributes are unstable")
|
||||||
@@ -579,16 +691,46 @@ pub fn compile_declarative_macro(
|
|||||||
return dummy_syn_ext(guar);
|
return dummy_syn_ext(guar);
|
||||||
}
|
}
|
||||||
let args = p.parse_token_tree();
|
let args = p.parse_token_tree();
|
||||||
check_args_parens(sess, &args);
|
check_args_parens(sess, sym::attr, &args);
|
||||||
let args = parse_one_tt(args, RulePart::Pattern, sess, node_id, features, edition);
|
let args = parse_one_tt(args, RulePart::Pattern, sess, node_id, features, edition);
|
||||||
check_emission(check_lhs(sess, node_id, &args));
|
check_emission(check_lhs(sess, node_id, &args));
|
||||||
if let Some(guar) = check_no_eof(sess, &p, "expected macro attr body") {
|
if let Some(guar) = check_no_eof(sess, &p, "expected macro attr body") {
|
||||||
return dummy_syn_ext(guar);
|
return dummy_syn_ext(guar);
|
||||||
}
|
}
|
||||||
Some(args)
|
(Some(args), false)
|
||||||
|
} else if p.eat_keyword_noexpect(sym::derive) {
|
||||||
|
kinds |= MacroKinds::DERIVE;
|
||||||
|
let derive_keyword_span = p.prev_token.span;
|
||||||
|
if !features.macro_derive() {
|
||||||
|
feature_err(sess, sym::macro_attr, span, "`macro_rules!` derives are unstable")
|
||||||
|
.emit();
|
||||||
|
}
|
||||||
|
if let Some(guar) = check_no_eof(sess, &p, "expected `()` after `derive`") {
|
||||||
|
return dummy_syn_ext(guar);
|
||||||
|
}
|
||||||
|
let args = p.parse_token_tree();
|
||||||
|
check_args_parens(sess, sym::derive, &args);
|
||||||
|
let args_empty_result = check_args_empty(sess, &args);
|
||||||
|
let args_not_empty = args_empty_result.is_err();
|
||||||
|
check_emission(args_empty_result);
|
||||||
|
if let Some(guar) = check_no_eof(sess, &p, "expected macro derive body") {
|
||||||
|
return dummy_syn_ext(guar);
|
||||||
|
}
|
||||||
|
// If the user has `=>` right after the `()`, they might have forgotten the empty
|
||||||
|
// parentheses.
|
||||||
|
if p.token == token::FatArrow {
|
||||||
|
let mut err = sess
|
||||||
|
.dcx()
|
||||||
|
.struct_span_err(p.token.span, "expected macro derive body, got `=>`");
|
||||||
|
if args_not_empty {
|
||||||
|
err.span_label(derive_keyword_span, "need `()` after this `derive`");
|
||||||
|
}
|
||||||
|
return dummy_syn_ext(err.emit());
|
||||||
|
}
|
||||||
|
(None, true)
|
||||||
} else {
|
} else {
|
||||||
kinds |= MacroKinds::BANG;
|
kinds |= MacroKinds::BANG;
|
||||||
None
|
(None, false)
|
||||||
};
|
};
|
||||||
let lhs_tt = p.parse_token_tree();
|
let lhs_tt = p.parse_token_tree();
|
||||||
let lhs_tt = parse_one_tt(lhs_tt, RulePart::Pattern, sess, node_id, features, edition);
|
let lhs_tt = parse_one_tt(lhs_tt, RulePart::Pattern, sess, node_id, features, edition);
|
||||||
@@ -619,6 +761,8 @@ pub fn compile_declarative_macro(
|
|||||||
let args = mbe::macro_parser::compute_locs(&delimited.tts);
|
let args = mbe::macro_parser::compute_locs(&delimited.tts);
|
||||||
let body_span = lhs_span;
|
let body_span = lhs_span;
|
||||||
rules.push(MacroRule::Attr { args, args_span, body: lhs, body_span, rhs: rhs_tt });
|
rules.push(MacroRule::Attr { args, args_span, body: lhs, body_span, rhs: rhs_tt });
|
||||||
|
} else if is_derive {
|
||||||
|
rules.push(MacroRule::Derive { body: lhs, body_span: lhs_span, rhs: rhs_tt });
|
||||||
} else {
|
} else {
|
||||||
rules.push(MacroRule::Func { lhs, lhs_span, rhs: rhs_tt });
|
rules.push(MacroRule::Func { lhs, lhs_span, rhs: rhs_tt });
|
||||||
}
|
}
|
||||||
@@ -665,7 +809,7 @@ fn check_no_eof(sess: &Session, p: &Parser<'_>, msg: &'static str) -> Option<Err
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_args_parens(sess: &Session, args: &tokenstream::TokenTree) {
|
fn check_args_parens(sess: &Session, rule_kw: Symbol, args: &tokenstream::TokenTree) {
|
||||||
// This does not handle the non-delimited case; that gets handled separately by `check_lhs`.
|
// This does not handle the non-delimited case; that gets handled separately by `check_lhs`.
|
||||||
if let tokenstream::TokenTree::Delimited(dspan, _, delim, _) = args
|
if let tokenstream::TokenTree::Delimited(dspan, _, delim, _) = args
|
||||||
&& *delim != Delimiter::Parenthesis
|
&& *delim != Delimiter::Parenthesis
|
||||||
@@ -673,10 +817,21 @@ fn check_args_parens(sess: &Session, args: &tokenstream::TokenTree) {
|
|||||||
sess.dcx().emit_err(errors::MacroArgsBadDelim {
|
sess.dcx().emit_err(errors::MacroArgsBadDelim {
|
||||||
span: dspan.entire(),
|
span: dspan.entire(),
|
||||||
sugg: errors::MacroArgsBadDelimSugg { open: dspan.open, close: dspan.close },
|
sugg: errors::MacroArgsBadDelimSugg { open: dspan.open, close: dspan.close },
|
||||||
|
rule_kw,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_args_empty(sess: &Session, args: &tokenstream::TokenTree) -> Result<(), ErrorGuaranteed> {
|
||||||
|
match args {
|
||||||
|
tokenstream::TokenTree::Delimited(.., delimited) if delimited.is_empty() => Ok(()),
|
||||||
|
_ => {
|
||||||
|
let msg = "`derive` rules do not accept arguments; `derive` must be followed by `()`";
|
||||||
|
Err(sess.dcx().span_err(args.span(), msg))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn check_lhs(sess: &Session, node_id: NodeId, lhs: &mbe::TokenTree) -> Result<(), ErrorGuaranteed> {
|
fn check_lhs(sess: &Session, node_id: NodeId, lhs: &mbe::TokenTree) -> Result<(), ErrorGuaranteed> {
|
||||||
let e1 = check_lhs_nt_follows(sess, node_id, lhs);
|
let e1 = check_lhs_nt_follows(sess, node_id, lhs);
|
||||||
let e2 = check_lhs_no_empty_seq(sess, slice::from_ref(lhs));
|
let e2 = check_lhs_no_empty_seq(sess, slice::from_ref(lhs));
|
||||||
|
|||||||
@@ -556,6 +556,8 @@ declare_features! (
|
|||||||
(incomplete, loop_match, "1.90.0", Some(132306)),
|
(incomplete, loop_match, "1.90.0", Some(132306)),
|
||||||
/// Allow `macro_rules!` attribute rules
|
/// Allow `macro_rules!` attribute rules
|
||||||
(unstable, macro_attr, "CURRENT_RUSTC_VERSION", Some(83527)),
|
(unstable, macro_attr, "CURRENT_RUSTC_VERSION", Some(83527)),
|
||||||
|
/// Allow `macro_rules!` derive rules
|
||||||
|
(unstable, macro_derive, "CURRENT_RUSTC_VERSION", Some(143549)),
|
||||||
/// Give access to additional metadata about declarative macro meta-variables.
|
/// Give access to additional metadata about declarative macro meta-variables.
|
||||||
(unstable, macro_metavar_expr, "1.61.0", Some(83527)),
|
(unstable, macro_metavar_expr, "1.61.0", Some(83527)),
|
||||||
/// Provides a way to concatenate identifiers using metavariable expressions.
|
/// Provides a way to concatenate identifiers using metavariable expressions.
|
||||||
|
|||||||
@@ -249,7 +249,7 @@ resolve_macro_cannot_use_as_attr =
|
|||||||
`{$ident}` exists, but has no `attr` rules
|
`{$ident}` exists, but has no `attr` rules
|
||||||
|
|
||||||
resolve_macro_cannot_use_as_derive =
|
resolve_macro_cannot_use_as_derive =
|
||||||
`{$ident}` exists, but a declarative macro cannot be used as a derive macro
|
`{$ident}` exists, but has no `derive` rules
|
||||||
|
|
||||||
resolve_macro_defined_later =
|
resolve_macro_defined_later =
|
||||||
a macro with the same name exists, but it appears later
|
a macro with the same name exists, but it appears later
|
||||||
|
|||||||
@@ -1329,6 +1329,7 @@ symbols! {
|
|||||||
macro_attr,
|
macro_attr,
|
||||||
macro_attributes_in_derive_output,
|
macro_attributes_in_derive_output,
|
||||||
macro_concat,
|
macro_concat,
|
||||||
|
macro_derive,
|
||||||
macro_escape,
|
macro_escape,
|
||||||
macro_export,
|
macro_export,
|
||||||
macro_lifetime_matcher,
|
macro_lifetime_matcher,
|
||||||
|
|||||||
4
tests/ui/feature-gates/feature-gate-macro-derive.rs
Normal file
4
tests/ui/feature-gates/feature-gate-macro-derive.rs
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#![crate_type = "lib"]
|
||||||
|
|
||||||
|
macro_rules! MyDerive { derive() {} => {} }
|
||||||
|
//~^ ERROR `macro_rules!` derives are unstable
|
||||||
13
tests/ui/feature-gates/feature-gate-macro-derive.stderr
Normal file
13
tests/ui/feature-gates/feature-gate-macro-derive.stderr
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
error[E0658]: `macro_rules!` derives are unstable
|
||||||
|
--> $DIR/feature-gate-macro-derive.rs:3:1
|
||||||
|
|
|
||||||
|
LL | macro_rules! MyDerive { derive() {} => {} }
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: see issue #83527 <https://github.com/rust-lang/rust/issues/83527> for more information
|
||||||
|
= help: add `#![feature(macro_attr)]` to the crate attributes to enable
|
||||||
|
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0658`.
|
||||||
@@ -2,7 +2,7 @@ error: cannot find derive macro `sample` in this scope
|
|||||||
--> $DIR/macro-rules-as-derive-or-attr-issue-132928.rs:6:10
|
--> $DIR/macro-rules-as-derive-or-attr-issue-132928.rs:6:10
|
||||||
|
|
|
|
||||||
LL | macro_rules! sample { () => {} }
|
LL | macro_rules! sample { () => {} }
|
||||||
| ------ `sample` exists, but a declarative macro cannot be used as a derive macro
|
| ------ `sample` exists, but has no `derive` rules
|
||||||
...
|
...
|
||||||
LL | #[derive(sample)]
|
LL | #[derive(sample)]
|
||||||
| ^^^^^^
|
| ^^^^^^
|
||||||
@@ -20,7 +20,7 @@ error: cannot find derive macro `sample` in this scope
|
|||||||
--> $DIR/macro-rules-as-derive-or-attr-issue-132928.rs:6:10
|
--> $DIR/macro-rules-as-derive-or-attr-issue-132928.rs:6:10
|
||||||
|
|
|
|
||||||
LL | macro_rules! sample { () => {} }
|
LL | macro_rules! sample { () => {} }
|
||||||
| ------ `sample` exists, but a declarative macro cannot be used as a derive macro
|
| ------ `sample` exists, but has no `derive` rules
|
||||||
...
|
...
|
||||||
LL | #[derive(sample)]
|
LL | #[derive(sample)]
|
||||||
| ^^^^^^
|
| ^^^^^^
|
||||||
@@ -31,7 +31,7 @@ error: cannot find derive macro `sample` in this scope
|
|||||||
--> $DIR/macro-rules-as-derive-or-attr-issue-132928.rs:6:10
|
--> $DIR/macro-rules-as-derive-or-attr-issue-132928.rs:6:10
|
||||||
|
|
|
|
||||||
LL | macro_rules! sample { () => {} }
|
LL | macro_rules! sample { () => {} }
|
||||||
| ------ `sample` exists, but a declarative macro cannot be used as a derive macro
|
| ------ `sample` exists, but has no `derive` rules
|
||||||
...
|
...
|
||||||
LL | #[derive(sample)]
|
LL | #[derive(sample)]
|
||||||
| ^^^^^^
|
| ^^^^^^
|
||||||
|
|||||||
51
tests/ui/macros/macro-rules-derive-error.rs
Normal file
51
tests/ui/macros/macro-rules-derive-error.rs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
#![feature(macro_derive)]
|
||||||
|
|
||||||
|
macro_rules! MyDerive {
|
||||||
|
derive() { $($body:tt)* } => {
|
||||||
|
compile_error!(concat!("MyDerive: ", stringify!($($body)*)));
|
||||||
|
};
|
||||||
|
//~^^ ERROR: MyDerive
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! fn_only {
|
||||||
|
//~^ NOTE: `fn_only` exists, but has no `derive` rules
|
||||||
|
//~| NOTE: `fn_only` exists, but has no `derive` rules
|
||||||
|
{} => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
//~v NOTE: `DeriveOnly` exists, but has no rules for function-like invocation
|
||||||
|
macro_rules! DeriveOnly {
|
||||||
|
derive() {} => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
//~v NOTE: in this expansion of #[derive(MyDerive)]
|
||||||
|
#[derive(MyDerive)]
|
||||||
|
struct S1;
|
||||||
|
|
||||||
|
//~vv ERROR: cannot find macro `MyDerive` in this scope
|
||||||
|
//~| NOTE: `MyDerive` is in scope, but it is a derive
|
||||||
|
MyDerive!(arg);
|
||||||
|
|
||||||
|
#[derive(fn_only)]
|
||||||
|
struct S2;
|
||||||
|
//~^^ ERROR: cannot find derive macro `fn_only` in this scope
|
||||||
|
//~| ERROR: cannot find derive macro `fn_only` in this scope
|
||||||
|
//~| NOTE: duplicate diagnostic emitted
|
||||||
|
|
||||||
|
DeriveOnly!(); //~ ERROR: cannot find macro `DeriveOnly` in this scope
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(ForwardReferencedDerive)]
|
||||||
|
struct S;
|
||||||
|
//~^^ ERROR: cannot find derive macro `ForwardReferencedDerive` in this scope
|
||||||
|
//~| NOTE: consider moving the definition of `ForwardReferencedDerive` before this call
|
||||||
|
//~| ERROR: cannot find derive macro `ForwardReferencedDerive` in this scope
|
||||||
|
//~| NOTE: consider moving the definition of `ForwardReferencedDerive` before this call
|
||||||
|
//~| NOTE: duplicate diagnostic emitted
|
||||||
|
|
||||||
|
macro_rules! ForwardReferencedDerive {
|
||||||
|
//~^ NOTE: a macro with the same name exists, but it appears later
|
||||||
|
//~| NOTE: a macro with the same name exists, but it appears later
|
||||||
|
derive() {} => {}
|
||||||
|
}
|
||||||
75
tests/ui/macros/macro-rules-derive-error.stderr
Normal file
75
tests/ui/macros/macro-rules-derive-error.stderr
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
error: MyDerive: struct S1;
|
||||||
|
--> $DIR/macro-rules-derive-error.rs:5:9
|
||||||
|
|
|
||||||
|
LL | compile_error!(concat!("MyDerive: ", stringify!($($body)*)));
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
...
|
||||||
|
LL | #[derive(MyDerive)]
|
||||||
|
| -------- in this derive macro expansion
|
||||||
|
|
|
||||||
|
= note: this error originates in the derive macro `MyDerive` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error: cannot find macro `MyDerive` in this scope
|
||||||
|
--> $DIR/macro-rules-derive-error.rs:28:5
|
||||||
|
|
|
||||||
|
LL | MyDerive!(arg);
|
||||||
|
| ^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: `MyDerive` is in scope, but it is a derive macro: `#[derive(MyDerive)]`
|
||||||
|
|
||||||
|
error: cannot find derive macro `fn_only` in this scope
|
||||||
|
--> $DIR/macro-rules-derive-error.rs:30:14
|
||||||
|
|
|
||||||
|
LL | macro_rules! fn_only {
|
||||||
|
| ------- `fn_only` exists, but has no `derive` rules
|
||||||
|
...
|
||||||
|
LL | #[derive(fn_only)]
|
||||||
|
| ^^^^^^^
|
||||||
|
|
||||||
|
error: cannot find derive macro `fn_only` in this scope
|
||||||
|
--> $DIR/macro-rules-derive-error.rs:30:14
|
||||||
|
|
|
||||||
|
LL | macro_rules! fn_only {
|
||||||
|
| ------- `fn_only` exists, but has no `derive` rules
|
||||||
|
...
|
||||||
|
LL | #[derive(fn_only)]
|
||||||
|
| ^^^^^^^
|
||||||
|
|
|
||||||
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
|
|
||||||
|
error: cannot find macro `DeriveOnly` in this scope
|
||||||
|
--> $DIR/macro-rules-derive-error.rs:36:5
|
||||||
|
|
|
||||||
|
LL | macro_rules! DeriveOnly {
|
||||||
|
| ---------- `DeriveOnly` exists, but has no rules for function-like invocation
|
||||||
|
...
|
||||||
|
LL | DeriveOnly!();
|
||||||
|
| ^^^^^^^^^^
|
||||||
|
|
||||||
|
error: cannot find derive macro `ForwardReferencedDerive` in this scope
|
||||||
|
--> $DIR/macro-rules-derive-error.rs:39:10
|
||||||
|
|
|
||||||
|
LL | #[derive(ForwardReferencedDerive)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^ consider moving the definition of `ForwardReferencedDerive` before this call
|
||||||
|
|
|
||||||
|
note: a macro with the same name exists, but it appears later
|
||||||
|
--> $DIR/macro-rules-derive-error.rs:47:14
|
||||||
|
|
|
||||||
|
LL | macro_rules! ForwardReferencedDerive {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: cannot find derive macro `ForwardReferencedDerive` in this scope
|
||||||
|
--> $DIR/macro-rules-derive-error.rs:39:10
|
||||||
|
|
|
||||||
|
LL | #[derive(ForwardReferencedDerive)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^ consider moving the definition of `ForwardReferencedDerive` before this call
|
||||||
|
|
|
||||||
|
note: a macro with the same name exists, but it appears later
|
||||||
|
--> $DIR/macro-rules-derive-error.rs:47:14
|
||||||
|
|
|
||||||
|
LL | macro_rules! ForwardReferencedDerive {
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||||
|
|
||||||
|
error: aborting due to 7 previous errors
|
||||||
|
|
||||||
71
tests/ui/macros/macro-rules-derive.rs
Normal file
71
tests/ui/macros/macro-rules-derive.rs
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
//@ run-pass
|
||||||
|
//@ check-run-results
|
||||||
|
#![feature(macro_derive)]
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! MyExportedDerive {
|
||||||
|
derive() { $($body:tt)* } => {
|
||||||
|
println!("MyExportedDerive: body={:?}", stringify!($($body)*));
|
||||||
|
};
|
||||||
|
{ $($args:tt)* } => {
|
||||||
|
println!("MyExportedDerive!({:?})", stringify!($($args)*));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! MyLocalDerive {
|
||||||
|
derive() { $($body:tt)* } => {
|
||||||
|
println!("MyLocalDerive: body={:?}", stringify!($($body)*));
|
||||||
|
};
|
||||||
|
{ $($args:tt)* } => {
|
||||||
|
println!("MyLocalDerive!({:?})", stringify!($($args)*));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
trait MyTrait {
|
||||||
|
fn name() -> &'static str;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! MyTrait {
|
||||||
|
derive() { struct $name:ident; } => {
|
||||||
|
impl MyTrait for $name {
|
||||||
|
fn name() -> &'static str {
|
||||||
|
stringify!($name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(MyTrait)]
|
||||||
|
struct MyGlobalType;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
#[derive(crate::MyExportedDerive)]
|
||||||
|
struct _S1;
|
||||||
|
#[derive(crate::MyExportedDerive, crate::MyExportedDerive)]
|
||||||
|
struct _Twice1;
|
||||||
|
|
||||||
|
crate::MyExportedDerive!();
|
||||||
|
crate::MyExportedDerive!(invoked, arguments);
|
||||||
|
|
||||||
|
#[derive(MyExportedDerive)]
|
||||||
|
struct _S2;
|
||||||
|
#[derive(MyExportedDerive, MyExportedDerive)]
|
||||||
|
struct _Twice2;
|
||||||
|
|
||||||
|
MyExportedDerive!();
|
||||||
|
MyExportedDerive!(invoked, arguments);
|
||||||
|
|
||||||
|
#[derive(MyLocalDerive)]
|
||||||
|
struct _S3;
|
||||||
|
#[derive(MyLocalDerive, MyLocalDerive)]
|
||||||
|
struct _Twice3;
|
||||||
|
|
||||||
|
MyLocalDerive!();
|
||||||
|
MyLocalDerive!(invoked, arguments);
|
||||||
|
|
||||||
|
#[derive(MyTrait)]
|
||||||
|
struct MyLocalType;
|
||||||
|
|
||||||
|
println!("MyGlobalType::name(): {}", MyGlobalType::name());
|
||||||
|
println!("MyLocalType::name(): {}", MyLocalType::name());
|
||||||
|
}
|
||||||
17
tests/ui/macros/macro-rules-derive.run.stdout
Normal file
17
tests/ui/macros/macro-rules-derive.run.stdout
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
MyExportedDerive: body="struct _S1;"
|
||||||
|
MyExportedDerive: body="struct _Twice1;"
|
||||||
|
MyExportedDerive: body="struct _Twice1;"
|
||||||
|
MyExportedDerive!("")
|
||||||
|
MyExportedDerive!("invoked, arguments")
|
||||||
|
MyExportedDerive: body="struct _S2;"
|
||||||
|
MyExportedDerive: body="struct _Twice2;"
|
||||||
|
MyExportedDerive: body="struct _Twice2;"
|
||||||
|
MyExportedDerive!("")
|
||||||
|
MyExportedDerive!("invoked, arguments")
|
||||||
|
MyLocalDerive: body="struct _S3;"
|
||||||
|
MyLocalDerive: body="struct _Twice3;"
|
||||||
|
MyLocalDerive: body="struct _Twice3;"
|
||||||
|
MyLocalDerive!("")
|
||||||
|
MyLocalDerive!("invoked, arguments")
|
||||||
|
MyGlobalType::name(): MyGlobalType
|
||||||
|
MyLocalType::name(): MyLocalType
|
||||||
@@ -14,10 +14,10 @@ macro_rules! attr_incomplete_4 { attr() {} => }
|
|||||||
//~^ ERROR macro definition ended unexpectedly
|
//~^ ERROR macro definition ended unexpectedly
|
||||||
|
|
||||||
macro_rules! attr_noparens_1 { attr{} {} => {} }
|
macro_rules! attr_noparens_1 { attr{} {} => {} }
|
||||||
//~^ ERROR macro attribute argument matchers require parentheses
|
//~^ ERROR `attr` rule argument matchers require parentheses
|
||||||
|
|
||||||
macro_rules! attr_noparens_2 { attr[] {} => {} }
|
macro_rules! attr_noparens_2 { attr[] {} => {} }
|
||||||
//~^ ERROR macro attribute argument matchers require parentheses
|
//~^ ERROR `attr` rule argument matchers require parentheses
|
||||||
|
|
||||||
macro_rules! attr_noparens_3 { attr _ {} => {} }
|
macro_rules! attr_noparens_3 { attr _ {} => {} }
|
||||||
//~^ ERROR invalid macro matcher
|
//~^ ERROR invalid macro matcher
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ error: macro definition ended unexpectedly
|
|||||||
LL | macro_rules! attr_incomplete_4 { attr() {} => }
|
LL | macro_rules! attr_incomplete_4 { attr() {} => }
|
||||||
| ^ expected right-hand side of macro rule
|
| ^ expected right-hand side of macro rule
|
||||||
|
|
||||||
error: macro attribute argument matchers require parentheses
|
error: `attr` rule argument matchers require parentheses
|
||||||
--> $DIR/macro-attr-bad.rs:16:36
|
--> $DIR/macro-attr-bad.rs:16:36
|
||||||
|
|
|
|
||||||
LL | macro_rules! attr_noparens_1 { attr{} {} => {} }
|
LL | macro_rules! attr_noparens_1 { attr{} {} => {} }
|
||||||
@@ -34,7 +34,7 @@ LL - macro_rules! attr_noparens_1 { attr{} {} => {} }
|
|||||||
LL + macro_rules! attr_noparens_1 { attr() {} => {} }
|
LL + macro_rules! attr_noparens_1 { attr() {} => {} }
|
||||||
|
|
|
|
||||||
|
|
||||||
error: macro attribute argument matchers require parentheses
|
error: `attr` rule argument matchers require parentheses
|
||||||
--> $DIR/macro-attr-bad.rs:19:36
|
--> $DIR/macro-attr-bad.rs:19:36
|
||||||
|
|
|
|
||||||
LL | macro_rules! attr_noparens_2 { attr[] {} => {} }
|
LL | macro_rules! attr_noparens_2 { attr[] {} => {} }
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
macro_rules! attr {
|
macro_rules! attr {
|
||||||
attr[$($args:tt)*] { $($body:tt)* } => {
|
attr[$($args:tt)*] { $($body:tt)* } => {
|
||||||
//~^ ERROR: macro attribute argument matchers require parentheses
|
//~^ ERROR: `attr` rule argument matchers require parentheses
|
||||||
//~v ERROR: attr:
|
//~v ERROR: attr:
|
||||||
compile_error!(concat!(
|
compile_error!(concat!(
|
||||||
"attr: args=\"",
|
"attr: args=\"",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
error: macro attribute argument matchers require parentheses
|
error: `attr` rule argument matchers require parentheses
|
||||||
--> $DIR/macro-attr-recovery.rs:5:9
|
--> $DIR/macro-attr-recovery.rs:5:9
|
||||||
|
|
|
|
||||||
LL | attr[$($args:tt)*] { $($body:tt)* } => {
|
LL | attr[$($args:tt)*] { $($body:tt)* } => {
|
||||||
|
|||||||
43
tests/ui/parser/macro/macro-derive-bad.rs
Normal file
43
tests/ui/parser/macro/macro-derive-bad.rs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#![crate_type = "lib"]
|
||||||
|
#![feature(macro_derive)]
|
||||||
|
|
||||||
|
macro_rules! derive_incomplete_1 { derive }
|
||||||
|
//~^ ERROR macro definition ended unexpectedly
|
||||||
|
//~| NOTE expected `()` after `derive`
|
||||||
|
|
||||||
|
macro_rules! derive_incomplete_2 { derive() }
|
||||||
|
//~^ ERROR macro definition ended unexpectedly
|
||||||
|
//~| NOTE expected macro derive body
|
||||||
|
|
||||||
|
macro_rules! derive_incomplete_3 { derive() {} }
|
||||||
|
//~^ ERROR expected `=>`
|
||||||
|
//~| NOTE expected `=>`
|
||||||
|
|
||||||
|
macro_rules! derive_incomplete_4 { derive() {} => }
|
||||||
|
//~^ ERROR macro definition ended unexpectedly
|
||||||
|
//~| NOTE expected right-hand side of macro rule
|
||||||
|
|
||||||
|
macro_rules! derive_noparens_1 { derive{} {} => {} }
|
||||||
|
//~^ ERROR `derive` rule argument matchers require parentheses
|
||||||
|
|
||||||
|
macro_rules! derive_noparens_2 { derive[] {} => {} }
|
||||||
|
//~^ ERROR `derive` rule argument matchers require parentheses
|
||||||
|
|
||||||
|
macro_rules! derive_noparens_3 { derive _ {} => {} }
|
||||||
|
//~^ ERROR `derive` must be followed by `()`
|
||||||
|
|
||||||
|
macro_rules! derive_args_1 { derive($x:ident) ($y:ident) => {} }
|
||||||
|
//~^ ERROR `derive` rules do not accept arguments
|
||||||
|
|
||||||
|
macro_rules! derive_args_2 { derive() => {} }
|
||||||
|
//~^ ERROR expected macro derive body, got `=>`
|
||||||
|
|
||||||
|
macro_rules! derive_args_3 { derive($x:ident) => {} }
|
||||||
|
//~^ ERROR `derive` rules do not accept arguments
|
||||||
|
//~| ERROR expected macro derive body, got `=>`
|
||||||
|
//~| NOTE need `()` after this `derive`
|
||||||
|
|
||||||
|
macro_rules! derive_dup_matcher { derive() {$x:ident $x:ident} => {} }
|
||||||
|
//~^ ERROR duplicate matcher binding
|
||||||
|
//~| NOTE duplicate binding
|
||||||
|
//~| NOTE previous binding
|
||||||
90
tests/ui/parser/macro/macro-derive-bad.stderr
Normal file
90
tests/ui/parser/macro/macro-derive-bad.stderr
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
error: macro definition ended unexpectedly
|
||||||
|
--> $DIR/macro-derive-bad.rs:4:42
|
||||||
|
|
|
||||||
|
LL | macro_rules! derive_incomplete_1 { derive }
|
||||||
|
| ^ expected `()` after `derive`
|
||||||
|
|
||||||
|
error: macro definition ended unexpectedly
|
||||||
|
--> $DIR/macro-derive-bad.rs:8:44
|
||||||
|
|
|
||||||
|
LL | macro_rules! derive_incomplete_2 { derive() }
|
||||||
|
| ^ expected macro derive body
|
||||||
|
|
||||||
|
error: expected `=>`, found end of macro arguments
|
||||||
|
--> $DIR/macro-derive-bad.rs:12:47
|
||||||
|
|
|
||||||
|
LL | macro_rules! derive_incomplete_3 { derive() {} }
|
||||||
|
| ^ expected `=>`
|
||||||
|
|
||||||
|
error: macro definition ended unexpectedly
|
||||||
|
--> $DIR/macro-derive-bad.rs:16:50
|
||||||
|
|
|
||||||
|
LL | macro_rules! derive_incomplete_4 { derive() {} => }
|
||||||
|
| ^ expected right-hand side of macro rule
|
||||||
|
|
||||||
|
error: `derive` rule argument matchers require parentheses
|
||||||
|
--> $DIR/macro-derive-bad.rs:20:40
|
||||||
|
|
|
||||||
|
LL | macro_rules! derive_noparens_1 { derive{} {} => {} }
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
help: the delimiters should be `(` and `)`
|
||||||
|
|
|
||||||
|
LL - macro_rules! derive_noparens_1 { derive{} {} => {} }
|
||||||
|
LL + macro_rules! derive_noparens_1 { derive() {} => {} }
|
||||||
|
|
|
||||||
|
|
||||||
|
error: `derive` rule argument matchers require parentheses
|
||||||
|
--> $DIR/macro-derive-bad.rs:23:40
|
||||||
|
|
|
||||||
|
LL | macro_rules! derive_noparens_2 { derive[] {} => {} }
|
||||||
|
| ^^
|
||||||
|
|
|
||||||
|
help: the delimiters should be `(` and `)`
|
||||||
|
|
|
||||||
|
LL - macro_rules! derive_noparens_2 { derive[] {} => {} }
|
||||||
|
LL + macro_rules! derive_noparens_2 { derive() {} => {} }
|
||||||
|
|
|
||||||
|
|
||||||
|
error: `derive` rules do not accept arguments; `derive` must be followed by `()`
|
||||||
|
--> $DIR/macro-derive-bad.rs:26:41
|
||||||
|
|
|
||||||
|
LL | macro_rules! derive_noparens_3 { derive _ {} => {} }
|
||||||
|
| ^
|
||||||
|
|
||||||
|
error: `derive` rules do not accept arguments; `derive` must be followed by `()`
|
||||||
|
--> $DIR/macro-derive-bad.rs:29:36
|
||||||
|
|
|
||||||
|
LL | macro_rules! derive_args_1 { derive($x:ident) ($y:ident) => {} }
|
||||||
|
| ^^^^^^^^^^
|
||||||
|
|
||||||
|
error: expected macro derive body, got `=>`
|
||||||
|
--> $DIR/macro-derive-bad.rs:32:39
|
||||||
|
|
|
||||||
|
LL | macro_rules! derive_args_2 { derive() => {} }
|
||||||
|
| ^^
|
||||||
|
|
||||||
|
error: `derive` rules do not accept arguments; `derive` must be followed by `()`
|
||||||
|
--> $DIR/macro-derive-bad.rs:35:36
|
||||||
|
|
|
||||||
|
LL | macro_rules! derive_args_3 { derive($x:ident) => {} }
|
||||||
|
| ^^^^^^^^^^
|
||||||
|
|
||||||
|
error: expected macro derive body, got `=>`
|
||||||
|
--> $DIR/macro-derive-bad.rs:35:47
|
||||||
|
|
|
||||||
|
LL | macro_rules! derive_args_3 { derive($x:ident) => {} }
|
||||||
|
| ------ ^^
|
||||||
|
| |
|
||||||
|
| need `()` after this `derive`
|
||||||
|
|
||||||
|
error: duplicate matcher binding
|
||||||
|
--> $DIR/macro-derive-bad.rs:40:54
|
||||||
|
|
|
||||||
|
LL | macro_rules! derive_dup_matcher { derive() {$x:ident $x:ident} => {} }
|
||||||
|
| -------- ^^^^^^^^ duplicate binding
|
||||||
|
| |
|
||||||
|
| previous binding
|
||||||
|
|
||||||
|
error: aborting due to 12 previous errors
|
||||||
|
|
||||||
Reference in New Issue
Block a user