@@ -29,11 +29,12 @@ use rustc_span::hygiene::Transparency;
29
29
use rustc_span:: { Ident , Span , kw, sym} ;
30
30
use tracing:: { debug, instrument, trace, trace_span} ;
31
31
32
+ use super :: diagnostics:: { failed_to_match_macro, failed_to_match_macro_attr} ;
32
33
use super :: macro_parser:: { NamedMatches , NamedParseResult } ;
33
34
use super :: { SequenceRepetition , diagnostics} ;
34
35
use crate :: base:: {
35
- DummyResult , ExpandResult , ExtCtxt , MacResult , MacroExpanderResult , SyntaxExtension ,
36
- SyntaxExtensionKind , TTMacroExpander ,
36
+ AttrProcMacro , DummyResult , ExpandResult , ExtCtxt , MacResult , MacroExpanderResult ,
37
+ SyntaxExtension , SyntaxExtensionKind , TTMacroExpander ,
37
38
} ;
38
39
use crate :: errors;
39
40
use crate :: expand:: { AstFragment , AstFragmentKind , ensure_complete_parse, parse_ast_fragment} ;
@@ -129,7 +130,6 @@ pub(super) enum MacroRule {
129
130
/// A function-style rule, for use with `m!()`
130
131
Func { lhs : Vec < MatcherLoc > , lhs_span : Span , rhs : mbe:: TokenTree } ,
131
132
/// An attr rule, for use with `#[m]`
132
- #[ expect( unused) ]
133
133
Attr { args : Vec < MatcherLoc > , body : Vec < MatcherLoc > , lhs_span : Span , rhs : mbe:: TokenTree } ,
134
134
}
135
135
@@ -170,6 +170,28 @@ impl TTMacroExpander for MacroRulesMacroExpander {
170
170
}
171
171
}
172
172
173
+ impl AttrProcMacro for MacroRulesMacroExpander {
174
+ fn expand (
175
+ & self ,
176
+ cx : & mut ExtCtxt < ' _ > ,
177
+ sp : Span ,
178
+ args : TokenStream ,
179
+ body : TokenStream ,
180
+ ) -> Result < TokenStream , ErrorGuaranteed > {
181
+ expand_macro_attr (
182
+ cx,
183
+ sp,
184
+ self . span ,
185
+ self . node_id ,
186
+ self . name ,
187
+ self . transparency ,
188
+ args,
189
+ body,
190
+ & self . rules ,
191
+ )
192
+ }
193
+ }
194
+
173
195
struct DummyExpander ( ErrorGuaranteed ) ;
174
196
175
197
impl TTMacroExpander for DummyExpander {
@@ -202,7 +224,7 @@ pub(super) trait Tracker<'matcher> {
202
224
203
225
/// This is called after an arm has been parsed, either successfully or unsuccessfully. When
204
226
/// this is called, `before_match_loc` was called at least once (with a `MatcherLoc::Eof`).
205
- fn after_arm ( & mut self , _result : & NamedParseResult < Self :: Failure > ) { }
227
+ fn after_arm ( & mut self , _in_body : bool , _result : & NamedParseResult < Self :: Failure > ) { }
206
228
207
229
/// For tracing.
208
230
fn description ( ) -> & ' static str ;
@@ -287,14 +309,76 @@ fn expand_macro<'cx>(
287
309
}
288
310
Err ( CanRetry :: Yes ) => {
289
311
// Retry and emit a better error.
290
- let ( span, guar) =
291
- diagnostics:: failed_to_match_macro ( cx. psess ( ) , sp, def_span, name, arg, rules) ;
312
+ let ( span, guar) = failed_to_match_macro ( cx. psess ( ) , sp, def_span, name, arg, rules) ;
292
313
cx. macro_error_and_trace_macros_diag ( ) ;
293
314
DummyResult :: any ( span, guar)
294
315
}
295
316
}
296
317
}
297
318
319
+ /// Expands the rules based macro defined by `rules` for a given attribute `args` and `body`.
320
+ #[ instrument( skip( cx, transparency, args, body, rules) ) ]
321
+ fn expand_macro_attr (
322
+ cx : & mut ExtCtxt < ' _ > ,
323
+ sp : Span ,
324
+ def_span : Span ,
325
+ node_id : NodeId ,
326
+ name : Ident ,
327
+ transparency : Transparency ,
328
+ args : TokenStream ,
329
+ body : TokenStream ,
330
+ rules : & [ MacroRule ] ,
331
+ ) -> Result < TokenStream , ErrorGuaranteed > {
332
+ let psess = & cx. sess . psess ;
333
+ // Macros defined in the current crate have a real node id,
334
+ // whereas macros from an external crate have a dummy id.
335
+ let is_local = node_id != DUMMY_NODE_ID ;
336
+
337
+ if cx. trace_macros ( ) {
338
+ let msg = format ! (
339
+ "expanding `$[{name}({})] {}`" ,
340
+ pprust:: tts_to_string( & args) ,
341
+ pprust:: tts_to_string( & body) ,
342
+ ) ;
343
+ trace_macros_note ( & mut cx. expansions , sp, msg) ;
344
+ }
345
+
346
+ // Track nothing for the best performance.
347
+ match try_match_macro_attr ( psess, name, & args, & body, rules, & mut NoopTracker ) {
348
+ Ok ( ( i, rule, named_matches) ) => {
349
+ let MacroRule :: Attr { rhs, .. } = rule else {
350
+ panic ! ( "try_macro_match_attr returned non-attr rule" ) ;
351
+ } ;
352
+ let mbe:: TokenTree :: Delimited ( rhs_span, _, rhs) = rhs else {
353
+ cx. dcx ( ) . span_bug ( sp, "malformed macro rhs" ) ;
354
+ } ;
355
+
356
+ let id = cx. current_expansion . id ;
357
+ let tts = transcribe ( psess, & named_matches, rhs, * rhs_span, transparency, id)
358
+ . map_err ( |e| e. emit ( ) ) ?;
359
+
360
+ if cx. trace_macros ( ) {
361
+ let msg = format ! ( "to `{}`" , pprust:: tts_to_string( & tts) ) ;
362
+ trace_macros_note ( & mut cx. expansions , sp, msg) ;
363
+ }
364
+
365
+ if is_local {
366
+ cx. resolver . record_macro_rule_usage ( node_id, i) ;
367
+ }
368
+
369
+ Ok ( tts)
370
+ }
371
+ Err ( CanRetry :: No ( guar) ) => Err ( guar) ,
372
+ Err ( CanRetry :: Yes ) => {
373
+ // Retry and emit a better error.
374
+ let guar =
375
+ failed_to_match_macro_attr ( cx. psess ( ) , sp, def_span, name, args, body, rules) ;
376
+ cx. trace_macros_diag ( ) ;
377
+ Err ( guar)
378
+ }
379
+ }
380
+ }
381
+
298
382
pub ( super ) enum CanRetry {
299
383
Yes ,
300
384
/// We are not allowed to retry macro expansion as a fatal error has been emitted already.
@@ -346,7 +430,7 @@ pub(super) fn try_match_macro<'matcher, T: Tracker<'matcher>>(
346
430
347
431
let result = tt_parser. parse_tt ( & mut Cow :: Borrowed ( & parser) , lhs, track) ;
348
432
349
- track. after_arm ( & result) ;
433
+ track. after_arm ( true , & result) ;
350
434
351
435
match result {
352
436
Success ( named_matches) => {
@@ -381,6 +465,60 @@ pub(super) fn try_match_macro<'matcher, T: Tracker<'matcher>>(
381
465
Err ( CanRetry :: Yes )
382
466
}
383
467
468
+ /// Try expanding the macro attribute. Returns the index of the successful arm and its
469
+ /// named_matches if it was successful, and nothing if it failed. On failure, it's the caller's job
470
+ /// to use `track` accordingly to record all errors correctly.
471
+ #[ instrument( level = "debug" , skip( psess, attr_args, attr_body, rules, track) , fields( tracking = %T :: description( ) ) ) ]
472
+ pub ( super ) fn try_match_macro_attr < ' matcher , T : Tracker < ' matcher > > (
473
+ psess : & ParseSess ,
474
+ name : Ident ,
475
+ attr_args : & TokenStream ,
476
+ attr_body : & TokenStream ,
477
+ rules : & ' matcher [ MacroRule ] ,
478
+ track : & mut T ,
479
+ ) -> Result < ( usize , & ' matcher MacroRule , NamedMatches ) , CanRetry > {
480
+ // This uses the same strategy as `try_match_macro`
481
+ let args_parser = parser_from_cx ( psess, attr_args. clone ( ) , T :: recovery ( ) ) ;
482
+ let body_parser = parser_from_cx ( psess, attr_body. clone ( ) , T :: recovery ( ) ) ;
483
+ let mut tt_parser = TtParser :: new ( name) ;
484
+ for ( i, rule) in rules. iter ( ) . enumerate ( ) {
485
+ let MacroRule :: Attr { args, body, .. } = rule else { continue } ;
486
+
487
+ let mut gated_spans_snapshot = mem:: take ( & mut * psess. gated_spans . spans . borrow_mut ( ) ) ;
488
+
489
+ let result = tt_parser. parse_tt ( & mut Cow :: Borrowed ( & args_parser) , args, track) ;
490
+ track. after_arm ( false , & result) ;
491
+
492
+ let mut named_matches = match result {
493
+ Success ( named_matches) => named_matches,
494
+ Failure ( _) => {
495
+ mem:: swap ( & mut gated_spans_snapshot, & mut psess. gated_spans . spans . borrow_mut ( ) ) ;
496
+ continue ;
497
+ }
498
+ Error ( _, _) => return Err ( CanRetry :: Yes ) ,
499
+ ErrorReported ( guar) => return Err ( CanRetry :: No ( guar) ) ,
500
+ } ;
501
+
502
+ let result = tt_parser. parse_tt ( & mut Cow :: Borrowed ( & body_parser) , body, track) ;
503
+ track. after_arm ( true , & result) ;
504
+
505
+ match result {
506
+ Success ( body_named_matches) => {
507
+ psess. gated_spans . merge ( gated_spans_snapshot) ;
508
+ named_matches. extend ( body_named_matches) ;
509
+ return Ok ( ( i, rule, named_matches) ) ;
510
+ }
511
+ Failure ( _) => {
512
+ mem:: swap ( & mut gated_spans_snapshot, & mut psess. gated_spans . spans . borrow_mut ( ) )
513
+ }
514
+ Error ( _, _) => return Err ( CanRetry :: Yes ) ,
515
+ ErrorReported ( guar) => return Err ( CanRetry :: No ( guar) ) ,
516
+ }
517
+ }
518
+
519
+ Err ( CanRetry :: Yes )
520
+ }
521
+
384
522
/// Converts a macro item into a syntax extension.
385
523
pub fn compile_declarative_macro (
386
524
sess : & Session ,
@@ -391,13 +529,13 @@ pub fn compile_declarative_macro(
391
529
span : Span ,
392
530
node_id : NodeId ,
393
531
edition : Edition ,
394
- ) -> ( SyntaxExtension , usize ) {
395
- let mk_syn_ext = |expander| {
396
- let kind = SyntaxExtensionKind :: LegacyBang ( expander) ;
532
+ ) -> ( SyntaxExtension , Option < Arc < SyntaxExtension > > , usize ) {
533
+ let mk_syn_ext = |kind| {
397
534
let is_local = is_defined_in_current_crate ( node_id) ;
398
535
SyntaxExtension :: new ( sess, kind, span, Vec :: new ( ) , edition, ident. name , attrs, is_local)
399
536
} ;
400
- let dummy_syn_ext = |guar| ( mk_syn_ext ( Arc :: new ( DummyExpander ( guar) ) ) , 0 ) ;
537
+ let mk_bang_ext = |expander| mk_syn_ext ( SyntaxExtensionKind :: LegacyBang ( expander) ) ;
538
+ let dummy_syn_ext = |guar| ( mk_bang_ext ( Arc :: new ( DummyExpander ( guar) ) ) , None , 0 ) ;
401
539
402
540
let macro_rules = macro_def. macro_rules ;
403
541
let exp_sep = if macro_rules { exp ! ( Semi ) } else { exp ! ( Comma ) } ;
@@ -410,10 +548,12 @@ pub fn compile_declarative_macro(
410
548
let mut guar = None ;
411
549
let mut check_emission = |ret : Result < ( ) , ErrorGuaranteed > | guar = guar. or ( ret. err ( ) ) ;
412
550
551
+ let mut has_attr_rules = false ;
413
552
let mut rules = Vec :: new ( ) ;
414
553
415
554
while p. token != token:: Eof {
416
555
let args = if p. eat_keyword_noexpect ( sym:: attr) {
556
+ has_attr_rules = true ;
417
557
if !features. macro_attr ( ) {
418
558
let msg = "`macro_rules!` attributes are unstable" ;
419
559
let e = feature_err ( sess, sym:: macro_attr, span, msg) ;
@@ -491,7 +631,9 @@ pub fn compile_declarative_macro(
491
631
492
632
let expander =
493
633
Arc :: new ( MacroRulesMacroExpander { name : ident, span, node_id, transparency, rules } ) ;
494
- ( mk_syn_ext ( expander) , nrules)
634
+ let opt_attr_ext =
635
+ has_attr_rules. then ( || Arc :: new ( mk_syn_ext ( SyntaxExtensionKind :: Attr ( expander. clone ( ) ) ) ) ) ;
636
+ ( mk_bang_ext ( expander) , opt_attr_ext, nrules)
495
637
}
496
638
497
639
fn check_no_eof ( sess : & Session , p : & Parser < ' _ > , msg : & ' static str ) -> Option < ErrorGuaranteed > {
0 commit comments