@@ -6,8 +6,8 @@ use std::{mem, slice};
6
6
use ast:: token:: IdentIsRaw ;
7
7
use rustc_ast:: token:: NtPatKind :: * ;
8
8
use rustc_ast:: token:: TokenKind :: * ;
9
- use rustc_ast:: token:: { self , NonterminalKind , Token , TokenKind } ;
10
- use rustc_ast:: tokenstream:: { DelimSpan , TokenStream } ;
9
+ use rustc_ast:: token:: { self , Delimiter , NonterminalKind , Token , TokenKind } ;
10
+ use rustc_ast:: tokenstream:: { self , DelimSpan , TokenStream } ;
11
11
use rustc_ast:: { self as ast, DUMMY_NODE_ID , NodeId } ;
12
12
use rustc_ast_pretty:: pprust;
13
13
use rustc_attr_data_structures:: { AttributeKind , find_attr} ;
@@ -22,7 +22,7 @@ use rustc_lint_defs::builtin::{
22
22
use rustc_parse:: exp;
23
23
use rustc_parse:: parser:: { Parser , Recovery } ;
24
24
use rustc_session:: Session ;
25
- use rustc_session:: parse:: ParseSess ;
25
+ use rustc_session:: parse:: { ParseSess , feature_err } ;
26
26
use rustc_span:: edition:: Edition ;
27
27
use rustc_span:: hygiene:: Transparency ;
28
28
use rustc_span:: { Ident , Span , kw, sym} ;
@@ -34,11 +34,13 @@ use crate::base::{
34
34
DummyResult , ExpandResult , ExtCtxt , MacResult , MacroExpanderResult , SyntaxExtension ,
35
35
SyntaxExtensionKind , TTMacroExpander ,
36
36
} ;
37
+ use crate :: errors;
37
38
use crate :: expand:: { AstFragment , AstFragmentKind , ensure_complete_parse, parse_ast_fragment} ;
39
+ use crate :: mbe:: macro_check:: check_meta_variables;
38
40
use crate :: mbe:: macro_parser:: { Error , ErrorReported , Failure , MatcherLoc , Success , TtParser } ;
39
41
use crate :: mbe:: quoted:: { RulePart , parse_one_tt} ;
40
42
use crate :: mbe:: transcribe:: transcribe;
41
- use crate :: mbe:: { self , KleeneOp , macro_check } ;
43
+ use crate :: mbe:: { self , KleeneOp } ;
42
44
43
45
pub ( crate ) struct ParserAnyMacro < ' a > {
44
46
parser : Parser < ' a > ,
@@ -122,10 +124,12 @@ impl<'a> ParserAnyMacro<'a> {
122
124
}
123
125
}
124
126
125
- pub ( super ) struct MacroRule {
126
- pub ( super ) lhs : Vec < MatcherLoc > ,
127
- lhs_span : Span ,
128
- rhs : mbe:: TokenTree ,
127
+ pub ( super ) enum MacroRule {
128
+ /// A function-style rule, for use with `m!()`
129
+ Func { lhs : Vec < MatcherLoc > , lhs_span : Span , rhs : mbe:: TokenTree } ,
130
+ /// An attr rule, for use with `#[m]`
131
+ #[ expect( unused) ]
132
+ Attr { args : Vec < MatcherLoc > , body : Vec < MatcherLoc > , lhs_span : Span , rhs : mbe:: TokenTree } ,
129
133
}
130
134
131
135
pub struct MacroRulesMacroExpander {
@@ -139,8 +143,9 @@ pub struct MacroRulesMacroExpander {
139
143
impl MacroRulesMacroExpander {
140
144
pub fn get_unused_rule ( & self , rule_i : usize ) -> Option < ( & Ident , Span ) > {
141
145
// If the rhs contains an invocation like `compile_error!`, don't report it as unused.
142
- let rule = & self . rules [ rule_i] ;
143
- if has_compile_error_macro ( & rule. rhs ) { None } else { Some ( ( & self . name , rule. lhs_span ) ) }
146
+ let ( MacroRule :: Func { lhs_span, ref rhs, .. } | MacroRule :: Attr { lhs_span, ref rhs, .. } ) =
147
+ self . rules [ rule_i] ;
148
+ if has_compile_error_macro ( rhs) { None } else { Some ( ( & self . name , lhs_span) ) }
144
149
}
145
150
}
146
151
@@ -244,14 +249,17 @@ fn expand_macro<'cx>(
244
249
245
250
match try_success_result {
246
251
Ok ( ( rule_index, rule, named_matches) ) => {
247
- let mbe:: TokenTree :: Delimited ( rhs_span, _, ref rhs) = rule. rhs else {
252
+ let MacroRule :: Func { rhs, .. } = rule else {
253
+ panic ! ( "try_match_macro returned non-func rule" ) ;
254
+ } ;
255
+ let mbe:: TokenTree :: Delimited ( rhs_span, _, rhs) = rhs else {
248
256
cx. dcx ( ) . span_bug ( sp, "malformed macro rhs" ) ;
249
257
} ;
250
- let arm_span = rule . rhs . span ( ) ;
258
+ let arm_span = rhs_span . entire ( ) ;
251
259
252
260
// rhs has holes ( `$id` and `$(...)` that need filled)
253
261
let id = cx. current_expansion . id ;
254
- let tts = match transcribe ( psess, & named_matches, rhs, rhs_span, transparency, id) {
262
+ let tts = match transcribe ( psess, & named_matches, rhs, * rhs_span, transparency, id) {
255
263
Ok ( tts) => tts,
256
264
Err ( err) => {
257
265
let guar = err. emit ( ) ;
@@ -326,6 +334,7 @@ pub(super) fn try_match_macro<'matcher, T: Tracker<'matcher>>(
326
334
// Try each arm's matchers.
327
335
let mut tt_parser = TtParser :: new ( name) ;
328
336
for ( i, rule) in rules. iter ( ) . enumerate ( ) {
337
+ let MacroRule :: Func { lhs, .. } = rule else { continue } ;
329
338
let _tracing_span = trace_span ! ( "Matching arm" , %i) ;
330
339
331
340
// Take a snapshot of the state of pre-expansion gating at this point.
@@ -334,7 +343,7 @@ pub(super) fn try_match_macro<'matcher, T: Tracker<'matcher>>(
334
343
// are not recorded. On the first `Success(..)`ful matcher, the spans are merged.
335
344
let mut gated_spans_snapshot = mem:: take ( & mut * psess. gated_spans . spans . borrow_mut ( ) ) ;
336
345
337
- let result = tt_parser. parse_tt ( & mut Cow :: Borrowed ( & parser) , & rule . lhs , track) ;
346
+ let result = tt_parser. parse_tt ( & mut Cow :: Borrowed ( & parser) , lhs, track) ;
338
347
339
348
track. after_arm ( & result) ;
340
349
@@ -403,6 +412,26 @@ pub fn compile_declarative_macro(
403
412
let mut rules = Vec :: new ( ) ;
404
413
405
414
while p. token != token:: Eof {
415
+ let args = if p. eat_keyword_noexpect ( sym:: attr) {
416
+ if !features. macro_attr ( ) {
417
+ let msg = "`macro_rules!` attributes are unstable" ;
418
+ let e = feature_err ( sess, sym:: macro_attr, span, msg) ;
419
+ return dummy_syn_ext ( e. emit ( ) ) ;
420
+ }
421
+ if let Some ( guar) = check_no_eof ( sess, & p, "expected macro attr args" ) {
422
+ return dummy_syn_ext ( guar) ;
423
+ }
424
+ let args = p. parse_token_tree ( ) ;
425
+ check_emission ( check_args_parens ( sess, & args) ) ;
426
+ let args = parse_one_tt ( args, RulePart :: Pattern , sess, node_id, features, edition) ;
427
+ check_emission ( check_lhs ( sess, node_id, & args) ) ;
428
+ if let Some ( guar) = check_no_eof ( sess, & p, "expected macro attr body" ) {
429
+ return dummy_syn_ext ( guar) ;
430
+ }
431
+ Some ( args)
432
+ } else {
433
+ None
434
+ } ;
406
435
let lhs_tt = p. parse_token_tree ( ) ;
407
436
let lhs_tt = parse_one_tt ( lhs_tt, RulePart :: Pattern , sess, node_id, features, edition) ;
408
437
check_emission ( check_lhs ( sess, node_id, & lhs_tt) ) ;
@@ -415,7 +444,7 @@ pub fn compile_declarative_macro(
415
444
let rhs_tt = p. parse_token_tree ( ) ;
416
445
let rhs_tt = parse_one_tt ( rhs_tt, RulePart :: Body , sess, node_id, features, edition) ;
417
446
check_emission ( check_rhs ( sess, & rhs_tt) ) ;
418
- check_emission ( macro_check :: check_meta_variables ( & sess. psess , node_id, & lhs_tt, & rhs_tt) ) ;
447
+ check_emission ( check_meta_variables ( & sess. psess , node_id, args . as_ref ( ) , & lhs_tt, & rhs_tt) ) ;
419
448
let lhs_span = lhs_tt. span ( ) ;
420
449
// Convert the lhs into `MatcherLoc` form, which is better for doing the
421
450
// actual matching.
@@ -424,7 +453,16 @@ pub fn compile_declarative_macro(
424
453
} else {
425
454
return dummy_syn_ext ( guar. unwrap ( ) ) ;
426
455
} ;
427
- rules. push ( MacroRule { lhs, lhs_span, rhs : rhs_tt } ) ;
456
+ if let Some ( args) = args {
457
+ let lhs_span = args. span ( ) . to ( lhs_span) ;
458
+ let mbe:: TokenTree :: Delimited ( .., delimited) = args else {
459
+ return dummy_syn_ext ( guar. unwrap ( ) ) ;
460
+ } ;
461
+ let args = mbe:: macro_parser:: compute_locs ( & delimited. tts ) ;
462
+ rules. push ( MacroRule :: Attr { args, body : lhs, lhs_span, rhs : rhs_tt } ) ;
463
+ } else {
464
+ rules. push ( MacroRule :: Func { lhs, lhs_span, rhs : rhs_tt } ) ;
465
+ }
428
466
if p. token == token:: Eof {
429
467
break ;
430
468
}
@@ -468,6 +506,19 @@ fn check_no_eof(sess: &Session, p: &Parser<'_>, msg: &'static str) -> Option<Err
468
506
None
469
507
}
470
508
509
+ fn check_args_parens ( sess : & Session , args : & tokenstream:: TokenTree ) -> Result < ( ) , ErrorGuaranteed > {
510
+ // This does not handle the non-delimited case; that gets handled separately by `check_lhs`.
511
+ if let tokenstream:: TokenTree :: Delimited ( dspan, _, delim, _) = args
512
+ && * delim != Delimiter :: Parenthesis
513
+ {
514
+ return Err ( sess. dcx ( ) . emit_err ( errors:: MacroArgsBadDelim {
515
+ span : dspan. entire ( ) ,
516
+ sugg : errors:: MacroArgsBadDelimSugg { open : dspan. open , close : dspan. close } ,
517
+ } ) ) ;
518
+ }
519
+ Ok ( ( ) )
520
+ }
521
+
471
522
fn check_lhs ( sess : & Session , node_id : NodeId , lhs : & mbe:: TokenTree ) -> Result < ( ) , ErrorGuaranteed > {
472
523
let e1 = check_lhs_nt_follows ( sess, node_id, lhs) ;
473
524
let e2 = check_lhs_no_empty_seq ( sess, slice:: from_ref ( lhs) ) ;
0 commit comments