Skip to content

Commit c984190

Browse files
committed
mbe: Parse macro derive rules
This handles various kinds of errors, but does not allow applying the derive yet. This adds the feature gate `macro_derive`.
1 parent 2d1b77e commit c984190

File tree

13 files changed

+214
-13
lines changed

13 files changed

+214
-13
lines changed

compiler/rustc_expand/messages.ftl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ expand_invalid_fragment_specifier =
7070
invalid fragment specifier `{$fragment}`
7171
.help = {$help}
7272
73-
expand_macro_args_bad_delim = macro attribute argument matchers require parentheses
73+
expand_macro_args_bad_delim = macro `{$rule_kw}` argument matchers require parentheses
7474
expand_macro_args_bad_delim_sugg = the delimiters should be `(` and `)`
7575
7676
expand_macro_body_stability =

compiler/rustc_expand/src/errors.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,7 @@ pub(crate) struct MacroArgsBadDelim {
490490
pub span: Span,
491491
#[subdiagnostic]
492492
pub sugg: MacroArgsBadDelimSugg,
493+
pub rule_kw: Symbol,
493494
}
494495

495496
#[derive(Subdiagnostic)]

compiler/rustc_expand/src/mbe/macro_rules.rs

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use rustc_session::Session;
2626
use rustc_session::parse::{ParseSess, feature_err};
2727
use rustc_span::edition::Edition;
2828
use rustc_span::hygiene::{MacroKinds, Transparency};
29-
use rustc_span::{Ident, Span, kw, sym};
29+
use rustc_span::{Ident, Span, Symbol, kw, sym};
3030
use tracing::{debug, instrument, trace, trace_span};
3131

3232
use super::diagnostics::failed_to_match_macro;
@@ -137,6 +137,9 @@ pub(super) enum MacroRule {
137137
body_span: Span,
138138
rhs: mbe::TokenTree,
139139
},
140+
/// A derive rule, for use with `#[m]`
141+
#[expect(unused)]
142+
Derive { body: Vec<MatcherLoc>, body_span: Span, rhs: mbe::TokenTree },
140143
}
141144

142145
pub struct MacroRulesMacroExpander {
@@ -156,6 +159,7 @@ impl MacroRulesMacroExpander {
156159
MacroRule::Attr { args_span, body_span, ref rhs, .. } => {
157160
(MultiSpan::from_spans(vec![args_span, body_span]), rhs)
158161
}
162+
MacroRule::Derive { body_span, ref rhs, .. } => (MultiSpan::from_span(body_span), rhs),
159163
};
160164
if has_compile_error_macro(rhs) { None } else { Some((&self.name, span)) }
161165
}
@@ -568,7 +572,7 @@ pub fn compile_declarative_macro(
568572
let mut rules = Vec::new();
569573

570574
while p.token != token::Eof {
571-
let args = if p.eat_keyword_noexpect(sym::attr) {
575+
let (args, is_derive) = if p.eat_keyword_noexpect(sym::attr) {
572576
kinds |= MacroKinds::ATTR;
573577
if !features.macro_attr() {
574578
feature_err(sess, sym::macro_attr, span, "`macro_rules!` attributes are unstable")
@@ -578,16 +582,46 @@ pub fn compile_declarative_macro(
578582
return dummy_syn_ext(guar);
579583
}
580584
let args = p.parse_token_tree();
581-
check_args_parens(sess, &args);
585+
check_args_parens(sess, sym::attr, &args);
582586
let args = parse_one_tt(args, RulePart::Pattern, sess, node_id, features, edition);
583587
check_emission(check_lhs(sess, node_id, &args));
584588
if let Some(guar) = check_no_eof(sess, &p, "expected macro attr body") {
585589
return dummy_syn_ext(guar);
586590
}
587-
Some(args)
591+
(Some(args), false)
592+
} else if p.eat_keyword_noexpect(sym::derive) {
593+
kinds |= MacroKinds::DERIVE;
594+
let derive_keyword_span = p.prev_token.span;
595+
if !features.macro_derive() {
596+
feature_err(sess, sym::macro_attr, span, "`macro_rules!` derives are unstable")
597+
.emit();
598+
}
599+
if let Some(guar) = check_no_eof(sess, &p, "expected `()` after `derive`") {
600+
return dummy_syn_ext(guar);
601+
}
602+
let args = p.parse_token_tree();
603+
check_args_parens(sess, sym::derive, &args);
604+
let args_empty_result = check_args_empty(sess, &args);
605+
let args_not_empty = args_empty_result.is_err();
606+
check_emission(args_empty_result);
607+
if let Some(guar) = check_no_eof(sess, &p, "expected macro derive body") {
608+
return dummy_syn_ext(guar);
609+
}
610+
// If the user has `=>` right after the `()`, they might have forgotten the empty
611+
// parentheses.
612+
if p.token == token::FatArrow {
613+
let mut err = sess
614+
.dcx()
615+
.struct_span_err(p.token.span, "expected macro derive body, got `=>`");
616+
if args_not_empty {
617+
err.span_label(derive_keyword_span, "need `()` after this `derive`");
618+
}
619+
return dummy_syn_ext(err.emit());
620+
}
621+
(None, true)
588622
} else {
589623
kinds |= MacroKinds::BANG;
590-
None
624+
(None, false)
591625
};
592626
let lhs_tt = p.parse_token_tree();
593627
let lhs_tt = parse_one_tt(lhs_tt, RulePart::Pattern, sess, node_id, features, edition);
@@ -618,6 +652,8 @@ pub fn compile_declarative_macro(
618652
let args = mbe::macro_parser::compute_locs(&delimited.tts);
619653
let body_span = lhs_span;
620654
rules.push(MacroRule::Attr { args, args_span, body: lhs, body_span, rhs: rhs_tt });
655+
} else if is_derive {
656+
rules.push(MacroRule::Derive { body: lhs, body_span: lhs_span, rhs: rhs_tt });
621657
} else {
622658
rules.push(MacroRule::Func { lhs, lhs_span, rhs: rhs_tt });
623659
}
@@ -664,18 +700,29 @@ fn check_no_eof(sess: &Session, p: &Parser<'_>, msg: &'static str) -> Option<Err
664700
None
665701
}
666702

667-
fn check_args_parens(sess: &Session, args: &tokenstream::TokenTree) {
703+
fn check_args_parens(sess: &Session, rule_kw: Symbol, args: &tokenstream::TokenTree) {
668704
// This does not handle the non-delimited case; that gets handled separately by `check_lhs`.
669705
if let tokenstream::TokenTree::Delimited(dspan, _, delim, _) = args
670706
&& *delim != Delimiter::Parenthesis
671707
{
672708
sess.dcx().emit_err(errors::MacroArgsBadDelim {
673709
span: dspan.entire(),
674710
sugg: errors::MacroArgsBadDelimSugg { open: dspan.open, close: dspan.close },
711+
rule_kw,
675712
});
676713
}
677714
}
678715

716+
fn check_args_empty(sess: &Session, args: &tokenstream::TokenTree) -> Result<(), ErrorGuaranteed> {
717+
match args {
718+
tokenstream::TokenTree::Delimited(.., delimited) if delimited.is_empty() => Ok(()),
719+
_ => {
720+
let msg = "`derive` rules do not accept arguments; `derive` must be followed by `()`";
721+
Err(sess.dcx().span_err(args.span(), msg))
722+
}
723+
}
724+
}
725+
679726
fn check_lhs(sess: &Session, node_id: NodeId, lhs: &mbe::TokenTree) -> Result<(), ErrorGuaranteed> {
680727
let e1 = check_lhs_nt_follows(sess, node_id, lhs);
681728
let e2 = check_lhs_no_empty_seq(sess, slice::from_ref(lhs));

compiler/rustc_feature/src/unstable.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,8 @@ declare_features! (
556556
(incomplete, loop_match, "1.90.0", Some(132306)),
557557
/// Allow `macro_rules!` attribute rules
558558
(unstable, macro_attr, "CURRENT_RUSTC_VERSION", Some(83527)),
559+
/// Allow `macro_rules!` derive rules
560+
(unstable, macro_derive, "CURRENT_RUSTC_VERSION", Some(143549)),
559561
/// Give access to additional metadata about declarative macro meta-variables.
560562
(unstable, macro_metavar_expr, "1.61.0", Some(83527)),
561563
/// Provides a way to concatenate identifiers using metavariable expressions.

compiler/rustc_span/src/symbol.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1314,6 +1314,7 @@ symbols! {
13141314
macro_attr,
13151315
macro_attributes_in_derive_output,
13161316
macro_concat,
1317+
macro_derive,
13171318
macro_escape,
13181319
macro_export,
13191320
macro_lifetime_matcher,
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#![crate_type = "lib"]
2+
3+
macro_rules! MyDerive { derive() {} => {} }
4+
//~^ ERROR `macro_rules!` derives are unstable
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
error[E0658]: `macro_rules!` derives are unstable
2+
--> $DIR/feature-gate-macro-derive.rs:3:1
3+
|
4+
LL | macro_rules! MyDerive { derive() {} => {} }
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: see issue #83527 <https://github.com/rust-lang/rust/issues/83527> for more information
8+
= help: add `#![feature(macro_attr)]` to the crate attributes to enable
9+
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
10+
11+
error: aborting due to 1 previous error
12+
13+
For more information about this error, try `rustc --explain E0658`.

tests/ui/parser/macro/macro-attr-bad.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ macro_rules! attr_incomplete_4 { attr() {} => }
1414
//~^ ERROR macro definition ended unexpectedly
1515

1616
macro_rules! attr_noparens_1 { attr{} {} => {} }
17-
//~^ ERROR macro attribute argument matchers require parentheses
17+
//~^ ERROR macro `attr` argument matchers require parentheses
1818

1919
macro_rules! attr_noparens_2 { attr[] {} => {} }
20-
//~^ ERROR macro attribute argument matchers require parentheses
20+
//~^ ERROR macro `attr` argument matchers require parentheses
2121

2222
macro_rules! attr_noparens_3 { attr _ {} => {} }
2323
//~^ ERROR invalid macro matcher

tests/ui/parser/macro/macro-attr-bad.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ error: macro definition ended unexpectedly
2222
LL | macro_rules! attr_incomplete_4 { attr() {} => }
2323
| ^ expected right-hand side of macro rule
2424

25-
error: macro attribute argument matchers require parentheses
25+
error: macro `attr` argument matchers require parentheses
2626
--> $DIR/macro-attr-bad.rs:16:36
2727
|
2828
LL | macro_rules! attr_noparens_1 { attr{} {} => {} }
@@ -34,7 +34,7 @@ LL - macro_rules! attr_noparens_1 { attr{} {} => {} }
3434
LL + macro_rules! attr_noparens_1 { attr() {} => {} }
3535
|
3636

37-
error: macro attribute argument matchers require parentheses
37+
error: macro `attr` argument matchers require parentheses
3838
--> $DIR/macro-attr-bad.rs:19:36
3939
|
4040
LL | macro_rules! attr_noparens_2 { attr[] {} => {} }

tests/ui/parser/macro/macro-attr-recovery.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
macro_rules! attr {
55
attr[$($args:tt)*] { $($body:tt)* } => {
6-
//~^ ERROR: macro attribute argument matchers require parentheses
6+
//~^ ERROR: macro `attr` argument matchers require parentheses
77
//~v ERROR: attr:
88
compile_error!(concat!(
99
"attr: args=\"",

0 commit comments

Comments
 (0)