@@ -6,12 +6,12 @@ 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_data_structures:: fx:: { FxHashMap , FxIndexMap } ;
14
- use rustc_errors:: { Applicability , Diag , ErrorGuaranteed } ;
14
+ use rustc_errors:: { Applicability , Diag , ErrorGuaranteed , MultiSpan } ;
15
15
use rustc_feature:: Features ;
16
16
use rustc_hir as hir;
17
17
use rustc_hir:: attrs:: AttributeKind ;
@@ -23,7 +23,7 @@ use rustc_lint_defs::builtin::{
23
23
use rustc_parse:: exp;
24
24
use rustc_parse:: parser:: { Parser , Recovery } ;
25
25
use rustc_session:: Session ;
26
- use rustc_session:: parse:: ParseSess ;
26
+ use rustc_session:: parse:: { ParseSess , feature_err } ;
27
27
use rustc_span:: edition:: Edition ;
28
28
use rustc_span:: hygiene:: Transparency ;
29
29
use rustc_span:: { Ident , Span , kw, sym} ;
@@ -35,11 +35,13 @@ use crate::base::{
35
35
DummyResult , ExpandResult , ExtCtxt , MacResult , MacroExpanderResult , SyntaxExtension ,
36
36
SyntaxExtensionKind , TTMacroExpander ,
37
37
} ;
38
+ use crate :: errors;
38
39
use crate :: expand:: { AstFragment , AstFragmentKind , ensure_complete_parse, parse_ast_fragment} ;
40
+ use crate :: mbe:: macro_check:: check_meta_variables;
39
41
use crate :: mbe:: macro_parser:: { Error , ErrorReported , Failure , MatcherLoc , Success , TtParser } ;
40
42
use crate :: mbe:: quoted:: { RulePart , parse_one_tt} ;
41
43
use crate :: mbe:: transcribe:: transcribe;
42
- use crate :: mbe:: { self , KleeneOp , macro_check } ;
44
+ use crate :: mbe:: { self , KleeneOp } ;
43
45
44
46
pub ( crate ) struct ParserAnyMacro < ' a > {
45
47
parser : Parser < ' a > ,
@@ -123,10 +125,18 @@ impl<'a> ParserAnyMacro<'a> {
123
125
}
124
126
}
125
127
126
- pub ( super ) struct MacroRule {
127
- pub ( super ) lhs : Vec < MatcherLoc > ,
128
- lhs_span : Span ,
129
- rhs : mbe:: TokenTree ,
128
+ pub ( super ) enum MacroRule {
129
+ /// A function-style rule, for use with `m!()`
130
+ Func { lhs : Vec < MatcherLoc > , lhs_span : Span , rhs : mbe:: TokenTree } ,
131
+ /// An attr rule, for use with `#[m]`
132
+ #[ expect( unused) ]
133
+ Attr {
134
+ args : Vec < MatcherLoc > ,
135
+ args_span : Span ,
136
+ body : Vec < MatcherLoc > ,
137
+ body_span : Span ,
138
+ rhs : mbe:: TokenTree ,
139
+ } ,
130
140
}
131
141
132
142
pub struct MacroRulesMacroExpander {
@@ -138,10 +148,15 @@ pub struct MacroRulesMacroExpander {
138
148
}
139
149
140
150
impl MacroRulesMacroExpander {
141
- pub fn get_unused_rule ( & self , rule_i : usize ) -> Option < ( & Ident , Span ) > {
151
+ pub fn get_unused_rule ( & self , rule_i : usize ) -> Option < ( & Ident , MultiSpan ) > {
142
152
// If the rhs contains an invocation like `compile_error!`, don't report it as unused.
143
- let rule = & self . rules [ rule_i] ;
144
- if has_compile_error_macro ( & rule. rhs ) { None } else { Some ( ( & self . name , rule. lhs_span ) ) }
153
+ let ( span, rhs) = match self . rules [ rule_i] {
154
+ MacroRule :: Func { lhs_span, ref rhs, .. } => ( MultiSpan :: from_span ( lhs_span) , rhs) ,
155
+ MacroRule :: Attr { args_span, body_span, ref rhs, .. } => {
156
+ ( MultiSpan :: from_spans ( vec ! [ args_span, body_span] ) , rhs)
157
+ }
158
+ } ;
159
+ if has_compile_error_macro ( rhs) { None } else { Some ( ( & self . name , span) ) }
145
160
}
146
161
}
147
162
@@ -245,14 +260,17 @@ fn expand_macro<'cx>(
245
260
246
261
match try_success_result {
247
262
Ok ( ( rule_index, rule, named_matches) ) => {
248
- let mbe:: TokenTree :: Delimited ( rhs_span, _, ref rhs) = rule. rhs else {
263
+ let MacroRule :: Func { rhs, .. } = rule else {
264
+ panic ! ( "try_match_macro returned non-func rule" ) ;
265
+ } ;
266
+ let mbe:: TokenTree :: Delimited ( rhs_span, _, rhs) = rhs else {
249
267
cx. dcx ( ) . span_bug ( sp, "malformed macro rhs" ) ;
250
268
} ;
251
- let arm_span = rule . rhs . span ( ) ;
269
+ let arm_span = rhs_span . entire ( ) ;
252
270
253
271
// rhs has holes ( `$id` and `$(...)` that need filled)
254
272
let id = cx. current_expansion . id ;
255
- let tts = match transcribe ( psess, & named_matches, rhs, rhs_span, transparency, id) {
273
+ let tts = match transcribe ( psess, & named_matches, rhs, * rhs_span, transparency, id) {
256
274
Ok ( tts) => tts,
257
275
Err ( err) => {
258
276
let guar = err. emit ( ) ;
@@ -327,6 +345,7 @@ pub(super) fn try_match_macro<'matcher, T: Tracker<'matcher>>(
327
345
// Try each arm's matchers.
328
346
let mut tt_parser = TtParser :: new ( name) ;
329
347
for ( i, rule) in rules. iter ( ) . enumerate ( ) {
348
+ let MacroRule :: Func { lhs, .. } = rule else { continue } ;
330
349
let _tracing_span = trace_span ! ( "Matching arm" , %i) ;
331
350
332
351
// Take a snapshot of the state of pre-expansion gating at this point.
@@ -335,7 +354,7 @@ pub(super) fn try_match_macro<'matcher, T: Tracker<'matcher>>(
335
354
// are not recorded. On the first `Success(..)`ful matcher, the spans are merged.
336
355
let mut gated_spans_snapshot = mem:: take ( & mut * psess. gated_spans . spans . borrow_mut ( ) ) ;
337
356
338
- let result = tt_parser. parse_tt ( & mut Cow :: Borrowed ( & parser) , & rule . lhs , track) ;
357
+ let result = tt_parser. parse_tt ( & mut Cow :: Borrowed ( & parser) , lhs, track) ;
339
358
340
359
track. after_arm ( & result) ;
341
360
@@ -404,6 +423,25 @@ pub fn compile_declarative_macro(
404
423
let mut rules = Vec :: new ( ) ;
405
424
406
425
while p. token != token:: Eof {
426
+ let args = if p. eat_keyword_noexpect ( sym:: attr) {
427
+ if !features. macro_attr ( ) {
428
+ feature_err ( sess, sym:: macro_attr, span, "`macro_rules!` attributes are unstable" )
429
+ . emit ( ) ;
430
+ }
431
+ if let Some ( guar) = check_no_eof ( sess, & p, "expected macro attr args" ) {
432
+ return dummy_syn_ext ( guar) ;
433
+ }
434
+ let args = p. parse_token_tree ( ) ;
435
+ check_args_parens ( sess, & args) ;
436
+ let args = parse_one_tt ( args, RulePart :: Pattern , sess, node_id, features, edition) ;
437
+ check_emission ( check_lhs ( sess, node_id, & args) ) ;
438
+ if let Some ( guar) = check_no_eof ( sess, & p, "expected macro attr body" ) {
439
+ return dummy_syn_ext ( guar) ;
440
+ }
441
+ Some ( args)
442
+ } else {
443
+ None
444
+ } ;
407
445
let lhs_tt = p. parse_token_tree ( ) ;
408
446
let lhs_tt = parse_one_tt ( lhs_tt, RulePart :: Pattern , sess, node_id, features, edition) ;
409
447
check_emission ( check_lhs ( sess, node_id, & lhs_tt) ) ;
@@ -416,7 +454,7 @@ pub fn compile_declarative_macro(
416
454
let rhs_tt = p. parse_token_tree ( ) ;
417
455
let rhs_tt = parse_one_tt ( rhs_tt, RulePart :: Body , sess, node_id, features, edition) ;
418
456
check_emission ( check_rhs ( sess, & rhs_tt) ) ;
419
- check_emission ( macro_check :: check_meta_variables ( & sess. psess , node_id, & lhs_tt, & rhs_tt) ) ;
457
+ check_emission ( check_meta_variables ( & sess. psess , node_id, args . as_ref ( ) , & lhs_tt, & rhs_tt) ) ;
420
458
let lhs_span = lhs_tt. span ( ) ;
421
459
// Convert the lhs into `MatcherLoc` form, which is better for doing the
422
460
// actual matching.
@@ -425,7 +463,17 @@ pub fn compile_declarative_macro(
425
463
} else {
426
464
return dummy_syn_ext ( guar. unwrap ( ) ) ;
427
465
} ;
428
- rules. push ( MacroRule { lhs, lhs_span, rhs : rhs_tt } ) ;
466
+ if let Some ( args) = args {
467
+ let args_span = args. span ( ) ;
468
+ let mbe:: TokenTree :: Delimited ( .., delimited) = args else {
469
+ return dummy_syn_ext ( guar. unwrap ( ) ) ;
470
+ } ;
471
+ let args = mbe:: macro_parser:: compute_locs ( & delimited. tts ) ;
472
+ let body_span = lhs_span;
473
+ rules. push ( MacroRule :: Attr { args, args_span, body : lhs, body_span, rhs : rhs_tt } ) ;
474
+ } else {
475
+ rules. push ( MacroRule :: Func { lhs, lhs_span, rhs : rhs_tt } ) ;
476
+ }
429
477
if p. token == token:: Eof {
430
478
break ;
431
479
}
@@ -469,6 +517,18 @@ fn check_no_eof(sess: &Session, p: &Parser<'_>, msg: &'static str) -> Option<Err
469
517
None
470
518
}
471
519
520
+ fn check_args_parens ( sess : & Session , args : & tokenstream:: TokenTree ) {
521
+ // This does not handle the non-delimited case; that gets handled separately by `check_lhs`.
522
+ if let tokenstream:: TokenTree :: Delimited ( dspan, _, delim, _) = args
523
+ && * delim != Delimiter :: Parenthesis
524
+ {
525
+ sess. dcx ( ) . emit_err ( errors:: MacroArgsBadDelim {
526
+ span : dspan. entire ( ) ,
527
+ sugg : errors:: MacroArgsBadDelimSugg { open : dspan. open , close : dspan. close } ,
528
+ } ) ;
529
+ }
530
+ }
531
+
472
532
fn check_lhs ( sess : & Session , node_id : NodeId , lhs : & mbe:: TokenTree ) -> Result < ( ) , ErrorGuaranteed > {
473
533
let e1 = check_lhs_nt_follows ( sess, node_id, lhs) ;
474
534
let e2 = check_lhs_no_empty_seq ( sess, slice:: from_ref ( lhs) ) ;
0 commit comments