@@ -26,7 +26,7 @@ use rustc_session::Session;
2626use rustc_session:: parse:: { ParseSess , feature_err} ;
2727use rustc_span:: edition:: Edition ;
2828use rustc_span:: hygiene:: Transparency ;
29- use rustc_span:: { Ident , Span , kw, sym} ;
29+ use rustc_span:: { Ident , Span , Symbol , kw, sym} ;
3030use tracing:: { debug, instrument, trace, trace_span} ;
3131
3232use 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
142145pub 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+
679726fn 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) ) ;
0 commit comments