@@ -6,8 +6,8 @@ use std::{mem, slice};
66use ast:: token:: IdentIsRaw ;
77use rustc_ast:: token:: NtPatKind :: * ;
88use 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 } ;
1111use rustc_ast:: { self as ast, DUMMY_NODE_ID , NodeId } ;
1212use rustc_ast_pretty:: pprust;
1313use rustc_data_structures:: fx:: { FxHashMap , FxIndexMap } ;
@@ -23,7 +23,7 @@ use rustc_lint_defs::builtin::{
2323use rustc_parse:: exp;
2424use rustc_parse:: parser:: { Parser , Recovery } ;
2525use rustc_session:: Session ;
26- use rustc_session:: parse:: ParseSess ;
26+ use rustc_session:: parse:: { ParseSess , feature_err } ;
2727use rustc_span:: edition:: Edition ;
2828use rustc_span:: hygiene:: Transparency ;
2929use rustc_span:: { Ident , Span , kw, sym} ;
@@ -35,11 +35,13 @@ use crate::base::{
3535 DummyResult , ExpandResult , ExtCtxt , MacResult , MacroExpanderResult , SyntaxExtension ,
3636 SyntaxExtensionKind , TTMacroExpander ,
3737} ;
38+ use crate :: errors;
3839use crate :: expand:: { AstFragment , AstFragmentKind , ensure_complete_parse, parse_ast_fragment} ;
40+ use crate :: mbe:: macro_check:: check_meta_variables;
3941use crate :: mbe:: macro_parser:: { Error , ErrorReported , Failure , MatcherLoc , Success , TtParser } ;
4042use crate :: mbe:: quoted:: { RulePart , parse_one_tt} ;
4143use crate :: mbe:: transcribe:: transcribe;
42- use crate :: mbe:: { self , KleeneOp , macro_check } ;
44+ use crate :: mbe:: { self , KleeneOp } ;
4345
4446pub ( crate ) struct ParserAnyMacro < ' a > {
4547 parser : Parser < ' a > ,
@@ -123,10 +125,12 @@ impl<'a> ParserAnyMacro<'a> {
123125 }
124126}
125127
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 { args : Vec < MatcherLoc > , body : Vec < MatcherLoc > , lhs_span : Span , rhs : mbe:: TokenTree } ,
130134}
131135
132136pub struct MacroRulesMacroExpander {
@@ -140,8 +144,9 @@ pub struct MacroRulesMacroExpander {
140144impl MacroRulesMacroExpander {
141145 pub fn get_unused_rule ( & self , rule_i : usize ) -> Option < ( & Ident , Span ) > {
142146 // 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 ) ) }
147+ let ( MacroRule :: Func { lhs_span, ref rhs, .. } | MacroRule :: Attr { lhs_span, ref rhs, .. } ) =
148+ self . rules [ rule_i] ;
149+ if has_compile_error_macro ( rhs) { None } else { Some ( ( & self . name , lhs_span) ) }
145150 }
146151}
147152
@@ -245,14 +250,17 @@ fn expand_macro<'cx>(
245250
246251 match try_success_result {
247252 Ok ( ( rule_index, rule, named_matches) ) => {
248- let mbe:: TokenTree :: Delimited ( rhs_span, _, ref rhs) = rule. rhs else {
253+ let MacroRule :: Func { rhs, .. } = rule else {
254+ panic ! ( "try_match_macro returned non-func rule" ) ;
255+ } ;
256+ let mbe:: TokenTree :: Delimited ( rhs_span, _, rhs) = rhs else {
249257 cx. dcx ( ) . span_bug ( sp, "malformed macro rhs" ) ;
250258 } ;
251- let arm_span = rule . rhs . span ( ) ;
259+ let arm_span = rhs_span . entire ( ) ;
252260
253261 // rhs has holes ( `$id` and `$(...)` that need filled)
254262 let id = cx. current_expansion . id ;
255- let tts = match transcribe ( psess, & named_matches, rhs, rhs_span, transparency, id) {
263+ let tts = match transcribe ( psess, & named_matches, rhs, * rhs_span, transparency, id) {
256264 Ok ( tts) => tts,
257265 Err ( err) => {
258266 let guar = err. emit ( ) ;
@@ -327,6 +335,7 @@ pub(super) fn try_match_macro<'matcher, T: Tracker<'matcher>>(
327335 // Try each arm's matchers.
328336 let mut tt_parser = TtParser :: new ( name) ;
329337 for ( i, rule) in rules. iter ( ) . enumerate ( ) {
338+ let MacroRule :: Func { lhs, .. } = rule else { continue } ;
330339 let _tracing_span = trace_span ! ( "Matching arm" , %i) ;
331340
332341 // Take a snapshot of the state of pre-expansion gating at this point.
@@ -335,7 +344,7 @@ pub(super) fn try_match_macro<'matcher, T: Tracker<'matcher>>(
335344 // are not recorded. On the first `Success(..)`ful matcher, the spans are merged.
336345 let mut gated_spans_snapshot = mem:: take ( & mut * psess. gated_spans . spans . borrow_mut ( ) ) ;
337346
338- let result = tt_parser. parse_tt ( & mut Cow :: Borrowed ( & parser) , & rule . lhs , track) ;
347+ let result = tt_parser. parse_tt ( & mut Cow :: Borrowed ( & parser) , lhs, track) ;
339348
340349 track. after_arm ( & result) ;
341350
@@ -404,6 +413,26 @@ pub fn compile_declarative_macro(
404413 let mut rules = Vec :: new ( ) ;
405414
406415 while p. token != token:: Eof {
416+ let args = if p. eat_keyword_noexpect ( sym:: attr) {
417+ if !features. macro_attr ( ) {
418+ let msg = "`macro_rules!` attributes are unstable" ;
419+ let e = feature_err ( sess, sym:: macro_attr, span, msg) ;
420+ return dummy_syn_ext ( e. emit ( ) ) ;
421+ }
422+ if let Some ( guar) = check_no_eof ( sess, & p, "expected macro attr args" ) {
423+ return dummy_syn_ext ( guar) ;
424+ }
425+ let args = p. parse_token_tree ( ) ;
426+ check_emission ( check_args_parens ( sess, & args) ) ;
427+ let args = parse_one_tt ( args, RulePart :: Pattern , sess, node_id, features, edition) ;
428+ check_emission ( check_lhs ( sess, node_id, & args) ) ;
429+ if let Some ( guar) = check_no_eof ( sess, & p, "expected macro attr body" ) {
430+ return dummy_syn_ext ( guar) ;
431+ }
432+ Some ( args)
433+ } else {
434+ None
435+ } ;
407436 let lhs_tt = p. parse_token_tree ( ) ;
408437 let lhs_tt = parse_one_tt ( lhs_tt, RulePart :: Pattern , sess, node_id, features, edition) ;
409438 check_emission ( check_lhs ( sess, node_id, & lhs_tt) ) ;
@@ -416,7 +445,7 @@ pub fn compile_declarative_macro(
416445 let rhs_tt = p. parse_token_tree ( ) ;
417446 let rhs_tt = parse_one_tt ( rhs_tt, RulePart :: Body , sess, node_id, features, edition) ;
418447 check_emission ( check_rhs ( sess, & rhs_tt) ) ;
419- check_emission ( macro_check :: check_meta_variables ( & sess. psess , node_id, & lhs_tt, & rhs_tt) ) ;
448+ check_emission ( check_meta_variables ( & sess. psess , node_id, args . as_ref ( ) , & lhs_tt, & rhs_tt) ) ;
420449 let lhs_span = lhs_tt. span ( ) ;
421450 // Convert the lhs into `MatcherLoc` form, which is better for doing the
422451 // actual matching.
@@ -425,7 +454,16 @@ pub fn compile_declarative_macro(
425454 } else {
426455 return dummy_syn_ext ( guar. unwrap ( ) ) ;
427456 } ;
428- rules. push ( MacroRule { lhs, lhs_span, rhs : rhs_tt } ) ;
457+ if let Some ( args) = args {
458+ let lhs_span = args. span ( ) . to ( lhs_span) ;
459+ let mbe:: TokenTree :: Delimited ( .., delimited) = args else {
460+ return dummy_syn_ext ( guar. unwrap ( ) ) ;
461+ } ;
462+ let args = mbe:: macro_parser:: compute_locs ( & delimited. tts ) ;
463+ rules. push ( MacroRule :: Attr { args, body : lhs, lhs_span, rhs : rhs_tt } ) ;
464+ } else {
465+ rules. push ( MacroRule :: Func { lhs, lhs_span, rhs : rhs_tt } ) ;
466+ }
429467 if p. token == token:: Eof {
430468 break ;
431469 }
@@ -469,6 +507,19 @@ fn check_no_eof(sess: &Session, p: &Parser<'_>, msg: &'static str) -> Option<Err
469507 None
470508}
471509
510+ fn check_args_parens ( sess : & Session , args : & tokenstream:: TokenTree ) -> Result < ( ) , ErrorGuaranteed > {
511+ // This does not handle the non-delimited case; that gets handled separately by `check_lhs`.
512+ if let tokenstream:: TokenTree :: Delimited ( dspan, _, delim, _) = args
513+ && * delim != Delimiter :: Parenthesis
514+ {
515+ return Err ( sess. dcx ( ) . emit_err ( errors:: MacroArgsBadDelim {
516+ span : dspan. entire ( ) ,
517+ sugg : errors:: MacroArgsBadDelimSugg { open : dspan. open , close : dspan. close } ,
518+ } ) ) ;
519+ }
520+ Ok ( ( ) )
521+ }
522+
472523fn check_lhs ( sess : & Session , node_id : NodeId , lhs : & mbe:: TokenTree ) -> Result < ( ) , ErrorGuaranteed > {
473524 let e1 = check_lhs_nt_follows ( sess, node_id, lhs) ;
474525 let e2 = check_lhs_no_empty_seq ( sess, slice:: from_ref ( lhs) ) ;
0 commit comments