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