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