1use std::borrow::Cow;
2
3use rustc_ast::token::{self, Token};
4use rustc_ast::tokenstream::TokenStream;
5use rustc_data_structures::fx::FxHashSet;
6use rustc_errors::{Applicability, Diag, DiagCtxtHandle, DiagMessage, pluralize};
7use rustc_hir::attrs::diagnostic::{CustomDiagnostic, Directive, FormatArgs};
8use rustc_macros::Subdiagnostic;
9use rustc_middle::bug;
10use rustc_parse::parser::{Parser, Recovery, token_descr};
11use rustc_session::parse::ParseSess;
12use rustc_span::source_map::SourceMap;
13use rustc_span::{DUMMY_SP, ErrorGuaranteed, Ident, Span};
14use tracing::debug;
15
16use super::macro_rules::{MacroRule, NoopTracker, parser_from_cx};
17use crate::expand::{AstFragmentKind, parse_ast_fragment};
18use crate::mbe::macro_parser::ParseResult::*;
19use crate::mbe::macro_parser::{MatcherLoc, NamedParseResult, TtParser};
20use crate::mbe::macro_rules::{
21 Tracker, WhichMatcher, try_match_macro, try_match_macro_attr, try_match_macro_derive,
22};
23
24pub(super) enum FailedMacro<'a> {
25 Func,
26 Attr(&'a TokenStream),
27 Derive,
28}
29
30pub(super) fn failed_to_match_macro(
31 psess: &ParseSess,
32 sp: Span,
33 def_span: Span,
34 name: Ident,
35 args: FailedMacro<'_>,
36 body: &TokenStream,
37 rules: &[MacroRule],
38 on_unmatched_args: Option<&Directive>,
39) -> (Span, ErrorGuaranteed) {
40 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_expand/src/mbe/diagnostics.rs:40",
"rustc_expand::mbe::diagnostics", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_expand/src/mbe/diagnostics.rs"),
::tracing_core::__macro_support::Option::Some(40u32),
::tracing_core::__macro_support::Option::Some("rustc_expand::mbe::diagnostics"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("failed to match macro")
as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!("failed to match macro");
41 let def_head_span = if !def_span.is_dummy() && !psess.source_map().is_imported(def_span) {
42 psess.source_map().guess_head_span(def_span)
43 } else {
44 DUMMY_SP
45 };
46
47 let mut tracker = CollectTrackerAndEmitter::new(name, psess.dcx(), sp);
50
51 let try_success_result = match args {
52 FailedMacro::Func => try_match_macro(psess, name, body, rules, &mut tracker),
53 FailedMacro::Attr(attr_args) => {
54 try_match_macro_attr(psess, name, attr_args, body, rules, &mut tracker)
55 }
56 FailedMacro::Derive => try_match_macro_derive(psess, name, body, rules, &mut tracker),
57 };
58
59 if try_success_result.is_ok() {
60 if !tracker.dcx.has_errors().is_some() {
{
::core::panicking::panic_fmt(format_args!("Macro matching returned a success on the second try"));
}
};assert!(
63 tracker.dcx.has_errors().is_some(),
64 "Macro matching returned a success on the second try"
65 );
66 }
67
68 if let Some(result) = tracker.result {
69 return result;
71 }
72
73 let Some(BestFailure { token, msg: label, remaining_matcher, .. }) = tracker.best_failure
74 else {
75 return (sp, psess.dcx().span_delayed_bug(sp, "failed to match a macro"));
76 };
77
78 let span = token.span.substitute_dummy(sp);
79 let CustomDiagnostic {
80 message: custom_message, label: custom_label, notes: custom_notes, ..
81 } = {
82 on_unmatched_args
83 .map(|directive| directive.eval(None, &FormatArgs { this: name.to_string(), .. }))
84 .unwrap_or_default()
85 };
86
87 let mut err = match custom_message {
88 Some(message) => psess.dcx().struct_span_err(span, message),
89 None => psess.dcx().struct_span_err(span, parse_failure_msg(&token, None)),
90 };
91 err.span_label(span, custom_label.unwrap_or_else(|| label.to_string()));
92 if !def_head_span.is_dummy() {
93 err.span_label(def_head_span, "when calling this macro");
94 }
95
96 annotate_doc_comment(&mut err, psess.source_map(), span);
97
98 if let Some(span) = remaining_matcher.span() {
99 err.span_note(span, ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("while trying to match {0}",
remaining_matcher))
})format!("while trying to match {remaining_matcher}"));
100 } else {
101 err.note(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("while trying to match {0}",
remaining_matcher))
})format!("while trying to match {remaining_matcher}"));
102 }
103 for note in custom_notes {
104 err.note(note);
105 }
106
107 if let MatcherLoc::Token { token: expected_token } = &remaining_matcher
108 && (#[allow(non_exhaustive_omitted_patterns)] match expected_token.kind {
token::OpenInvisible(_) => true,
_ => false,
}matches!(expected_token.kind, token::OpenInvisible(_))
109 || #[allow(non_exhaustive_omitted_patterns)] match token.kind {
token::OpenInvisible(_) => true,
_ => false,
}matches!(token.kind, token::OpenInvisible(_)))
110 {
111 err.note("captured metavariables except for `:tt`, `:ident` and `:lifetime` cannot be compared to other tokens");
112 err.note("see <https://doc.rust-lang.org/nightly/reference/macros-by-example.html#forwarding-a-matched-fragment> for more information");
113
114 if !def_span.is_dummy() && !psess.source_map().is_imported(def_span) {
115 err.help("try using `:tt` instead in the macro definition");
116 }
117 }
118
119 if let FailedMacro::Func = args
121 && let Some((body, comma_span)) = body.add_comma()
122 {
123 for rule in rules {
124 let MacroRule::Func { lhs, .. } = rule else { continue };
125 let parser = parser_from_cx(psess, body.clone(), Recovery::Allowed);
126 let mut tt_parser = TtParser::new();
127
128 if let Success(_) =
129 tt_parser.parse_tt(&mut Cow::Borrowed(&parser), lhs, &mut NoopTracker)
130 {
131 if comma_span.is_dummy() {
132 err.note("you might be missing a comma");
133 } else {
134 err.span_suggestion_short(
135 comma_span,
136 "missing comma here",
137 ", ",
138 Applicability::MachineApplicable,
139 );
140 }
141 }
142 }
143 }
144 let guar = err.emit();
145 (sp, guar)
146}
147
148struct CollectTrackerAndEmitter<'dcx, 'matcher> {
150 macro_name: Ident,
151 dcx: DiagCtxtHandle<'dcx>,
152
153 current: Option<(WhichMatcher, &'matcher [MatcherLoc])>,
157
158 matches: FxHashSet<SuccessfulMatch>,
163
164 remaining_matcher: Option<&'matcher MatcherLoc>,
165 best_failure: Option<BestFailure>,
167 root_span: Span,
168 result: Option<(Span, ErrorGuaranteed)>,
169}
170
171#[derive(#[automatically_derived]
impl ::core::marker::Copy for SuccessfulMatch { }Copy, #[automatically_derived]
impl ::core::clone::Clone for SuccessfulMatch {
#[inline]
fn clone(&self) -> SuccessfulMatch {
let _: ::core::clone::AssertParamIsClone<u32>;
*self
}
}Clone, #[automatically_derived]
impl ::core::fmt::Debug for SuccessfulMatch {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field2_finish(f,
"SuccessfulMatch", "input_pos", &self.input_pos, "loc_index",
&&self.loc_index)
}
}Debug, #[automatically_derived]
impl ::core::cmp::PartialEq for SuccessfulMatch {
#[inline]
fn eq(&self, other: &SuccessfulMatch) -> bool {
self.input_pos == other.input_pos && self.loc_index == other.loc_index
}
}PartialEq, #[automatically_derived]
impl ::core::cmp::Eq for SuccessfulMatch {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<u32>;
}
}Eq, #[automatically_derived]
impl ::core::cmp::PartialOrd for SuccessfulMatch {
#[inline]
fn partial_cmp(&self, other: &SuccessfulMatch)
-> ::core::option::Option<::core::cmp::Ordering> {
::core::option::Option::Some(::core::cmp::Ord::cmp(self, other))
}
}PartialOrd, #[automatically_derived]
impl ::core::cmp::Ord for SuccessfulMatch {
#[inline]
fn cmp(&self, other: &SuccessfulMatch) -> ::core::cmp::Ordering {
match ::core::cmp::Ord::cmp(&self.input_pos, &other.input_pos) {
::core::cmp::Ordering::Equal =>
::core::cmp::Ord::cmp(&self.loc_index, &other.loc_index),
cmp => cmp,
}
}
}Ord, #[automatically_derived]
impl ::core::hash::Hash for SuccessfulMatch {
#[inline]
fn hash<__H: ::core::hash::Hasher>(&self, state: &mut __H) {
::core::hash::Hash::hash(&self.input_pos, state);
::core::hash::Hash::hash(&self.loc_index, state)
}
}Hash)]
172struct SuccessfulMatch {
173 input_pos: u32,
177
178 loc_index: u32,
180}
181
182struct BestFailure {
183 token: Token,
184
185 matcher: WhichMatcher,
187
188 position: u32,
192
193 msg: &'static str,
194 remaining_matcher: MatcherLoc,
195}
196
197impl BestFailure {
198 fn is_better_position(&self, matcher: WhichMatcher, position: u32) -> bool {
199 (matcher, position) > (self.matcher, self.position)
200 }
201}
202
203impl<'dcx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'dcx, 'matcher> {
204 fn prepare(&mut self, which_matcher: WhichMatcher, matcher: &'matcher [MatcherLoc]) {
205 if self.current.is_some() {
206 ::rustc_middle::util::bug::bug_fmt(format_args!("`Self::after_arm()` was not called to clean up context"));bug!("`Self::after_arm()` was not called to clean up context");
207 }
208
209 self.current = Some((which_matcher, matcher));
210 }
211
212 fn before_match_loc(&mut self, parser: &TtParser, matcher: &'matcher MatcherLoc) {
213 if self.remaining_matcher.is_none()
214 || (parser.has_no_remaining_items_for_step() && *matcher != MatcherLoc::Eof)
215 {
216 self.remaining_matcher = Some(matcher);
217 }
218 }
219
220 fn matched_one(&mut self, parser: &Parser<'_>, loc_index: usize) {
221 let input_pos = parser.approx_token_stream_pos();
222 let loc_index: u32 = loc_index.try_into().unwrap();
223 let m = SuccessfulMatch { input_pos, loc_index };
224 self.matches.insert(m);
225 }
226
227 fn after_arm(&mut self, result: &NamedParseResult) {
228 match *result {
229 Success(_) => {
230 self.dcx.span_delayed_bug(
233 self.root_span,
234 "should not collect detailed info for successful macro match",
235 );
236 }
237 Failure => {
238 if self.best_failure.is_none() {
239 ::rustc_middle::util::bug::bug_fmt(format_args!("A matching failure occurred but `Self::failure()` was not called"));bug!("A matching failure occurred but `Self::failure()` was not called");
240 }
241 }
242 Ambiguity => {
243 if self.result.is_none() {
244 ::rustc_middle::util::bug::bug_fmt(format_args!("An ambiguity error occurred but `Self::ambiguity()` was not called"));bug!("An ambiguity error occurred but `Self::ambiguity()` was not called");
245 }
246 }
247 ErrorReported(guar) => self.result = Some((self.root_span, guar)),
248 }
249
250 self.current = None;
251 self.matches.clear();
252 }
253
254 fn failure(&mut self, parser: &Parser<'_>) {
255 let Some((which_matcher, _)) = self.current else {
256 ::rustc_middle::util::bug::bug_fmt(format_args!("`Self::prepare()` was not called to initialize context"));bug!("`Self::prepare()` was not called to initialize context");
257 };
258
259 let mut token = parser.token;
260 let approx_position = parser.approx_token_stream_pos();
261 let msg = if token.kind == token::Eof {
262 if !token.span.is_dummy() {
264 token.span = token.span.shrink_to_hi();
265 }
266 "missing tokens in macro arguments"
267 } else {
268 "no rules expected this token in macro call"
269 };
270
271 {
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event compiler/rustc_expand/src/mbe/diagnostics.rs:271",
"rustc_expand::mbe::diagnostics", ::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_expand/src/mbe/diagnostics.rs"),
::tracing_core::__macro_support::Option::Some(271u32),
::tracing_core::__macro_support::Option::Some("rustc_expand::mbe::diagnostics"),
::tracing_core::field::FieldSet::new(&["message",
{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("token")
}> =
::tracing::__macro_support::FieldName::new("token");
NAME.as_str()
},
{
const NAME:
::tracing::__macro_support::FieldName<{
::tracing::__macro_support::FieldName::len("msg")
}> =
::tracing::__macro_support::FieldName::new("msg");
NAME.as_str()
}], ::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("a new failure of an arm")
as &dyn ::tracing::field::Value)),
(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&token)
as &dyn ::tracing::field::Value)),
(::tracing::__macro_support::Option::Some(&::tracing::field::debug(&msg)
as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!(?token, ?msg, "a new failure of an arm");
272
273 if self
274 .best_failure
275 .as_ref()
276 .is_none_or(|failure| failure.is_better_position(which_matcher, approx_position))
277 {
278 self.best_failure = Some(BestFailure {
279 token,
280 matcher: which_matcher,
281 position: approx_position,
282 msg,
283 remaining_matcher: self
284 .remaining_matcher
285 .expect("must have collected matcher already")
286 .clone(),
287 })
288 }
289 }
290
291 fn ambiguity(&mut self, parser: &Parser<'_>) {
292 let Some((_, matcher)) = self.current else {
293 ::rustc_middle::util::bug::bug_fmt(format_args!("`Self::prepare()` was not called to initialize context"));bug!("`Self::prepare()` was not called to initialize context");
294 };
295
296 #[expect(
297 rustc::potential_query_instability,
298 reason = "sorting the results deterministically afterwards"
299 )]
300 let (mut bb_locs, mut next_locs) = self
301 .matches
302 .iter()
303 .filter(|m| m.input_pos == parser.approx_token_stream_pos())
304 .partition::<Vec<&SuccessfulMatch>, _>(|m| {
305 let loc = &matcher[m.loc_index as usize];
306 #[allow(non_exhaustive_omitted_patterns)] match loc {
MatcherLoc::MetaVarDecl { .. } => true,
_ => false,
}matches!(loc, MatcherLoc::MetaVarDecl { .. })
307 });
308
309 bb_locs.sort_unstable_by_key(|m| m.loc_index);
311 next_locs.sort_unstable_by_key(|m| m.loc_index);
312
313 let span = parser.token.span.substitute_dummy(self.root_span);
314
315 if parser.token == token::Eof {
316 let msg = "ambiguity: multiple successful parses".to_string();
317 let guar = self.dcx.span_err(span, msg);
318 self.result = Some((span, guar));
319 return;
320 }
321
322 let nts = bb_locs
323 .into_iter()
324 .map(|m| {
325 let loc = &matcher[m.loc_index as usize];
326 let MatcherLoc::MetaVarDecl { bind, kind, .. } = loc else { ::core::panicking::panic("internal error: entered unreachable code")unreachable!() };
327 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{0} (\'{1}\')", kind, bind))
})format!("{kind} ('{bind}')")
328 })
329 .collect::<Vec<String>>()
330 .join(" or ");
331
332 let msg = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("local ambiguity when calling macro `{0}`: multiple parsing options: {1}",
self.macro_name,
match next_locs.len() {
0 =>
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("built-in NTs {0}.", nts))
}),
n =>
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("built-in NTs {1} or {2} other option{0}.",
if n == 1 { "" } else { "s" }, nts, n))
}),
}))
})format!(
333 "local ambiguity when calling macro `{}`: multiple parsing options: {}",
334 self.macro_name,
335 match next_locs.len() {
336 0 => format!("built-in NTs {nts}."),
337 n => format!("built-in NTs {nts} or {n} other option{s}.", s = pluralize!(n)),
338 }
339 );
340
341 let guar = self.dcx.span_err(span, msg);
342 self.result = Some((span, guar));
343 }
344
345 fn description() -> &'static str {
346 "detailed"
347 }
348
349 fn recovery() -> Recovery {
350 Recovery::Allowed
351 }
352}
353
354impl<'dcx> CollectTrackerAndEmitter<'dcx, '_> {
355 fn new(macro_name: Ident, dcx: DiagCtxtHandle<'dcx>, root_span: Span) -> Self {
356 Self {
357 macro_name,
358 dcx,
359 current: None,
360 matches: FxHashSet::default(),
361 remaining_matcher: None,
362 best_failure: None,
363 root_span,
364 result: None,
365 }
366 }
367}
368
369pub(super) fn emit_frag_parse_err(
370 mut e: Diag<'_>,
371 parser: &mut Parser<'_>,
372 orig_parser: &mut Parser<'_>,
373 site_span: Span,
374 arm_span: Span,
375 kind: AstFragmentKind,
376 bindings: &[MacroRule],
377 matched_rule_bindings: &[MatcherLoc],
378) -> ErrorGuaranteed {
379 if parser.token == token::Eof
381 && let DiagMessage::Str(message) = &e.messages[0].0
382 && message.ends_with(", found `<eof>`")
383 {
384 let msg = &e.messages[0];
385 e.messages[0] = (
386 DiagMessage::from(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("macro expansion ends with an incomplete expression: {0}",
message.replace(", found `<eof>`", "")))
})format!(
387 "macro expansion ends with an incomplete expression: {}",
388 message.replace(", found `<eof>`", ""),
389 )),
390 msg.1,
391 );
392 if !e.span.is_dummy() {
393 e.replace_span_with(parser.token.span.shrink_to_hi(), true);
395 }
396 }
397 if e.span.is_dummy() {
398 e.replace_span_with(site_span, true);
400 if !parser.psess.source_map().is_imported(arm_span) {
401 e.span_label(arm_span, "in this macro arm");
402 }
403 } else if parser.psess.source_map().is_imported(parser.token.span) {
404 e.span_label(site_span, "in this macro invocation");
405 }
406 match kind {
407 AstFragmentKind::Expr => match parse_ast_fragment(orig_parser, AstFragmentKind::Stmts) {
409 Err(err) => err.cancel(),
410 Ok(_) => {
411 e.note(
412 "the macro call doesn't expand to an expression, but it can expand to a statement",
413 );
414
415 if parser.token == token::Semi {
416 if let Ok(snippet) = parser.psess.source_map().span_to_snippet(site_span) {
417 e.span_suggestion_verbose(
418 site_span,
419 "surround the macro invocation with `{}` to interpret the expansion as a statement",
420 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{{ {0}; }}", snippet))
})format!("{{ {snippet}; }}"),
421 Applicability::MaybeIncorrect,
422 );
423 }
424 } else {
425 e.span_suggestion_verbose(
426 site_span.shrink_to_hi(),
427 "add `;` to interpret the expansion as a statement",
428 ";",
429 Applicability::MaybeIncorrect,
430 );
431 }
432 }
433 },
434 _ => annotate_err_with_kind(&mut e, kind, site_span),
435 };
436
437 if parser.token.kind == token::Dollar {
438 let dollar_span = parser.token.span;
439 parser.bump();
440 if let token::Ident(name, _) = parser.token.kind {
441 let metavar_span = dollar_span.to(parser.token.span);
442 let mut bindings_names = ::alloc::vec::Vec::new()vec![];
443 for rule in bindings {
444 let MacroRule::Func { lhs, .. } = rule else { continue };
445 for param in lhs {
446 let MatcherLoc::MetaVarDecl { bind, .. } = param else { continue };
447 bindings_names.push(bind.name);
448 }
449 }
450
451 let mut matched_rule_bindings_names = ::alloc::vec::Vec::new()vec![];
452 for param in matched_rule_bindings {
453 let MatcherLoc::MetaVarDecl { bind, .. } = param else { continue };
454 matched_rule_bindings_names.push(bind.name);
455 }
456
457 e.primary_message(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("cannot find macro parameter `${0}` in this scope",
name))
})format!("cannot find macro parameter `${name}` in this scope"));
460 e.span(metavar_span);
461 e.span_label(metavar_span, "not found in this scope");
462 if parser.psess.source_map().is_imported(metavar_span) {
463 e.span_label(site_span, "in this macro invocation");
464 }
465
466 if let Some(matched_name) = rustc_span::edit_distance::find_best_match_for_name(
467 &matched_rule_bindings_names[..],
468 name,
469 None,
470 ) {
471 e.span_suggestion_verbose(
472 parser.token.span,
473 "there is a macro metavariable with a similar name",
474 matched_name,
475 Applicability::MaybeIncorrect,
476 );
477 } else if bindings_names.contains(&name) {
478 e.span_label(
479 parser.token.span,
480 "there is an macro metavariable with this name in another macro matcher",
481 );
482 } else if let Some(matched_name) =
483 rustc_span::edit_distance::find_best_match_for_name(&bindings_names[..], name, None)
484 {
485 e.span_suggestion_verbose(
486 parser.token.span,
487 "there is a macro metavariable with a similar name in another macro matcher",
488 matched_name,
489 Applicability::MaybeIncorrect,
490 );
491 } else if !matched_rule_bindings_names.is_empty() {
492 let msg = matched_rule_bindings_names
493 .iter()
494 .map(|sym| ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("${0}", sym))
})format!("${}", sym))
495 .collect::<Vec<_>>()
496 .join(", ");
497 e.note(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("available metavariable names are: {0}",
msg))
})format!("available metavariable names are: {msg}"));
498 }
499 }
500 }
501 e.emit()
502}
503
504pub(crate) fn annotate_err_with_kind(err: &mut Diag<'_>, kind: AstFragmentKind, span: Span) {
505 match kind {
506 AstFragmentKind::Ty => {
507 err.span_label(span, "this macro call doesn't expand to a type");
508 }
509 AstFragmentKind::Pat => {
510 err.span_label(span, "this macro call doesn't expand to a pattern");
511 }
512 _ => {}
513 };
514}
515
516#[derive(const _: () =
{
impl rustc_errors::Subdiagnostic for ExplainDocComment {
fn add_to_diag<__G>(self, diag: &mut rustc_errors::Diag<'_, __G>)
where __G: rustc_errors::EmissionGuarantee {
match self {
ExplainDocComment::Inner { span: __binding_0 } => {
let mut sub_args = rustc_errors::DiagArgMap::default();
let __message =
rustc_errors::format_diag_message(&rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("inner doc comments expand to `#![doc = \"...\"]`, which is what this macro attempted to match")),
&sub_args);
diag.span_label(__binding_0, __message);
}
ExplainDocComment::Outer { span: __binding_0 } => {
let mut sub_args = rustc_errors::DiagArgMap::default();
let __message =
rustc_errors::format_diag_message(&rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("outer doc comments expand to `#[doc = \"...\"]`, which is what this macro attempted to match")),
&sub_args);
diag.span_label(__binding_0, __message);
}
}
}
}
};Subdiagnostic)]
517enum ExplainDocComment {
518 #[label(
519 "inner doc comments expand to `#![doc = \"...\"]`, which is what this macro attempted to match"
520 )]
521 Inner {
522 #[primary_span]
523 span: Span,
524 },
525 #[label(
526 "outer doc comments expand to `#[doc = \"...\"]`, which is what this macro attempted to match"
527 )]
528 Outer {
529 #[primary_span]
530 span: Span,
531 },
532}
533
534fn annotate_doc_comment(err: &mut Diag<'_>, sm: &SourceMap, span: Span) {
535 if let Ok(src) = sm.span_to_snippet(span) {
536 if src.starts_with("///") || src.starts_with("/**") {
537 err.subdiagnostic(ExplainDocComment::Outer { span });
538 } else if src.starts_with("//!") || src.starts_with("/*!") {
539 err.subdiagnostic(ExplainDocComment::Inner { span });
540 }
541 }
542}
543
544fn parse_failure_msg(tok: &Token, expected_token: Option<&Token>) -> Cow<'static, str> {
547 if let Some(expected_token) = expected_token {
548 Cow::from(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("expected {0}, found {1}",
token_descr(expected_token), token_descr(tok)))
})format!("expected {}, found {}", token_descr(expected_token), token_descr(tok)))
549 } else {
550 match tok.kind {
551 token::Eof => Cow::from("unexpected end of macro invocation"),
552 _ => Cow::from(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("no rules expected {0}",
token_descr(tok)))
})format!("no rules expected {}", token_descr(tok))),
553 }
554 }
555}