@@ -26,7 +26,7 @@ use rustc_session::Session;
26
26
use rustc_session:: parse:: { ParseSess , feature_err} ;
27
27
use rustc_span:: edition:: Edition ;
28
28
use rustc_span:: hygiene:: { MacroKinds , Transparency } ;
29
- use rustc_span:: { Ident , Span , kw, sym} ;
29
+ use rustc_span:: { Ident , Span , Symbol , kw, sym} ;
30
30
use tracing:: { debug, instrument, trace, trace_span} ;
31
31
32
32
use super :: diagnostics:: failed_to_match_macro;
@@ -137,6 +137,9 @@ pub(super) enum MacroRule {
137
137
body_span : Span ,
138
138
rhs : mbe:: TokenTree ,
139
139
} ,
140
+ /// A derive rule, for use with `#[m]`
141
+ #[ expect( unused) ]
142
+ Derive { body : Vec < MatcherLoc > , body_span : Span , rhs : mbe:: TokenTree } ,
140
143
}
141
144
142
145
pub struct MacroRulesMacroExpander {
@@ -156,6 +159,7 @@ impl MacroRulesMacroExpander {
156
159
MacroRule :: Attr { args_span, body_span, ref rhs, .. } => {
157
160
( MultiSpan :: from_spans ( vec ! [ args_span, body_span] ) , rhs)
158
161
}
162
+ MacroRule :: Derive { body_span, ref rhs, .. } => ( MultiSpan :: from_span ( body_span) , rhs) ,
159
163
} ;
160
164
if has_compile_error_macro ( rhs) { None } else { Some ( ( & self . name , span) ) }
161
165
}
@@ -568,7 +572,7 @@ pub fn compile_declarative_macro(
568
572
let mut rules = Vec :: new ( ) ;
569
573
570
574
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) {
572
576
kinds |= MacroKinds :: ATTR ;
573
577
if !features. macro_attr ( ) {
574
578
feature_err ( sess, sym:: macro_attr, span, "`macro_rules!` attributes are unstable" )
@@ -578,16 +582,46 @@ pub fn compile_declarative_macro(
578
582
return dummy_syn_ext ( guar) ;
579
583
}
580
584
let args = p. parse_token_tree ( ) ;
581
- check_args_parens ( sess, & args) ;
585
+ check_args_parens ( sess, sym :: attr , & args) ;
582
586
let args = parse_one_tt ( args, RulePart :: Pattern , sess, node_id, features, edition) ;
583
587
check_emission ( check_lhs ( sess, node_id, & args) ) ;
584
588
if let Some ( guar) = check_no_eof ( sess, & p, "expected macro attr body" ) {
585
589
return dummy_syn_ext ( guar) ;
586
590
}
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 )
588
622
} else {
589
623
kinds |= MacroKinds :: BANG ;
590
- None
624
+ ( None , false )
591
625
} ;
592
626
let lhs_tt = p. parse_token_tree ( ) ;
593
627
let lhs_tt = parse_one_tt ( lhs_tt, RulePart :: Pattern , sess, node_id, features, edition) ;
@@ -618,6 +652,8 @@ pub fn compile_declarative_macro(
618
652
let args = mbe:: macro_parser:: compute_locs ( & delimited. tts ) ;
619
653
let body_span = lhs_span;
620
654
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 } ) ;
621
657
} else {
622
658
rules. push ( MacroRule :: Func { lhs, lhs_span, rhs : rhs_tt } ) ;
623
659
}
@@ -664,18 +700,29 @@ fn check_no_eof(sess: &Session, p: &Parser<'_>, msg: &'static str) -> Option<Err
664
700
None
665
701
}
666
702
667
- fn check_args_parens ( sess : & Session , args : & tokenstream:: TokenTree ) {
703
+ fn check_args_parens ( sess : & Session , rule_kw : Symbol , args : & tokenstream:: TokenTree ) {
668
704
// This does not handle the non-delimited case; that gets handled separately by `check_lhs`.
669
705
if let tokenstream:: TokenTree :: Delimited ( dspan, _, delim, _) = args
670
706
&& * delim != Delimiter :: Parenthesis
671
707
{
672
708
sess. dcx ( ) . emit_err ( errors:: MacroArgsBadDelim {
673
709
span : dspan. entire ( ) ,
674
710
sugg : errors:: MacroArgsBadDelimSugg { open : dspan. open , close : dspan. close } ,
711
+ rule_kw,
675
712
} ) ;
676
713
}
677
714
}
678
715
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
+
679
726
fn check_lhs ( sess : & Session , node_id : NodeId , lhs : & mbe:: TokenTree ) -> Result < ( ) , ErrorGuaranteed > {
680
727
let e1 = check_lhs_nt_follows ( sess, node_id, lhs) ;
681
728
let e2 = check_lhs_no_empty_seq ( sess, slice:: from_ref ( lhs) ) ;
0 commit comments