@@ -27,10 +27,10 @@ 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
33- use super :: diagnostics:: failed_to_match_macro;
33+ use super :: diagnostics:: { FailedMacro , failed_to_match_macro} ;
3434use super :: macro_parser:: { NamedMatches , NamedParseResult } ;
3535use super :: { SequenceRepetition , diagnostics} ;
3636use crate :: base:: {
@@ -138,6 +138,8 @@ pub(super) enum MacroRule {
138138 body_span : Span ,
139139 rhs : mbe:: TokenTree ,
140140 } ,
141+ /// A derive rule, for use with `#[m]`
142+ Derive { body : Vec < MatcherLoc > , body_span : Span , rhs : mbe:: TokenTree } ,
141143}
142144
143145pub struct MacroRulesMacroExpander {
@@ -157,13 +159,71 @@ impl MacroRulesMacroExpander {
157159 MacroRule :: Attr { args_span, body_span, ref rhs, .. } => {
158160 ( MultiSpan :: from_spans ( vec ! [ args_span, body_span] ) , rhs)
159161 }
162+ MacroRule :: Derive { body_span, ref rhs, .. } => ( MultiSpan :: from_span ( body_span) , rhs) ,
160163 } ;
161164 if has_compile_error_macro ( rhs) { None } else { Some ( ( & self . name , span) ) }
162165 }
163166
164167 pub fn kinds ( & self ) -> MacroKinds {
165168 self . kinds
166169 }
170+
171+ pub fn expand_derive (
172+ & self ,
173+ cx : & mut ExtCtxt < ' _ > ,
174+ sp : Span ,
175+ body : & TokenStream ,
176+ ) -> Result < TokenStream , ErrorGuaranteed > {
177+ // This is similar to `expand_macro`, but they have very different signatures, and will
178+ // diverge further once derives support arguments.
179+ let Self { name, ref rules, node_id, .. } = * self ;
180+ let psess = & cx. sess . psess ;
181+
182+ if cx. trace_macros ( ) {
183+ let msg = format ! ( "expanding `#[derive({name})] {}`" , pprust:: tts_to_string( body) ) ;
184+ trace_macros_note ( & mut cx. expansions , sp, msg) ;
185+ }
186+
187+ match try_match_macro_derive ( psess, name, body, rules, & mut NoopTracker ) {
188+ Ok ( ( rule_index, rule, named_matches) ) => {
189+ let MacroRule :: Derive { rhs, .. } = rule else {
190+ panic ! ( "try_match_macro_derive returned non-derive rule" ) ;
191+ } ;
192+ let mbe:: TokenTree :: Delimited ( rhs_span, _, rhs) = rhs else {
193+ cx. dcx ( ) . span_bug ( sp, "malformed macro derive rhs" ) ;
194+ } ;
195+
196+ let id = cx. current_expansion . id ;
197+ let tts = transcribe ( psess, & named_matches, rhs, * rhs_span, self . transparency , id)
198+ . map_err ( |e| e. emit ( ) ) ?;
199+
200+ if cx. trace_macros ( ) {
201+ let msg = format ! ( "to `{}`" , pprust:: tts_to_string( & tts) ) ;
202+ trace_macros_note ( & mut cx. expansions , sp, msg) ;
203+ }
204+
205+ if is_defined_in_current_crate ( node_id) {
206+ cx. resolver . record_macro_rule_usage ( node_id, rule_index) ;
207+ }
208+
209+ Ok ( tts)
210+ }
211+ Err ( CanRetry :: No ( guar) ) => Err ( guar) ,
212+ Err ( CanRetry :: Yes ) => {
213+ let ( _, guar) = failed_to_match_macro (
214+ cx. psess ( ) ,
215+ sp,
216+ self . span ,
217+ name,
218+ FailedMacro :: Derive ,
219+ body,
220+ rules,
221+ ) ;
222+ cx. macro_error_and_trace_macros_diag ( ) ;
223+ Err ( guar)
224+ }
225+ }
226+ }
167227}
168228
169229impl TTMacroExpander for MacroRulesMacroExpander {
@@ -325,8 +385,15 @@ fn expand_macro<'cx>(
325385 }
326386 Err ( CanRetry :: Yes ) => {
327387 // Retry and emit a better error.
328- let ( span, guar) =
329- failed_to_match_macro ( cx. psess ( ) , sp, def_span, name, None , & arg, rules) ;
388+ let ( span, guar) = failed_to_match_macro (
389+ cx. psess ( ) ,
390+ sp,
391+ def_span,
392+ name,
393+ FailedMacro :: Func ,
394+ & arg,
395+ rules,
396+ ) ;
330397 cx. macro_error_and_trace_macros_diag ( ) ;
331398 DummyResult :: any ( span, guar)
332399 }
@@ -388,8 +455,15 @@ fn expand_macro_attr(
388455 Err ( CanRetry :: No ( guar) ) => Err ( guar) ,
389456 Err ( CanRetry :: Yes ) => {
390457 // Retry and emit a better error.
391- let ( _, guar) =
392- failed_to_match_macro ( cx. psess ( ) , sp, def_span, name, Some ( & args) , & body, rules) ;
458+ let ( _, guar) = failed_to_match_macro (
459+ cx. psess ( ) ,
460+ sp,
461+ def_span,
462+ name,
463+ FailedMacro :: Attr ( & args) ,
464+ & body,
465+ rules,
466+ ) ;
393467 cx. trace_macros_diag ( ) ;
394468 Err ( guar)
395469 }
@@ -536,6 +610,44 @@ pub(super) fn try_match_macro_attr<'matcher, T: Tracker<'matcher>>(
536610 Err ( CanRetry :: Yes )
537611}
538612
613+ /// Try expanding the macro derive. Returns the index of the successful arm and its
614+ /// named_matches if it was successful, and nothing if it failed. On failure, it's the caller's job
615+ /// to use `track` accordingly to record all errors correctly.
616+ #[ instrument( level = "debug" , skip( psess, body, rules, track) , fields( tracking = %T :: description( ) ) ) ]
617+ pub ( super ) fn try_match_macro_derive < ' matcher , T : Tracker < ' matcher > > (
618+ psess : & ParseSess ,
619+ name : Ident ,
620+ body : & TokenStream ,
621+ rules : & ' matcher [ MacroRule ] ,
622+ track : & mut T ,
623+ ) -> Result < ( usize , & ' matcher MacroRule , NamedMatches ) , CanRetry > {
624+ // This uses the same strategy as `try_match_macro`
625+ let body_parser = parser_from_cx ( psess, body. clone ( ) , T :: recovery ( ) ) ;
626+ let mut tt_parser = TtParser :: new ( name) ;
627+ for ( i, rule) in rules. iter ( ) . enumerate ( ) {
628+ let MacroRule :: Derive { body, .. } = rule else { continue } ;
629+
630+ let mut gated_spans_snapshot = mem:: take ( & mut * psess. gated_spans . spans . borrow_mut ( ) ) ;
631+
632+ let result = tt_parser. parse_tt ( & mut Cow :: Borrowed ( & body_parser) , body, track) ;
633+ track. after_arm ( true , & result) ;
634+
635+ match result {
636+ Success ( named_matches) => {
637+ psess. gated_spans . merge ( gated_spans_snapshot) ;
638+ return Ok ( ( i, rule, named_matches) ) ;
639+ }
640+ Failure ( _) => {
641+ mem:: swap ( & mut gated_spans_snapshot, & mut psess. gated_spans . spans . borrow_mut ( ) )
642+ }
643+ Error ( _, _) => return Err ( CanRetry :: Yes ) ,
644+ ErrorReported ( guar) => return Err ( CanRetry :: No ( guar) ) ,
645+ }
646+ }
647+
648+ Err ( CanRetry :: Yes )
649+ }
650+
539651/// Converts a macro item into a syntax extension.
540652pub fn compile_declarative_macro (
541653 sess : & Session ,
@@ -569,7 +681,7 @@ pub fn compile_declarative_macro(
569681 let mut rules = Vec :: new ( ) ;
570682
571683 while p. token != token:: Eof {
572- let args = if p. eat_keyword_noexpect ( sym:: attr) {
684+ let ( args, is_derive ) = if p. eat_keyword_noexpect ( sym:: attr) {
573685 kinds |= MacroKinds :: ATTR ;
574686 if !features. macro_attr ( ) {
575687 feature_err ( sess, sym:: macro_attr, span, "`macro_rules!` attributes are unstable" )
@@ -579,16 +691,46 @@ pub fn compile_declarative_macro(
579691 return dummy_syn_ext ( guar) ;
580692 }
581693 let args = p. parse_token_tree ( ) ;
582- check_args_parens ( sess, & args) ;
694+ check_args_parens ( sess, sym :: attr , & args) ;
583695 let args = parse_one_tt ( args, RulePart :: Pattern , sess, node_id, features, edition) ;
584696 check_emission ( check_lhs ( sess, node_id, & args) ) ;
585697 if let Some ( guar) = check_no_eof ( sess, & p, "expected macro attr body" ) {
586698 return dummy_syn_ext ( guar) ;
587699 }
588- Some ( args)
700+ ( Some ( args) , false )
701+ } else if p. eat_keyword_noexpect ( sym:: derive) {
702+ kinds |= MacroKinds :: DERIVE ;
703+ let derive_keyword_span = p. prev_token . span ;
704+ if !features. macro_derive ( ) {
705+ feature_err ( sess, sym:: macro_attr, span, "`macro_rules!` derives are unstable" )
706+ . emit ( ) ;
707+ }
708+ if let Some ( guar) = check_no_eof ( sess, & p, "expected `()` after `derive`" ) {
709+ return dummy_syn_ext ( guar) ;
710+ }
711+ let args = p. parse_token_tree ( ) ;
712+ check_args_parens ( sess, sym:: derive, & args) ;
713+ let args_empty_result = check_args_empty ( sess, & args) ;
714+ let args_not_empty = args_empty_result. is_err ( ) ;
715+ check_emission ( args_empty_result) ;
716+ if let Some ( guar) = check_no_eof ( sess, & p, "expected macro derive body" ) {
717+ return dummy_syn_ext ( guar) ;
718+ }
719+ // If the user has `=>` right after the `()`, they might have forgotten the empty
720+ // parentheses.
721+ if p. token == token:: FatArrow {
722+ let mut err = sess
723+ . dcx ( )
724+ . struct_span_err ( p. token . span , "expected macro derive body, got `=>`" ) ;
725+ if args_not_empty {
726+ err. span_label ( derive_keyword_span, "need `()` after this `derive`" ) ;
727+ }
728+ return dummy_syn_ext ( err. emit ( ) ) ;
729+ }
730+ ( None , true )
589731 } else {
590732 kinds |= MacroKinds :: BANG ;
591- None
733+ ( None , false )
592734 } ;
593735 let lhs_tt = p. parse_token_tree ( ) ;
594736 let lhs_tt = parse_one_tt ( lhs_tt, RulePart :: Pattern , sess, node_id, features, edition) ;
@@ -619,6 +761,8 @@ pub fn compile_declarative_macro(
619761 let args = mbe:: macro_parser:: compute_locs ( & delimited. tts ) ;
620762 let body_span = lhs_span;
621763 rules. push ( MacroRule :: Attr { args, args_span, body : lhs, body_span, rhs : rhs_tt } ) ;
764+ } else if is_derive {
765+ rules. push ( MacroRule :: Derive { body : lhs, body_span : lhs_span, rhs : rhs_tt } ) ;
622766 } else {
623767 rules. push ( MacroRule :: Func { lhs, lhs_span, rhs : rhs_tt } ) ;
624768 }
@@ -665,18 +809,29 @@ fn check_no_eof(sess: &Session, p: &Parser<'_>, msg: &'static str) -> Option<Err
665809 None
666810}
667811
668- fn check_args_parens ( sess : & Session , args : & tokenstream:: TokenTree ) {
812+ fn check_args_parens ( sess : & Session , rule_kw : Symbol , args : & tokenstream:: TokenTree ) {
669813 // This does not handle the non-delimited case; that gets handled separately by `check_lhs`.
670814 if let tokenstream:: TokenTree :: Delimited ( dspan, _, delim, _) = args
671815 && * delim != Delimiter :: Parenthesis
672816 {
673817 sess. dcx ( ) . emit_err ( errors:: MacroArgsBadDelim {
674818 span : dspan. entire ( ) ,
675819 sugg : errors:: MacroArgsBadDelimSugg { open : dspan. open , close : dspan. close } ,
820+ rule_kw,
676821 } ) ;
677822 }
678823}
679824
825+ fn check_args_empty ( sess : & Session , args : & tokenstream:: TokenTree ) -> Result < ( ) , ErrorGuaranteed > {
826+ match args {
827+ tokenstream:: TokenTree :: Delimited ( .., delimited) if delimited. is_empty ( ) => Ok ( ( ) ) ,
828+ _ => {
829+ let msg = "`derive` rules do not accept arguments; `derive` must be followed by `()`" ;
830+ Err ( sess. dcx ( ) . span_err ( args. span ( ) , msg) )
831+ }
832+ }
833+ }
834+
680835fn check_lhs ( sess : & Session , node_id : NodeId , lhs : & mbe:: TokenTree ) -> Result < ( ) , ErrorGuaranteed > {
681836 let e1 = check_lhs_nt_follows ( sess, node_id, lhs) ;
682837 let e2 = check_lhs_no_empty_seq ( sess, slice:: from_ref ( lhs) ) ;
0 commit comments