Skip to main content

rustc_expand/mbe/
diagnostics.rs

1use std::borrow::Cow;
2
3use rustc_ast::token::{self, Token};
4use rustc_ast::tokenstream::TokenStream;
5use rustc_errors::{Applicability, Diag, DiagCtxtHandle, DiagMessage, pluralize};
6use rustc_hir::attrs::diagnostic::{CustomDiagnostic, Directive, FormatArgs};
7use rustc_macros::Subdiagnostic;
8use rustc_middle::bug;
9use rustc_parse::parser::{Parser, Recovery, token_descr};
10use rustc_session::parse::ParseSess;
11use rustc_span::source_map::SourceMap;
12use rustc_span::{DUMMY_SP, ErrorGuaranteed, Ident, Span};
13use tracing::debug;
14
15use super::macro_rules::{MacroRule, NoopTracker, parser_from_cx};
16use crate::expand::{AstFragmentKind, parse_ast_fragment};
17use crate::mbe::macro_parser::ParseResult::*;
18use crate::mbe::macro_parser::{MatcherLoc, NamedParseResult, TtParser};
19use crate::mbe::macro_rules::{
20    Tracker, WhichMatcher, try_match_macro, try_match_macro_attr, try_match_macro_derive,
21};
22
23pub(super) enum FailedMacro<'a> {
24    Func,
25    Attr(&'a TokenStream),
26    Derive,
27}
28
29pub(super) fn failed_to_match_macro(
30    psess: &ParseSess,
31    sp: Span,
32    def_span: Span,
33    name: Ident,
34    args: FailedMacro<'_>,
35    body: &TokenStream,
36    rules: &[MacroRule],
37    on_unmatched_args: Option<&Directive>,
38) -> (Span, ErrorGuaranteed) {
39    {
    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:39",
                        "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(39u32),
                        ::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};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("failed to match macro")
                                            as &dyn Value))])
            });
    } else { ; }
};debug!("failed to match macro");
40    let def_head_span = if !def_span.is_dummy() && !psess.source_map().is_imported(def_span) {
41        psess.source_map().guess_head_span(def_span)
42    } else {
43        DUMMY_SP
44    };
45
46    // An error occurred, try the expansion again, tracking the expansion closely for better
47    // diagnostics.
48    let mut tracker = CollectTrackerAndEmitter::new(name, psess.dcx(), sp);
49
50    let try_success_result = match args {
51        FailedMacro::Func => try_match_macro(psess, name, body, rules, &mut tracker),
52        FailedMacro::Attr(attr_args) => {
53            try_match_macro_attr(psess, name, attr_args, body, rules, &mut tracker)
54        }
55        FailedMacro::Derive => try_match_macro_derive(psess, name, body, rules, &mut tracker),
56    };
57
58    if try_success_result.is_ok() {
59        // Nonterminal parser recovery might turn failed matches into successful ones,
60        // but for that it must have emitted an error already
61        if !tracker.dcx.has_errors().is_some() {
    {
        ::core::panicking::panic_fmt(format_args!("Macro matching returned a success on the second try"));
    }
};assert!(
62            tracker.dcx.has_errors().is_some(),
63            "Macro matching returned a success on the second try"
64        );
65    }
66
67    if let Some(result) = tracker.result {
68        // An irrecoverable error occurred and has been emitted.
69        return result;
70    }
71
72    let Some(BestFailure { token, msg: label, remaining_matcher, .. }) = tracker.best_failure
73    else {
74        return (sp, psess.dcx().span_delayed_bug(sp, "failed to match a macro"));
75    };
76
77    let span = token.span.substitute_dummy(sp);
78    let CustomDiagnostic {
79        message: custom_message, label: custom_label, notes: custom_notes, ..
80    } = {
81        on_unmatched_args
82            .map(|directive| directive.eval(None, &FormatArgs { this: name.to_string(), .. }))
83            .unwrap_or_default()
84    };
85
86    let mut err = match custom_message {
87        Some(message) => psess.dcx().struct_span_err(span, message),
88        None => psess.dcx().struct_span_err(span, parse_failure_msg(&token, None)),
89    };
90    err.span_label(span, custom_label.unwrap_or_else(|| label.to_string()));
91    if !def_head_span.is_dummy() {
92        err.span_label(def_head_span, "when calling this macro");
93    }
94
95    annotate_doc_comment(&mut err, psess.source_map(), span);
96
97    if let Some(span) = remaining_matcher.span() {
98        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}"));
99    } else {
100        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}"));
101    }
102    for note in custom_notes {
103        err.note(note);
104    }
105
106    if let MatcherLoc::Token { token: expected_token } = &remaining_matcher
107        && (#[allow(non_exhaustive_omitted_patterns)] match expected_token.kind {
    token::OpenInvisible(_) => true,
    _ => false,
}matches!(expected_token.kind, token::OpenInvisible(_))
108            || #[allow(non_exhaustive_omitted_patterns)] match token.kind {
    token::OpenInvisible(_) => true,
    _ => false,
}matches!(token.kind, token::OpenInvisible(_)))
109    {
110        err.note("captured metavariables except for `:tt`, `:ident` and `:lifetime` cannot be compared to other tokens");
111        err.note("see <https://doc.rust-lang.org/nightly/reference/macros-by-example.html#forwarding-a-matched-fragment> for more information");
112
113        if !def_span.is_dummy() && !psess.source_map().is_imported(def_span) {
114            err.help("try using `:tt` instead in the macro definition");
115        }
116    }
117
118    // Check whether there's a missing comma in this macro call, like `println!("{}" a);`
119    if let FailedMacro::Func = args
120        && let Some((body, comma_span)) = body.add_comma()
121    {
122        for rule in rules {
123            let MacroRule::Func { lhs, .. } = rule else { continue };
124            let parser = parser_from_cx(psess, body.clone(), Recovery::Allowed);
125            let mut tt_parser = TtParser::new();
126
127            if let Success(_) =
128                tt_parser.parse_tt(&mut Cow::Borrowed(&parser), lhs, &mut NoopTracker)
129            {
130                if comma_span.is_dummy() {
131                    err.note("you might be missing a comma");
132                } else {
133                    err.span_suggestion_short(
134                        comma_span,
135                        "missing comma here",
136                        ", ",
137                        Applicability::MachineApplicable,
138                    );
139                }
140            }
141        }
142    }
143    let guar = err.emit();
144    (sp, guar)
145}
146
147/// The tracker used for the slow error path that collects useful info for diagnostics.
148struct CollectTrackerAndEmitter<'dcx, 'matcher> {
149    macro_name: Ident,
150    dcx: DiagCtxtHandle<'dcx>,
151
152    /// The matcher currently being parsed.
153    //
154    // FIXME: Factor out a per-arm `Tracker` so that the `Option` is unnecessary.
155    current: Option<WhichMatcher>,
156
157    remaining_matcher: Option<&'matcher MatcherLoc>,
158    /// Which arm's failure should we report? (the one furthest along)
159    best_failure: Option<BestFailure>,
160    root_span: Span,
161    result: Option<(Span, ErrorGuaranteed)>,
162}
163
164struct BestFailure {
165    token: Token,
166
167    /// The matcher in which the failure occurred.
168    matcher: WhichMatcher,
169
170    /// The approximate (parser) position of the failure.
171    ///
172    /// This is relative to [`Self::matcher`].
173    position: u32,
174
175    msg: &'static str,
176    remaining_matcher: MatcherLoc,
177}
178
179impl BestFailure {
180    fn is_better_position(&self, matcher: WhichMatcher, position: u32) -> bool {
181        (matcher, position) > (self.matcher, self.position)
182    }
183}
184
185impl<'dcx, 'matcher> Tracker<'matcher> for CollectTrackerAndEmitter<'dcx, 'matcher> {
186    fn prepare(&mut self, which_matcher: WhichMatcher) {
187        if self.current.is_some() {
188            ::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");
189        }
190
191        self.current = Some(which_matcher);
192    }
193
194    fn before_match_loc(&mut self, parser: &TtParser, matcher: &'matcher MatcherLoc) {
195        if self.remaining_matcher.is_none()
196            || (parser.has_no_remaining_items_for_step() && *matcher != MatcherLoc::Eof)
197        {
198            self.remaining_matcher = Some(matcher);
199        }
200    }
201
202    fn after_arm(&mut self, result: &NamedParseResult) {
203        match *result {
204            Success(_) => {
205                // Nonterminal parser recovery might turn failed matches into successful ones,
206                // but for that it must have emitted an error already
207                self.dcx.span_delayed_bug(
208                    self.root_span,
209                    "should not collect detailed info for successful macro match",
210                );
211            }
212            Failure => {
213                if self.best_failure.is_none() {
214                    ::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");
215                }
216            }
217            Ambiguity => {
218                if self.result.is_none() {
219                    ::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");
220                }
221            }
222            ErrorReported(guar) => self.result = Some((self.root_span, guar)),
223        }
224
225        self.current = None;
226    }
227
228    fn failure(&mut self, parser: &Parser<'_>) {
229        let Some(which_matcher) = self.current else {
230            ::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");
231        };
232
233        let mut token = parser.token;
234        let approx_position = parser.approx_token_stream_pos();
235        let msg = if token.kind == token::Eof {
236            // FIXME: Can this be factored out of the EOF case?
237            if !token.span.is_dummy() {
238                token.span = token.span.shrink_to_hi();
239            }
240            "missing tokens in macro arguments"
241        } else {
242            "no rules expected this token in macro call"
243        };
244
245        {
    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:245",
                        "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(245u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_expand::mbe::diagnostics"),
                        ::tracing_core::field::FieldSet::new(&["message", "token",
                                        "msg"], ::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};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("a new failure of an arm")
                                            as &dyn Value)),
                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&debug(&token) as
                                            &dyn Value)),
                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&debug(&msg) as
                                            &dyn Value))])
            });
    } else { ; }
};debug!(?token, ?msg, "a new failure of an arm");
246
247        if self
248            .best_failure
249            .as_ref()
250            .is_none_or(|failure| failure.is_better_position(which_matcher, approx_position))
251        {
252            self.best_failure = Some(BestFailure {
253                token,
254                matcher: which_matcher,
255                position: approx_position,
256                msg,
257                remaining_matcher: self
258                    .remaining_matcher
259                    .expect("must have collected matcher already")
260                    .clone(),
261            })
262        }
263    }
264
265    fn ambiguity(
266        &mut self,
267        parser: &Parser<'_>,
268        bb_locs: impl IntoIterator<Item = &'matcher MatcherLoc>,
269        next_locs: impl IntoIterator<Item = &'matcher MatcherLoc>,
270    ) {
271        let span = parser.token.span.substitute_dummy(self.root_span);
272
273        if parser.token == token::Eof {
274            let msg = "ambiguity: multiple successful parses".to_string();
275            let guar = self.dcx.span_err(span, msg);
276            self.result = Some((span, guar));
277            return;
278        }
279
280        let nts = bb_locs
281            .into_iter()
282            .map(|loc| match loc {
283                MatcherLoc::MetaVarDecl { bind, kind, .. } => {
284                    ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{0} (\'{1}\')", kind, bind))
    })format!("{kind} ('{bind}')")
285                }
286                _ => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
287            })
288            .collect::<Vec<String>>()
289            .join(" or ");
290
291        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.into_iter().count() {
                    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!(
292            "local ambiguity when calling macro `{}`: multiple parsing options: {}",
293            self.macro_name,
294            match next_locs.into_iter().count() {
295                0 => format!("built-in NTs {nts}."),
296                n => format!("built-in NTs {nts} or {n} other option{s}.", s = pluralize!(n)),
297            }
298        );
299
300        let guar = self.dcx.span_err(span, msg);
301        self.result = Some((span, guar));
302    }
303
304    fn description() -> &'static str {
305        "detailed"
306    }
307
308    fn recovery() -> Recovery {
309        Recovery::Allowed
310    }
311}
312
313impl<'dcx> CollectTrackerAndEmitter<'dcx, '_> {
314    fn new(macro_name: Ident, dcx: DiagCtxtHandle<'dcx>, root_span: Span) -> Self {
315        Self {
316            macro_name,
317            dcx,
318            current: None,
319            remaining_matcher: None,
320            best_failure: None,
321            root_span,
322            result: None,
323        }
324    }
325}
326
327pub(super) fn emit_frag_parse_err(
328    mut e: Diag<'_>,
329    parser: &mut Parser<'_>,
330    orig_parser: &mut Parser<'_>,
331    site_span: Span,
332    arm_span: Span,
333    kind: AstFragmentKind,
334    bindings: &[MacroRule],
335    matched_rule_bindings: &[MatcherLoc],
336) -> ErrorGuaranteed {
337    // FIXME(davidtwco): avoid depending on the error message text
338    if parser.token == token::Eof
339        && let DiagMessage::Str(message) = &e.messages[0].0
340        && message.ends_with(", found `<eof>`")
341    {
342        let msg = &e.messages[0];
343        e.messages[0] = (
344            DiagMessage::from(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("macro expansion ends with an incomplete expression: {0}",
                message.replace(", found `<eof>`", "")))
    })format!(
345                "macro expansion ends with an incomplete expression: {}",
346                message.replace(", found `<eof>`", ""),
347            )),
348            msg.1,
349        );
350        if !e.span.is_dummy() {
351            // early end of macro arm (#52866)
352            e.replace_span_with(parser.token.span.shrink_to_hi(), true);
353        }
354    }
355    if e.span.is_dummy() {
356        // Get around lack of span in error (#30128)
357        e.replace_span_with(site_span, true);
358        if !parser.psess.source_map().is_imported(arm_span) {
359            e.span_label(arm_span, "in this macro arm");
360        }
361    } else if parser.psess.source_map().is_imported(parser.token.span) {
362        e.span_label(site_span, "in this macro invocation");
363    }
364    match kind {
365        // Try a statement if an expression is wanted but failed and suggest adding `;` to call.
366        AstFragmentKind::Expr => match parse_ast_fragment(orig_parser, AstFragmentKind::Stmts) {
367            Err(err) => err.cancel(),
368            Ok(_) => {
369                e.note(
370                    "the macro call doesn't expand to an expression, but it can expand to a statement",
371                );
372
373                if parser.token == token::Semi {
374                    if let Ok(snippet) = parser.psess.source_map().span_to_snippet(site_span) {
375                        e.span_suggestion_verbose(
376                            site_span,
377                            "surround the macro invocation with `{}` to interpret the expansion as a statement",
378                            ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{{ {0}; }}", snippet))
    })format!("{{ {snippet}; }}"),
379                            Applicability::MaybeIncorrect,
380                        );
381                    }
382                } else {
383                    e.span_suggestion_verbose(
384                        site_span.shrink_to_hi(),
385                        "add `;` to interpret the expansion as a statement",
386                        ";",
387                        Applicability::MaybeIncorrect,
388                    );
389                }
390            }
391        },
392        _ => annotate_err_with_kind(&mut e, kind, site_span),
393    };
394
395    if parser.token.kind == token::Dollar {
396        let dollar_span = parser.token.span;
397        parser.bump();
398        if let token::Ident(name, _) = parser.token.kind {
399            let metavar_span = dollar_span.to(parser.token.span);
400            let mut bindings_names = ::alloc::vec::Vec::new()vec![];
401            for rule in bindings {
402                let MacroRule::Func { lhs, .. } = rule else { continue };
403                for param in lhs {
404                    let MatcherLoc::MetaVarDecl { bind, .. } = param else { continue };
405                    bindings_names.push(bind.name);
406                }
407            }
408
409            let mut matched_rule_bindings_names = ::alloc::vec::Vec::new()vec![];
410            for param in matched_rule_bindings {
411                let MatcherLoc::MetaVarDecl { bind, .. } = param else { continue };
412                matched_rule_bindings_names.push(bind.name);
413            }
414
415            // Report the unbound metavariable as the primary error up front, so every
416            // case is consistent regardless of which suggestion (if any) we attach below.
417            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"));
418            e.span(metavar_span);
419            e.span_label(metavar_span, "not found in this scope");
420            if parser.psess.source_map().is_imported(metavar_span) {
421                e.span_label(site_span, "in this macro invocation");
422            }
423
424            if let Some(matched_name) = rustc_span::edit_distance::find_best_match_for_name(
425                &matched_rule_bindings_names[..],
426                name,
427                None,
428            ) {
429                e.span_suggestion_verbose(
430                    parser.token.span,
431                    "there is a macro metavariable with a similar name",
432                    matched_name,
433                    Applicability::MaybeIncorrect,
434                );
435            } else if bindings_names.contains(&name) {
436                e.span_label(
437                    parser.token.span,
438                    "there is an macro metavariable with this name in another macro matcher",
439                );
440            } else if let Some(matched_name) =
441                rustc_span::edit_distance::find_best_match_for_name(&bindings_names[..], name, None)
442            {
443                e.span_suggestion_verbose(
444                    parser.token.span,
445                    "there is a macro metavariable with a similar name in another macro matcher",
446                    matched_name,
447                    Applicability::MaybeIncorrect,
448                );
449            } else if !matched_rule_bindings_names.is_empty() {
450                let msg = matched_rule_bindings_names
451                    .iter()
452                    .map(|sym| ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("${0}", sym))
    })format!("${}", sym))
453                    .collect::<Vec<_>>()
454                    .join(", ");
455                e.note(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("available metavariable names are: {0}",
                msg))
    })format!("available metavariable names are: {msg}"));
456            }
457        }
458    }
459    e.emit()
460}
461
462pub(crate) fn annotate_err_with_kind(err: &mut Diag<'_>, kind: AstFragmentKind, span: Span) {
463    match kind {
464        AstFragmentKind::Ty => {
465            err.span_label(span, "this macro call doesn't expand to a type");
466        }
467        AstFragmentKind::Pat => {
468            err.span_label(span, "this macro call doesn't expand to a pattern");
469        }
470        _ => {}
471    };
472}
473
474#[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)]
475enum ExplainDocComment {
476    #[label(
477        "inner doc comments expand to `#![doc = \"...\"]`, which is what this macro attempted to match"
478    )]
479    Inner {
480        #[primary_span]
481        span: Span,
482    },
483    #[label(
484        "outer doc comments expand to `#[doc = \"...\"]`, which is what this macro attempted to match"
485    )]
486    Outer {
487        #[primary_span]
488        span: Span,
489    },
490}
491
492fn annotate_doc_comment(err: &mut Diag<'_>, sm: &SourceMap, span: Span) {
493    if let Ok(src) = sm.span_to_snippet(span) {
494        if src.starts_with("///") || src.starts_with("/**") {
495            err.subdiagnostic(ExplainDocComment::Outer { span });
496        } else if src.starts_with("//!") || src.starts_with("/*!") {
497            err.subdiagnostic(ExplainDocComment::Inner { span });
498        }
499    }
500}
501
502/// Generates an appropriate parsing failure message. For EOF, this is "unexpected end...". For
503/// other tokens, this is "unexpected token...".
504fn parse_failure_msg(tok: &Token, expected_token: Option<&Token>) -> Cow<'static, str> {
505    if let Some(expected_token) = expected_token {
506        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)))
507    } else {
508        match tok.kind {
509            token::Eof => Cow::from("unexpected end of macro invocation"),
510            _ => 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))),
511        }
512    }
513}