@@ -29,7 +29,7 @@ use rustc_span::hygiene::{MacroKinds, Transparency};
2929use rustc_span:: { Ident , Span , Symbol , kw, sym} ;
3030use tracing:: { debug, instrument, trace, trace_span} ;
3131
32- use super :: diagnostics:: failed_to_match_macro;
32+ use super :: diagnostics:: { FailedMacro , failed_to_match_macro} ;
3333use super :: macro_parser:: { NamedMatches , NamedParseResult } ;
3434use super :: { SequenceRepetition , diagnostics} ;
3535use crate :: base:: {
@@ -138,7 +138,6 @@ pub(super) enum MacroRule {
138138 rhs : mbe:: TokenTree ,
139139 } ,
140140 /// A derive rule, for use with `#[m]`
141- #[ expect( unused) ]
142141 Derive { body : Vec < MatcherLoc > , body_span : Span , rhs : mbe:: TokenTree } ,
143142}
144143
@@ -167,6 +166,63 @@ impl MacroRulesMacroExpander {
167166 pub fn kinds ( & self ) -> MacroKinds {
168167 self . kinds
169168 }
169+
170+ pub fn expand_derive (
171+ & self ,
172+ cx : & mut ExtCtxt < ' _ > ,
173+ sp : Span ,
174+ body : & TokenStream ,
175+ ) -> Result < TokenStream , ErrorGuaranteed > {
176+ // This is similar to `expand_macro`, but they have very different signatures, and will
177+ // diverge further once derives support arguments.
178+ let Self { name, ref rules, node_id, .. } = * self ;
179+ let psess = & cx. sess . psess ;
180+
181+ if cx. trace_macros ( ) {
182+ let msg = format ! ( "expanding `#[derive({name})] {}`" , pprust:: tts_to_string( body) ) ;
183+ trace_macros_note ( & mut cx. expansions , sp, msg) ;
184+ }
185+
186+ match try_match_macro_derive ( psess, name, body, rules, & mut NoopTracker ) {
187+ Ok ( ( rule_index, rule, named_matches) ) => {
188+ let MacroRule :: Derive { rhs, .. } = rule else {
189+ panic ! ( "try_match_macro_derive returned non-derive rule" ) ;
190+ } ;
191+ let mbe:: TokenTree :: Delimited ( rhs_span, _, rhs) = rhs else {
192+ cx. dcx ( ) . span_bug ( sp, "malformed macro derive rhs" ) ;
193+ } ;
194+
195+ let id = cx. current_expansion . id ;
196+ let tts = transcribe ( psess, & named_matches, rhs, * rhs_span, self . transparency , id)
197+ . map_err ( |e| e. emit ( ) ) ?;
198+
199+ if cx. trace_macros ( ) {
200+ let msg = format ! ( "to `{}`" , pprust:: tts_to_string( & tts) ) ;
201+ trace_macros_note ( & mut cx. expansions , sp, msg) ;
202+ }
203+
204+ if is_defined_in_current_crate ( node_id) {
205+ cx. resolver . record_macro_rule_usage ( node_id, rule_index) ;
206+ }
207+
208+ Ok ( tts)
209+ }
210+ Err ( CanRetry :: No ( guar) ) => Err ( guar) ,
211+ Err ( CanRetry :: Yes ) => {
212+ let ( _, guar) = failed_to_match_macro (
213+ cx. psess ( ) ,
214+ sp,
215+ self . span ,
216+ name,
217+ FailedMacro :: Derive ,
218+ body,
219+ rules,
220+ ) ;
221+ cx. macro_error_and_trace_macros_diag ( ) ;
222+ Err ( guar)
223+ }
224+ }
225+ }
170226}
171227
172228impl TTMacroExpander for MacroRulesMacroExpander {
@@ -328,8 +384,15 @@ fn expand_macro<'cx>(
328384 }
329385 Err ( CanRetry :: Yes ) => {
330386 // Retry and emit a better error.
331- let ( span, guar) =
332- failed_to_match_macro ( cx. psess ( ) , sp, def_span, name, None , & arg, rules) ;
387+ let ( span, guar) = failed_to_match_macro (
388+ cx. psess ( ) ,
389+ sp,
390+ def_span,
391+ name,
392+ FailedMacro :: Func ,
393+ & arg,
394+ rules,
395+ ) ;
333396 cx. macro_error_and_trace_macros_diag ( ) ;
334397 DummyResult :: any ( span, guar)
335398 }
@@ -391,8 +454,15 @@ fn expand_macro_attr(
391454 Err ( CanRetry :: No ( guar) ) => Err ( guar) ,
392455 Err ( CanRetry :: Yes ) => {
393456 // Retry and emit a better error.
394- let ( _, guar) =
395- failed_to_match_macro ( cx. psess ( ) , sp, def_span, name, Some ( & args) , & body, rules) ;
457+ let ( _, guar) = failed_to_match_macro (
458+ cx. psess ( ) ,
459+ sp,
460+ def_span,
461+ name,
462+ FailedMacro :: Attr ( & args) ,
463+ & body,
464+ rules,
465+ ) ;
396466 cx. trace_macros_diag ( ) ;
397467 Err ( guar)
398468 }
@@ -539,6 +609,44 @@ pub(super) fn try_match_macro_attr<'matcher, T: Tracker<'matcher>>(
539609 Err ( CanRetry :: Yes )
540610}
541611
612+ /// Try expanding the macro derive. Returns the index of the successful arm and its
613+ /// named_matches if it was successful, and nothing if it failed. On failure, it's the caller's job
614+ /// to use `track` accordingly to record all errors correctly.
615+ #[ instrument( level = "debug" , skip( psess, body, rules, track) , fields( tracking = %T :: description( ) ) ) ]
616+ pub ( super ) fn try_match_macro_derive < ' matcher , T : Tracker < ' matcher > > (
617+ psess : & ParseSess ,
618+ name : Ident ,
619+ body : & TokenStream ,
620+ rules : & ' matcher [ MacroRule ] ,
621+ track : & mut T ,
622+ ) -> Result < ( usize , & ' matcher MacroRule , NamedMatches ) , CanRetry > {
623+ // This uses the same strategy as `try_match_macro`
624+ let body_parser = parser_from_cx ( psess, body. clone ( ) , T :: recovery ( ) ) ;
625+ let mut tt_parser = TtParser :: new ( name) ;
626+ for ( i, rule) in rules. iter ( ) . enumerate ( ) {
627+ let MacroRule :: Derive { body, .. } = rule else { continue } ;
628+
629+ let mut gated_spans_snapshot = mem:: take ( & mut * psess. gated_spans . spans . borrow_mut ( ) ) ;
630+
631+ let result = tt_parser. parse_tt ( & mut Cow :: Borrowed ( & body_parser) , body, track) ;
632+ track. after_arm ( true , & result) ;
633+
634+ match result {
635+ Success ( named_matches) => {
636+ psess. gated_spans . merge ( gated_spans_snapshot) ;
637+ return Ok ( ( i, rule, named_matches) ) ;
638+ }
639+ Failure ( _) => {
640+ mem:: swap ( & mut gated_spans_snapshot, & mut psess. gated_spans . spans . borrow_mut ( ) )
641+ }
642+ Error ( _, _) => return Err ( CanRetry :: Yes ) ,
643+ ErrorReported ( guar) => return Err ( CanRetry :: No ( guar) ) ,
644+ }
645+ }
646+
647+ Err ( CanRetry :: Yes )
648+ }
649+
542650/// Converts a macro item into a syntax extension.
543651pub fn compile_declarative_macro (
544652 sess : & Session ,
0 commit comments