@@ -29,7 +29,7 @@ use rustc_span::hygiene::{MacroKinds, Transparency};
29
29
use rustc_span:: { Ident , Span , Symbol , kw, sym} ;
30
30
use tracing:: { debug, instrument, trace, trace_span} ;
31
31
32
- use super :: diagnostics:: failed_to_match_macro;
32
+ use super :: diagnostics:: { FailedMacro , failed_to_match_macro} ;
33
33
use super :: macro_parser:: { NamedMatches , NamedParseResult } ;
34
34
use super :: { SequenceRepetition , diagnostics} ;
35
35
use crate :: base:: {
@@ -138,7 +138,6 @@ pub(super) enum MacroRule {
138
138
rhs : mbe:: TokenTree ,
139
139
} ,
140
140
/// A derive rule, for use with `#[m]`
141
- #[ expect( unused) ]
142
141
Derive { body : Vec < MatcherLoc > , body_span : Span , rhs : mbe:: TokenTree } ,
143
142
}
144
143
@@ -167,6 +166,63 @@ impl MacroRulesMacroExpander {
167
166
pub fn kinds ( & self ) -> MacroKinds {
168
167
self . kinds
169
168
}
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
+ }
170
226
}
171
227
172
228
impl TTMacroExpander for MacroRulesMacroExpander {
@@ -328,8 +384,15 @@ fn expand_macro<'cx>(
328
384
}
329
385
Err ( CanRetry :: Yes ) => {
330
386
// 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
+ ) ;
333
396
cx. macro_error_and_trace_macros_diag ( ) ;
334
397
DummyResult :: any ( span, guar)
335
398
}
@@ -391,8 +454,15 @@ fn expand_macro_attr(
391
454
Err ( CanRetry :: No ( guar) ) => Err ( guar) ,
392
455
Err ( CanRetry :: Yes ) => {
393
456
// 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
+ ) ;
396
466
cx. trace_macros_diag ( ) ;
397
467
Err ( guar)
398
468
}
@@ -539,6 +609,44 @@ pub(super) fn try_match_macro_attr<'matcher, T: Tracker<'matcher>>(
539
609
Err ( CanRetry :: Yes )
540
610
}
541
611
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
+
542
650
/// Converts a macro item into a syntax extension.
543
651
pub fn compile_declarative_macro (
544
652
sess : & Session ,
0 commit comments