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