@@ -7,29 +7,40 @@ use rustc_macros::Subdiagnostic;
7
7
use rustc_parse:: parser:: { Parser , Recovery , token_descr} ;
8
8
use rustc_session:: parse:: ParseSess ;
9
9
use rustc_span:: source_map:: SourceMap ;
10
- use rustc_span:: { ErrorGuaranteed , Ident , Span } ;
10
+ use rustc_span:: { DUMMY_SP , ErrorGuaranteed , Ident , Span } ;
11
11
use tracing:: debug;
12
12
13
13
use super :: macro_rules:: { MacroRule , NoopTracker , parser_from_cx} ;
14
14
use crate :: expand:: { AstFragmentKind , parse_ast_fragment} ;
15
15
use crate :: mbe:: macro_parser:: ParseResult :: * ;
16
16
use 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 } ;
18
18
19
19
pub ( super ) fn failed_to_match_macro (
20
20
psess : & ParseSess ,
21
21
sp : Span ,
22
22
def_span : Span ,
23
23
name : Ident ,
24
- arg : TokenStream ,
24
+ attr_args : Option < & TokenStream > ,
25
+ body : & TokenStream ,
25
26
rules : & [ MacroRule ] ,
26
27
) -> ( Span , ErrorGuaranteed ) {
27
28
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
+
28
35
// An error occurred, try the expansion again, tracking the expansion closely for better
29
36
// diagnostics.
30
37
let mut tracker = CollectTrackerAndEmitter :: new ( psess. dcx ( ) , sp) ;
31
38
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
+ } ;
33
44
34
45
if try_success_result. is_ok ( ) {
35
46
// Nonterminal parser recovery might turn failed matches into successful ones,
@@ -47,15 +58,27 @@ pub(super) fn failed_to_match_macro(
47
58
48
59
let Some ( BestFailure { token, msg : label, remaining_matcher, .. } ) = tracker. best_failure
49
60
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
+ }
50
73
return ( sp, psess. dcx ( ) . span_delayed_bug ( sp, "failed to match a macro" ) ) ;
51
74
} ;
52
75
53
76
let span = token. span . substitute_dummy ( sp) ;
54
77
55
78
let mut err = psess. dcx ( ) . struct_span_err ( span, parse_failure_msg ( & token, None ) ) ;
56
79
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" ) ;
59
82
}
60
83
61
84
annotate_doc_comment ( & mut err, psess. source_map ( ) , span) ;
@@ -79,13 +102,16 @@ pub(super) fn failed_to_match_macro(
79
102
}
80
103
81
104
// 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
+ {
83
108
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 ) ;
85
111
let mut tt_parser = TtParser :: new ( name) ;
86
112
87
113
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 )
89
115
{
90
116
if comma_span. is_dummy ( ) {
91
117
err. note ( "you might be missing a comma" ) ;
@@ -116,13 +142,13 @@ struct CollectTrackerAndEmitter<'dcx, 'matcher> {
116
142
117
143
struct BestFailure {
118
144
token : Token ,
119
- position_in_tokenstream : u32 ,
145
+ position_in_tokenstream : ( bool , u32 ) ,
120
146
msg : & ' static str ,
121
147
remaining_matcher : MatcherLoc ,
122
148
}
123
149
124
150
impl BestFailure {
125
- fn is_better_position ( & self , position : u32 ) -> bool {
151
+ fn is_better_position ( & self , position : ( bool , u32 ) ) -> bool {
126
152
position > self . position_in_tokenstream
127
153
}
128
154
}
@@ -142,7 +168,7 @@ impl<'dcx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'dcx, 'match
142
168
}
143
169
}
144
170
145
- fn after_arm ( & mut self , result : & NamedParseResult < Self :: Failure > ) {
171
+ fn after_arm ( & mut self , in_body : bool , result : & NamedParseResult < Self :: Failure > ) {
146
172
match result {
147
173
Success ( _) => {
148
174
// Nonterminal parser recovery might turn failed matches into successful ones,
@@ -155,14 +181,15 @@ impl<'dcx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'dcx, 'match
155
181
Failure ( ( token, approx_position, msg) ) => {
156
182
debug ! ( ?token, ?msg, "a new failure of an arm" ) ;
157
183
184
+ let position_in_tokenstream = ( in_body, * approx_position) ;
158
185
if self
159
186
. best_failure
160
187
. 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 ) )
162
189
{
163
190
self . best_failure = Some ( BestFailure {
164
191
token : * token,
165
- position_in_tokenstream : * approx_position ,
192
+ position_in_tokenstream,
166
193
msg,
167
194
remaining_matcher : self
168
195
. remaining_matcher
0 commit comments