@@ -7,29 +7,40 @@ use rustc_macros::Subdiagnostic;
77use rustc_parse:: parser:: { Parser , Recovery , token_descr} ;
88use rustc_session:: parse:: ParseSess ;
99use rustc_span:: source_map:: SourceMap ;
10- use rustc_span:: { ErrorGuaranteed , Ident , Span } ;
10+ use rustc_span:: { DUMMY_SP , ErrorGuaranteed , Ident , Span } ;
1111use tracing:: debug;
1212
1313use super :: macro_rules:: { MacroRule , NoopTracker , parser_from_cx} ;
1414use crate :: expand:: { AstFragmentKind , parse_ast_fragment} ;
1515use crate :: mbe:: macro_parser:: ParseResult :: * ;
1616use crate :: mbe:: macro_parser:: { MatcherLoc , NamedParseResult , TtParser } ;
17- use crate :: mbe:: macro_rules:: { Tracker , try_match_macro} ;
17+ use crate :: mbe:: macro_rules:: { Tracker , try_match_macro, try_match_macro_attr } ;
1818
1919pub ( super ) fn failed_to_match_macro (
2020 psess : & ParseSess ,
2121 sp : Span ,
2222 def_span : Span ,
2323 name : Ident ,
24- arg : TokenStream ,
24+ attr_args : Option < & TokenStream > ,
25+ body : & TokenStream ,
2526 rules : & [ MacroRule ] ,
2627) -> ( Span , ErrorGuaranteed ) {
2728 debug ! ( "failed to match macro" ) ;
29+ let def_head_span = if !def_span. is_dummy ( ) && !psess. source_map ( ) . is_imported ( def_span) {
30+ psess. source_map ( ) . guess_head_span ( def_span)
31+ } else {
32+ DUMMY_SP
33+ } ;
34+
2835 // An error occurred, try the expansion again, tracking the expansion closely for better
2936 // diagnostics.
3037 let mut tracker = CollectTrackerAndEmitter :: new ( psess. dcx ( ) , sp) ;
3138
32- let try_success_result = try_match_macro ( psess, name, & arg, rules, & mut tracker) ;
39+ let try_success_result = if let Some ( attr_args) = attr_args {
40+ try_match_macro_attr ( psess, name, attr_args, body, rules, & mut tracker)
41+ } else {
42+ try_match_macro ( psess, name, body, rules, & mut tracker)
43+ } ;
3344
3445 if try_success_result. is_ok ( ) {
3546 // Nonterminal parser recovery might turn failed matches into successful ones,
@@ -47,15 +58,27 @@ pub(super) fn failed_to_match_macro(
4758
4859 let Some ( BestFailure { token, msg : label, remaining_matcher, .. } ) = tracker. best_failure
4960 else {
61+ // FIXME: we should report this at macro resolution time, as we do for
62+ // `resolve_macro_cannot_use_as_attr`. We can do that once we track multiple macro kinds for a
63+ // Def.
64+ if attr_args. is_none ( ) && !rules. iter ( ) . any ( |rule| matches ! ( rule, MacroRule :: Func { .. } ) ) {
65+ let msg = format ! ( "macro has no rules for function-like invocation `{name}!`" ) ;
66+ let mut err = psess. dcx ( ) . struct_span_err ( sp, msg) ;
67+ if !def_head_span. is_dummy ( ) {
68+ let msg = "this macro has no rules for function-like invocation" ;
69+ err. span_label ( def_head_span, msg) ;
70+ }
71+ return ( sp, err. emit ( ) ) ;
72+ }
5073 return ( sp, psess. dcx ( ) . span_delayed_bug ( sp, "failed to match a macro" ) ) ;
5174 } ;
5275
5376 let span = token. span . substitute_dummy ( sp) ;
5477
5578 let mut err = psess. dcx ( ) . struct_span_err ( span, parse_failure_msg ( & token, None ) ) ;
5679 err. span_label ( span, label) ;
57- if !def_span . is_dummy ( ) && !psess . source_map ( ) . is_imported ( def_span ) {
58- err. span_label ( psess . source_map ( ) . guess_head_span ( def_span ) , "when calling this macro" ) ;
80+ if !def_head_span . is_dummy ( ) {
81+ err. span_label ( def_head_span , "when calling this macro" ) ;
5982 }
6083
6184 annotate_doc_comment ( & mut err, psess. source_map ( ) , span) ;
@@ -79,13 +102,16 @@ pub(super) fn failed_to_match_macro(
79102 }
80103
81104 // Check whether there's a missing comma in this macro call, like `println!("{}" a);`
82- if let Some ( ( arg, comma_span) ) = arg. add_comma ( ) {
105+ if attr_args. is_none ( )
106+ && let Some ( ( body, comma_span) ) = body. add_comma ( )
107+ {
83108 for rule in rules {
84- let parser = parser_from_cx ( psess, arg. clone ( ) , Recovery :: Allowed ) ;
109+ let MacroRule :: Func { lhs, .. } = rule else { continue } ;
110+ let parser = parser_from_cx ( psess, body. clone ( ) , Recovery :: Allowed ) ;
85111 let mut tt_parser = TtParser :: new ( name) ;
86112
87113 if let Success ( _) =
88- tt_parser. parse_tt ( & mut Cow :: Borrowed ( & parser) , & rule . lhs , & mut NoopTracker )
114+ tt_parser. parse_tt ( & mut Cow :: Borrowed ( & parser) , lhs, & mut NoopTracker )
89115 {
90116 if comma_span. is_dummy ( ) {
91117 err. note ( "you might be missing a comma" ) ;
@@ -116,13 +142,13 @@ struct CollectTrackerAndEmitter<'dcx, 'matcher> {
116142
117143struct BestFailure {
118144 token : Token ,
119- position_in_tokenstream : u32 ,
145+ position_in_tokenstream : ( bool , u32 ) ,
120146 msg : & ' static str ,
121147 remaining_matcher : MatcherLoc ,
122148}
123149
124150impl BestFailure {
125- fn is_better_position ( & self , position : u32 ) -> bool {
151+ fn is_better_position ( & self , position : ( bool , u32 ) ) -> bool {
126152 position > self . position_in_tokenstream
127153 }
128154}
@@ -142,7 +168,7 @@ impl<'dcx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'dcx, 'match
142168 }
143169 }
144170
145- fn after_arm ( & mut self , result : & NamedParseResult < Self :: Failure > ) {
171+ fn after_arm ( & mut self , in_body : bool , result : & NamedParseResult < Self :: Failure > ) {
146172 match result {
147173 Success ( _) => {
148174 // Nonterminal parser recovery might turn failed matches into successful ones,
@@ -155,14 +181,15 @@ impl<'dcx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'dcx, 'match
155181 Failure ( ( token, approx_position, msg) ) => {
156182 debug ! ( ?token, ?msg, "a new failure of an arm" ) ;
157183
184+ let position_in_tokenstream = ( in_body, * approx_position) ;
158185 if self
159186 . best_failure
160187 . as_ref ( )
161- . is_none_or ( |failure| failure. is_better_position ( * approx_position ) )
188+ . is_none_or ( |failure| failure. is_better_position ( position_in_tokenstream ) )
162189 {
163190 self . best_failure = Some ( BestFailure {
164191 token : * token,
165- position_in_tokenstream : * approx_position ,
192+ position_in_tokenstream,
166193 msg,
167194 remaining_matcher : self
168195 . remaining_matcher
0 commit comments