diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl index 1f8f3be68092a..5225b134a0eea 100644 --- a/compiler/rustc_expand/messages.ftl +++ b/compiler/rustc_expand/messages.ftl @@ -70,7 +70,7 @@ expand_invalid_fragment_specifier = invalid fragment specifier `{$fragment}` .help = {$help} -expand_macro_args_bad_delim = macro attribute argument matchers require parentheses +expand_macro_args_bad_delim = macro `{$rule_kw}` argument matchers require parentheses expand_macro_args_bad_delim_sugg = the delimiters should be `(` and `)` expand_macro_body_stability = diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index e58269991fcb0..ba9d76970f0cc 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -490,6 +490,7 @@ pub(crate) struct MacroArgsBadDelim { pub span: Span, #[subdiagnostic] pub sugg: MacroArgsBadDelimSugg, + pub rule_kw: Symbol, } #[derive(Subdiagnostic)] diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index 670f5c91bb93a..90e79da70a0f2 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -16,6 +16,7 @@ use rustc_attr_parsing::{EvalConfigResult, ShouldEmit}; use rustc_data_structures::flat_map_in_place::FlatMapInPlace; use rustc_errors::PResult; use rustc_feature::Features; +use rustc_hir::def::MacroKinds; use rustc_parse::parser::{ AttemptLocalParseRecovery, CommaRecoveryMode, ForceCollect, Parser, RecoverColon, RecoverComma, token_descr, @@ -922,6 +923,35 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } 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!(), }, InvocationKind::GlobDelegation { item, of_trait } => { diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs index 80433b7be9103..f5edaf50edd52 100644 --- a/compiler/rustc_expand/src/mbe/diagnostics.rs +++ b/compiler/rustc_expand/src/mbe/diagnostics.rs @@ -14,14 +14,22 @@ use super::macro_rules::{MacroRule, NoopTracker, parser_from_cx}; use crate::expand::{AstFragmentKind, parse_ast_fragment}; use crate::mbe::macro_parser::ParseResult::*; 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( psess: &ParseSess, sp: Span, def_span: Span, name: Ident, - attr_args: Option<&TokenStream>, + args: FailedMacro<'_>, body: &TokenStream, rules: &[MacroRule], ) -> (Span, ErrorGuaranteed) { @@ -36,10 +44,12 @@ pub(super) fn failed_to_match_macro( // diagnostics. let mut tracker = CollectTrackerAndEmitter::new(psess.dcx(), sp); - let try_success_result = if let Some(attr_args) = attr_args { - try_match_macro_attr(psess, name, attr_args, body, rules, &mut tracker) - } else { - try_match_macro(psess, name, body, rules, &mut tracker) + 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) + } + FailedMacro::Derive => try_match_macro_derive(psess, name, body, rules, &mut tracker), }; 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);` - if attr_args.is_none() + if let FailedMacro::Func = args && let Some((body, comma_span)) = body.add_comma() { for rule in rules { diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 334f57f9d6259..bced02ae4dabb 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -27,10 +27,10 @@ use rustc_session::Session; use rustc_session::parse::{ParseSess, feature_err}; use rustc_span::edition::Edition; 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 super::diagnostics::failed_to_match_macro; +use super::diagnostics::{FailedMacro, failed_to_match_macro}; use super::macro_parser::{NamedMatches, NamedParseResult}; use super::{SequenceRepetition, diagnostics}; use crate::base::{ @@ -138,6 +138,8 @@ pub(super) enum MacroRule { body_span: Span, rhs: mbe::TokenTree, }, + /// A derive rule, for use with `#[m]` + Derive { body: Vec, body_span: Span, rhs: mbe::TokenTree }, } pub struct MacroRulesMacroExpander { @@ -157,6 +159,7 @@ impl MacroRulesMacroExpander { MacroRule::Attr { args_span, body_span, ref 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)) } } @@ -164,6 +167,63 @@ impl MacroRulesMacroExpander { pub fn kinds(&self) -> MacroKinds { self.kinds } + + pub fn expand_derive( + &self, + cx: &mut ExtCtxt<'_>, + sp: Span, + body: &TokenStream, + ) -> Result { + // 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 { @@ -325,8 +385,15 @@ fn expand_macro<'cx>( } Err(CanRetry::Yes) => { // Retry and emit a better error. - let (span, guar) = - failed_to_match_macro(cx.psess(), sp, def_span, name, None, &arg, rules); + let (span, guar) = failed_to_match_macro( + cx.psess(), + sp, + def_span, + name, + FailedMacro::Func, + &arg, + rules, + ); cx.macro_error_and_trace_macros_diag(); DummyResult::any(span, guar) } @@ -388,8 +455,15 @@ fn expand_macro_attr( Err(CanRetry::No(guar)) => Err(guar), Err(CanRetry::Yes) => { // Retry and emit a better error. - let (_, guar) = - failed_to_match_macro(cx.psess(), sp, def_span, name, Some(&args), &body, rules); + let (_, guar) = failed_to_match_macro( + cx.psess(), + sp, + def_span, + name, + FailedMacro::Attr(&args), + &body, + rules, + ); cx.trace_macros_diag(); Err(guar) } @@ -536,6 +610,44 @@ pub(super) fn try_match_macro_attr<'matcher, T: Tracker<'matcher>>( 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. pub fn compile_declarative_macro( sess: &Session, @@ -569,7 +681,7 @@ pub fn compile_declarative_macro( let mut rules = Vec::new(); 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; if !features.macro_attr() { 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); } 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); check_emission(check_lhs(sess, node_id, &args)); if let Some(guar) = check_no_eof(sess, &p, "expected macro attr body") { 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 { kinds |= MacroKinds::BANG; - None + (None, false) }; let lhs_tt = p.parse_token_tree(); 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 body_span = lhs_span; 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 { 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 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> { let e1 = check_lhs_nt_follows(sess, node_id, lhs); let e2 = check_lhs_no_empty_seq(sess, slice::from_ref(lhs)); diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index acc21f6c6d253..e119a8fc033b0 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -556,6 +556,8 @@ declare_features! ( (incomplete, loop_match, "1.90.0", Some(132306)), /// Allow `macro_rules!` attribute rules (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. (unstable, macro_metavar_expr, "1.61.0", Some(83527)), /// Provides a way to concatenate identifiers using metavariable expressions. diff --git a/compiler/rustc_resolve/messages.ftl b/compiler/rustc_resolve/messages.ftl index d5ff8a4b60970..05b5abc3dc674 100644 --- a/compiler/rustc_resolve/messages.ftl +++ b/compiler/rustc_resolve/messages.ftl @@ -249,7 +249,7 @@ resolve_macro_cannot_use_as_attr = `{$ident}` exists, but has no `attr` rules 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 = a macro with the same name exists, but it appears later diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index acbed7a9eed81..9254f041711c9 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1315,6 +1315,7 @@ symbols! { macro_attr, macro_attributes_in_derive_output, macro_concat, + macro_derive, macro_escape, macro_export, macro_lifetime_matcher, diff --git a/tests/ui/feature-gates/feature-gate-macro-derive.rs b/tests/ui/feature-gates/feature-gate-macro-derive.rs new file mode 100644 index 0000000000000..b9d634230616b --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-macro-derive.rs @@ -0,0 +1,4 @@ +#![crate_type = "lib"] + +macro_rules! MyDerive { derive() {} => {} } +//~^ ERROR `macro_rules!` derives are unstable diff --git a/tests/ui/feature-gates/feature-gate-macro-derive.stderr b/tests/ui/feature-gates/feature-gate-macro-derive.stderr new file mode 100644 index 0000000000000..b7ca6717bd51f --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-macro-derive.stderr @@ -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 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`. diff --git a/tests/ui/macros/macro-rules-as-derive-or-attr-issue-132928.stderr b/tests/ui/macros/macro-rules-as-derive-or-attr-issue-132928.stderr index 77f8bef83a452..aad4a844ec17c 100644 --- a/tests/ui/macros/macro-rules-as-derive-or-attr-issue-132928.stderr +++ b/tests/ui/macros/macro-rules-as-derive-or-attr-issue-132928.stderr @@ -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 | 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)] | ^^^^^^ @@ -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 | 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)] | ^^^^^^ @@ -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 | 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)] | ^^^^^^ diff --git a/tests/ui/macros/macro-rules-derive-error.rs b/tests/ui/macros/macro-rules-derive-error.rs new file mode 100644 index 0000000000000..e28dd46f8416f --- /dev/null +++ b/tests/ui/macros/macro-rules-derive-error.rs @@ -0,0 +1,44 @@ +#![feature(macro_derive)] + +macro_rules! MyDerive { + derive() { $($body:tt)* } => { + compile_error!(concat!("MyDerive: ", stringify!($($body)*))); + }; + //~^^ ERROR: MyDerive +} + +//~v NOTE: `fn_only` exists, but has no `derive` rules +macro_rules! fn_only { + {} => {} +} + +//~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); + + //~v ERROR: cannot find derive macro `fn_only` in this scope + #[derive(fn_only)] + struct S2; + + DeriveOnly!(); //~ ERROR: cannot find macro `DeriveOnly` in this scope +} + +//~vv ERROR: cannot find derive macro `ForwardReferencedDerive` in this scope +//~| NOTE: consider moving the definition of `ForwardReferencedDerive` before this call +#[derive(ForwardReferencedDerive)] +struct S; + +//~v NOTE: a macro with the same name exists, but it appears later +macro_rules! ForwardReferencedDerive { + derive() {} => {} +} diff --git a/tests/ui/macros/macro-rules-derive-error.stderr b/tests/ui/macros/macro-rules-derive-error.stderr new file mode 100644 index 0000000000000..bbba238156976 --- /dev/null +++ b/tests/ui/macros/macro-rules-derive-error.stderr @@ -0,0 +1,51 @@ +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:27: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 macro `DeriveOnly` in this scope + --> $DIR/macro-rules-derive-error.rs:33: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:38: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:42:14 + | +LL | macro_rules! ForwardReferencedDerive { + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + diff --git a/tests/ui/macros/macro-rules-derive.rs b/tests/ui/macros/macro-rules-derive.rs new file mode 100644 index 0000000000000..d5294330fbf06 --- /dev/null +++ b/tests/ui/macros/macro-rules-derive.rs @@ -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()); +} diff --git a/tests/ui/macros/macro-rules-derive.run.stdout b/tests/ui/macros/macro-rules-derive.run.stdout new file mode 100644 index 0000000000000..ee4928733025d --- /dev/null +++ b/tests/ui/macros/macro-rules-derive.run.stdout @@ -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 diff --git a/tests/ui/parser/macro/macro-attr-bad.rs b/tests/ui/parser/macro/macro-attr-bad.rs index 4313a4d04abf4..767f501695f3e 100644 --- a/tests/ui/parser/macro/macro-attr-bad.rs +++ b/tests/ui/parser/macro/macro-attr-bad.rs @@ -14,10 +14,10 @@ macro_rules! attr_incomplete_4 { attr() {} => } //~^ ERROR macro definition ended unexpectedly macro_rules! attr_noparens_1 { attr{} {} => {} } -//~^ ERROR macro attribute argument matchers require parentheses +//~^ ERROR macro `attr` argument matchers require parentheses macro_rules! attr_noparens_2 { attr[] {} => {} } -//~^ ERROR macro attribute argument matchers require parentheses +//~^ ERROR macro `attr` argument matchers require parentheses macro_rules! attr_noparens_3 { attr _ {} => {} } //~^ ERROR invalid macro matcher diff --git a/tests/ui/parser/macro/macro-attr-bad.stderr b/tests/ui/parser/macro/macro-attr-bad.stderr index 4d286b6664937..0bbd32786419f 100644 --- a/tests/ui/parser/macro/macro-attr-bad.stderr +++ b/tests/ui/parser/macro/macro-attr-bad.stderr @@ -22,7 +22,7 @@ error: macro definition ended unexpectedly LL | macro_rules! attr_incomplete_4 { attr() {} => } | ^ expected right-hand side of macro rule -error: macro attribute argument matchers require parentheses +error: macro `attr` argument matchers require parentheses --> $DIR/macro-attr-bad.rs:16:36 | 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() {} => {} } | -error: macro attribute argument matchers require parentheses +error: macro `attr` argument matchers require parentheses --> $DIR/macro-attr-bad.rs:19:36 | LL | macro_rules! attr_noparens_2 { attr[] {} => {} } diff --git a/tests/ui/parser/macro/macro-attr-recovery.rs b/tests/ui/parser/macro/macro-attr-recovery.rs index dbb795f57aa59..6df4f5d20feb1 100644 --- a/tests/ui/parser/macro/macro-attr-recovery.rs +++ b/tests/ui/parser/macro/macro-attr-recovery.rs @@ -3,7 +3,7 @@ macro_rules! attr { attr[$($args:tt)*] { $($body:tt)* } => { - //~^ ERROR: macro attribute argument matchers require parentheses + //~^ ERROR: macro `attr` argument matchers require parentheses //~v ERROR: attr: compile_error!(concat!( "attr: args=\"", diff --git a/tests/ui/parser/macro/macro-attr-recovery.stderr b/tests/ui/parser/macro/macro-attr-recovery.stderr index ab3a0b7c60720..cb6b71b83cd11 100644 --- a/tests/ui/parser/macro/macro-attr-recovery.stderr +++ b/tests/ui/parser/macro/macro-attr-recovery.stderr @@ -1,4 +1,4 @@ -error: macro attribute argument matchers require parentheses +error: macro `attr` argument matchers require parentheses --> $DIR/macro-attr-recovery.rs:5:9 | LL | attr[$($args:tt)*] { $($body:tt)* } => { diff --git a/tests/ui/parser/macro/macro-derive-bad.rs b/tests/ui/parser/macro/macro-derive-bad.rs new file mode 100644 index 0000000000000..c87fc6648976f --- /dev/null +++ b/tests/ui/parser/macro/macro-derive-bad.rs @@ -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 macro `derive` argument matchers require parentheses + +macro_rules! derive_noparens_2 { derive[] {} => {} } +//~^ ERROR macro `derive` 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 diff --git a/tests/ui/parser/macro/macro-derive-bad.stderr b/tests/ui/parser/macro/macro-derive-bad.stderr new file mode 100644 index 0000000000000..2c92831ee9383 --- /dev/null +++ b/tests/ui/parser/macro/macro-derive-bad.stderr @@ -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: macro `derive` 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: macro `derive` 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 +