Skip to content

Commit 5094e9c

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 2c1ac85 commit 5094e9c

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
@@ -27,7 +27,7 @@ use rustc_session::Session;
2727
use rustc_session::parse::{ParseSess, feature_err};
2828
use rustc_span::edition::Edition;
2929
use rustc_span::hygiene::Transparency;
30-
use rustc_span::{Ident, Span, kw, sym};
30+
use rustc_span::{Ident, Span, Symbol, kw, sym};
3131
use tracing::{debug, instrument, trace, trace_span};
3232

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

143146
pub struct MacroRulesMacroExpander {
@@ -157,6 +160,7 @@ impl MacroRulesMacroExpander {
157160
MacroRule::Attr { args_span, body_span, ref rhs, .. } => {
158161
(MultiSpan::from_spans(vec![args_span, body_span]), rhs)
159162
}
163+
MacroRule::Derive { body_span, ref rhs, .. } => (MultiSpan::from_span(body_span), rhs),
160164
};
161165
if has_compile_error_macro(rhs) { None } else { Some((&self.name, span)) }
162166
}
@@ -569,7 +573,7 @@ pub fn compile_declarative_macro(
569573
let mut rules = Vec::new();
570574

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

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

717+
fn check_args_empty(sess: &Session, args: &tokenstream::TokenTree) -> Result<(), ErrorGuaranteed> {
718+
match args {
719+
tokenstream::TokenTree::Delimited(.., delimited) if delimited.is_empty() => Ok(()),
720+
_ => {
721+
let msg = "`derive` rules do not accept arguments; `derive` must be followed by `()`";
722+
Err(sess.dcx().span_err(args.span(), msg))
723+
}
724+
}
725+
}
726+
680727
fn check_lhs(sess: &Session, node_id: NodeId, lhs: &mbe::TokenTree) -> Result<(), ErrorGuaranteed> {
681728
let e1 = check_lhs_nt_follows(sess, node_id, lhs);
682729
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
@@ -1315,6 +1315,7 @@ symbols! {
13151315
macro_attr,
13161316
macro_attributes_in_derive_output,
13171317
macro_concat,
1318+
macro_derive,
13181319
macro_escape,
13191320
macro_export,
13201321
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)