@@ -27,10 +27,10 @@ use rustc_session::Session;
27
27
use rustc_session:: parse:: { ParseSess , feature_err} ;
28
28
use rustc_span:: edition:: Edition ;
29
29
use rustc_span:: hygiene:: Transparency ;
30
- use rustc_span:: { Ident , Span , kw, sym} ;
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:: {
@@ -138,6 +138,8 @@ pub(super) enum MacroRule {
138
138
body_span : Span ,
139
139
rhs : mbe:: TokenTree ,
140
140
} ,
141
+ /// A derive rule, for use with `#[m]`
142
+ Derive { body : Vec < MatcherLoc > , body_span : Span , rhs : mbe:: TokenTree } ,
141
143
}
142
144
143
145
pub struct MacroRulesMacroExpander {
@@ -157,13 +159,71 @@ impl MacroRulesMacroExpander {
157
159
MacroRule :: Attr { args_span, body_span, ref rhs, .. } => {
158
160
( MultiSpan :: from_spans ( vec ! [ args_span, body_span] ) , rhs)
159
161
}
162
+ MacroRule :: Derive { body_span, ref rhs, .. } => ( MultiSpan :: from_span ( body_span) , rhs) ,
160
163
} ;
161
164
if has_compile_error_macro ( rhs) { None } else { Some ( ( & self . name , span) ) }
162
165
}
163
166
164
167
pub fn kinds ( & self ) -> MacroKinds {
165
168
self . kinds
166
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
+ }
167
227
}
168
228
169
229
impl TTMacroExpander for MacroRulesMacroExpander {
@@ -325,8 +385,15 @@ fn expand_macro<'cx>(
325
385
}
326
386
Err ( CanRetry :: Yes ) => {
327
387
// 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
+ ) ;
330
397
cx. macro_error_and_trace_macros_diag ( ) ;
331
398
DummyResult :: any ( span, guar)
332
399
}
@@ -388,8 +455,15 @@ fn expand_macro_attr(
388
455
Err ( CanRetry :: No ( guar) ) => Err ( guar) ,
389
456
Err ( CanRetry :: Yes ) => {
390
457
// 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
+ ) ;
393
467
cx. trace_macros_diag ( ) ;
394
468
Err ( guar)
395
469
}
@@ -537,6 +611,44 @@ pub(super) fn try_match_macro_attr<'matcher, T: Tracker<'matcher>>(
537
611
Err ( CanRetry :: Yes )
538
612
}
539
613
614
+ /// Try expanding the macro derive. Returns the index of the successful arm and its
615
+ /// named_matches if it was successful, and nothing if it failed. On failure, it's the caller's job
616
+ /// to use `track` accordingly to record all errors correctly.
617
+ #[ instrument( level = "debug" , skip( psess, body, rules, track) , fields( tracking = %T :: description( ) ) ) ]
618
+ pub ( super ) fn try_match_macro_derive < ' matcher , T : Tracker < ' matcher > > (
619
+ psess : & ParseSess ,
620
+ name : Ident ,
621
+ body : & TokenStream ,
622
+ rules : & ' matcher [ MacroRule ] ,
623
+ track : & mut T ,
624
+ ) -> Result < ( usize , & ' matcher MacroRule , NamedMatches ) , CanRetry > {
625
+ // This uses the same strategy as `try_match_macro`
626
+ let body_parser = parser_from_cx ( psess, body. clone ( ) , T :: recovery ( ) ) ;
627
+ let mut tt_parser = TtParser :: new ( name) ;
628
+ for ( i, rule) in rules. iter ( ) . enumerate ( ) {
629
+ let MacroRule :: Derive { body, .. } = rule else { continue } ;
630
+
631
+ let mut gated_spans_snapshot = mem:: take ( & mut * psess. gated_spans . spans . borrow_mut ( ) ) ;
632
+
633
+ let result = tt_parser. parse_tt ( & mut Cow :: Borrowed ( & body_parser) , body, track) ;
634
+ track. after_arm ( true , & result) ;
635
+
636
+ match result {
637
+ Success ( named_matches) => {
638
+ psess. gated_spans . merge ( gated_spans_snapshot) ;
639
+ return Ok ( ( i, rule, named_matches) ) ;
640
+ }
641
+ Failure ( _) => {
642
+ mem:: swap ( & mut gated_spans_snapshot, & mut psess. gated_spans . spans . borrow_mut ( ) )
643
+ }
644
+ Error ( _, _) => return Err ( CanRetry :: Yes ) ,
645
+ ErrorReported ( guar) => return Err ( CanRetry :: No ( guar) ) ,
646
+ }
647
+ }
648
+
649
+ Err ( CanRetry :: Yes )
650
+ }
651
+
540
652
/// Converts a macro item into a syntax extension.
541
653
pub fn compile_declarative_macro (
542
654
sess : & Session ,
@@ -570,7 +682,7 @@ pub fn compile_declarative_macro(
570
682
let mut rules = Vec :: new ( ) ;
571
683
572
684
while p. token != token:: Eof {
573
- let args = if p. eat_keyword_noexpect ( sym:: attr) {
685
+ let ( args, is_derive ) = if p. eat_keyword_noexpect ( sym:: attr) {
574
686
kinds |= MacroKinds :: ATTR ;
575
687
if !features. macro_attr ( ) {
576
688
feature_err ( sess, sym:: macro_attr, span, "`macro_rules!` attributes are unstable" )
@@ -580,16 +692,46 @@ pub fn compile_declarative_macro(
580
692
return dummy_syn_ext ( guar) ;
581
693
}
582
694
let args = p. parse_token_tree ( ) ;
583
- check_args_parens ( sess, & args) ;
695
+ check_args_parens ( sess, sym :: attr , & args) ;
584
696
let args = parse_one_tt ( args, RulePart :: Pattern , sess, node_id, features, edition) ;
585
697
check_emission ( check_lhs ( sess, node_id, & args) ) ;
586
698
if let Some ( guar) = check_no_eof ( sess, & p, "expected macro attr body" ) {
587
699
return dummy_syn_ext ( guar) ;
588
700
}
589
- Some ( args)
701
+ ( Some ( args) , false )
702
+ } else if p. eat_keyword_noexpect ( sym:: derive) {
703
+ kinds |= MacroKinds :: DERIVE ;
704
+ let derive_keyword_span = p. prev_token . span ;
705
+ if !features. macro_derive ( ) {
706
+ feature_err ( sess, sym:: macro_attr, span, "`macro_rules!` derives are unstable" )
707
+ . emit ( ) ;
708
+ }
709
+ if let Some ( guar) = check_no_eof ( sess, & p, "expected `()` after `derive`" ) {
710
+ return dummy_syn_ext ( guar) ;
711
+ }
712
+ let args = p. parse_token_tree ( ) ;
713
+ check_args_parens ( sess, sym:: derive, & args) ;
714
+ let args_empty_result = check_args_empty ( sess, & args) ;
715
+ let args_not_empty = args_empty_result. is_err ( ) ;
716
+ check_emission ( args_empty_result) ;
717
+ if let Some ( guar) = check_no_eof ( sess, & p, "expected macro derive body" ) {
718
+ return dummy_syn_ext ( guar) ;
719
+ }
720
+ // If the user has `=>` right after the `()`, they might have forgotten the empty
721
+ // parentheses.
722
+ if p. token == token:: FatArrow {
723
+ let mut err = sess
724
+ . dcx ( )
725
+ . struct_span_err ( p. token . span , "expected macro derive body, got `=>`" ) ;
726
+ if args_not_empty {
727
+ err. span_label ( derive_keyword_span, "need `()` after this `derive`" ) ;
728
+ }
729
+ return dummy_syn_ext ( err. emit ( ) ) ;
730
+ }
731
+ ( None , true )
590
732
} else {
591
733
kinds |= MacroKinds :: BANG ;
592
- None
734
+ ( None , false )
593
735
} ;
594
736
let lhs_tt = p. parse_token_tree ( ) ;
595
737
let lhs_tt = parse_one_tt ( lhs_tt, RulePart :: Pattern , sess, node_id, features, edition) ;
@@ -620,6 +762,8 @@ pub fn compile_declarative_macro(
620
762
let args = mbe:: macro_parser:: compute_locs ( & delimited. tts ) ;
621
763
let body_span = lhs_span;
622
764
rules. push ( MacroRule :: Attr { args, args_span, body : lhs, body_span, rhs : rhs_tt } ) ;
765
+ } else if is_derive {
766
+ rules. push ( MacroRule :: Derive { body : lhs, body_span : lhs_span, rhs : rhs_tt } ) ;
623
767
} else {
624
768
rules. push ( MacroRule :: Func { lhs, lhs_span, rhs : rhs_tt } ) ;
625
769
}
@@ -666,18 +810,29 @@ fn check_no_eof(sess: &Session, p: &Parser<'_>, msg: &'static str) -> Option<Err
666
810
None
667
811
}
668
812
669
- fn check_args_parens ( sess : & Session , args : & tokenstream:: TokenTree ) {
813
+ fn check_args_parens ( sess : & Session , rule_kw : Symbol , args : & tokenstream:: TokenTree ) {
670
814
// This does not handle the non-delimited case; that gets handled separately by `check_lhs`.
671
815
if let tokenstream:: TokenTree :: Delimited ( dspan, _, delim, _) = args
672
816
&& * delim != Delimiter :: Parenthesis
673
817
{
674
818
sess. dcx ( ) . emit_err ( errors:: MacroArgsBadDelim {
675
819
span : dspan. entire ( ) ,
676
820
sugg : errors:: MacroArgsBadDelimSugg { open : dspan. open , close : dspan. close } ,
821
+ rule_kw,
677
822
} ) ;
678
823
}
679
824
}
680
825
826
+ fn check_args_empty ( sess : & Session , args : & tokenstream:: TokenTree ) -> Result < ( ) , ErrorGuaranteed > {
827
+ match args {
828
+ tokenstream:: TokenTree :: Delimited ( .., delimited) if delimited. is_empty ( ) => Ok ( ( ) ) ,
829
+ _ => {
830
+ let msg = "`derive` rules do not accept arguments; `derive` must be followed by `()`" ;
831
+ Err ( sess. dcx ( ) . span_err ( args. span ( ) , msg) )
832
+ }
833
+ }
834
+ }
835
+
681
836
fn check_lhs ( sess : & Session , node_id : NodeId , lhs : & mbe:: TokenTree ) -> Result < ( ) , ErrorGuaranteed > {
682
837
let e1 = check_lhs_nt_follows ( sess, node_id, lhs) ;
683
838
let e2 = check_lhs_no_empty_seq ( sess, slice:: from_ref ( lhs) ) ;
0 commit comments