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