Skip to main content

rustc_attr_parsing/attributes/diagnostic/
mod.rs

1use std::ops::Range;
2
3use rustc_errors::E0232;
4use rustc_hir::AttrPath;
5use rustc_hir::attrs::diagnostic::{
6    Directive, FilterFormatString, Flag, FormatArg, FormatString, LitOrArg, Name, NameValue,
7    OnUnimplementedCondition, Piece, Predicate,
8};
9use rustc_hir::lints::{AttributeLintKind, FormatWarning};
10use rustc_macros::Diagnostic;
11use rustc_parse_format::{
12    Argument, FormatSpec, ParseError, ParseMode, Parser, Piece as RpfPiece, Position,
13};
14use rustc_session::lint::builtin::{
15    MALFORMED_DIAGNOSTIC_ATTRIBUTES, MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
16};
17use rustc_span::{Ident, InnerSpan, Span, Symbol, kw, sym};
18use thin_vec::{ThinVec, thin_vec};
19
20use crate::context::{AcceptContext, Stage};
21use crate::parser::{ArgParser, MetaItemListParser, MetaItemOrLitParser, MetaItemParser};
22
23pub(crate) mod do_not_recommend;
24pub(crate) mod on_const;
25pub(crate) mod on_move;
26pub(crate) mod on_unimplemented;
27pub(crate) mod on_unknown;
28
29#[derive(#[automatically_derived]
impl ::core::marker::Copy for Mode { }Copy, #[automatically_derived]
impl ::core::clone::Clone for Mode {
    #[inline]
    fn clone(&self) -> Mode { *self }
}Clone)]
30pub(crate) enum Mode {
31    /// `#[rustc_on_unimplemented]`
32    RustcOnUnimplemented,
33    /// `#[diagnostic::on_unimplemented]`
34    DiagnosticOnUnimplemented,
35    /// `#[diagnostic::on_const]`
36    DiagnosticOnConst,
37    /// `#[diagnostic::on_move]`
38    DiagnosticOnMove,
39    /// `#[diagnostic::on_unknown]`
40    DiagnosticOnUnknown,
41}
42
43impl Mode {
44    fn as_str(&self) -> &'static str {
45        match self {
46            Self::RustcOnUnimplemented => "rustc_on_unimplemented",
47            Self::DiagnosticOnUnimplemented => "diagnostic::on_unimplemented",
48            Self::DiagnosticOnConst => "diagnostic::on_const",
49            Self::DiagnosticOnMove => "diagnostic::on_move",
50            Self::DiagnosticOnUnknown => "diagnostic::on_unknown",
51        }
52    }
53
54    fn expected_options(&self) -> &'static str {
55        const DEFAULT: &str =
56            "at least one of the `message`, `note` and `label` options are expected";
57        match self {
58            Self::RustcOnUnimplemented => {
59                "see <https://rustc-dev-guide.rust-lang.org/diagnostics.html#rustc_on_unimplemented>"
60            }
61            Self::DiagnosticOnUnimplemented => DEFAULT,
62            Self::DiagnosticOnConst => DEFAULT,
63            Self::DiagnosticOnMove => DEFAULT,
64            Self::DiagnosticOnUnknown => DEFAULT,
65        }
66    }
67
68    fn allowed_options(&self) -> &'static str {
69        const DEFAULT: &str = "only `message`, `note` and `label` are allowed as options";
70        match self {
71            Self::RustcOnUnimplemented => {
72                "see <https://rustc-dev-guide.rust-lang.org/diagnostics.html#rustc_on_unimplemented>"
73            }
74            Self::DiagnosticOnUnimplemented => DEFAULT,
75            Self::DiagnosticOnConst => DEFAULT,
76            Self::DiagnosticOnMove => DEFAULT,
77            Self::DiagnosticOnUnknown => DEFAULT,
78        }
79    }
80}
81
82fn merge_directives<S: Stage>(
83    cx: &mut AcceptContext<'_, '_, S>,
84    first: &mut Option<(Span, Directive)>,
85    later: (Span, Directive),
86) {
87    if let Some((_, first)) = first {
88        if first.is_rustc_attr || later.1.is_rustc_attr {
89            cx.emit_err(DupesNotAllowed);
90        }
91
92        merge(cx, &mut first.message, later.1.message, sym::message);
93        merge(cx, &mut first.label, later.1.label, sym::label);
94        first.notes.extend(later.1.notes);
95    } else {
96        *first = Some(later);
97    }
98}
99
100fn merge<T, S: Stage>(
101    cx: &mut AcceptContext<'_, '_, S>,
102    first: &mut Option<(Span, T)>,
103    later: Option<(Span, T)>,
104    option_name: Symbol,
105) {
106    match (first, later) {
107        (Some(_) | None, None) => {}
108        (Some((first_span, _)), Some((later_span, _))) => {
109            cx.emit_lint(
110                MALFORMED_DIAGNOSTIC_ATTRIBUTES,
111                AttributeLintKind::IgnoredDiagnosticOption {
112                    first_span: *first_span,
113                    later_span,
114                    option_name,
115                },
116                later_span,
117            );
118        }
119        (first @ None, Some(later)) => {
120            first.get_or_insert(later);
121        }
122    }
123}
124
125fn parse_list<'p, S: Stage>(
126    cx: &mut AcceptContext<'_, '_, S>,
127    args: &'p ArgParser,
128    mode: Mode,
129) -> Option<&'p MetaItemListParser> {
130    let span = cx.attr_span;
131    match args {
132        ArgParser::List(items) if items.len() != 0 => return Some(items),
133        ArgParser::List(list) => {
134            // We're dealing with `#[diagnostic::attr()]`.
135            // This can be because that is what the user typed, but that's also what we'd see
136            // if the user used non-metaitem syntax. See `ArgParser::from_attr_args`.
137            cx.emit_lint(
138                MALFORMED_DIAGNOSTIC_ATTRIBUTES,
139                AttributeLintKind::NonMetaItemDiagnosticAttribute,
140                list.span,
141            );
142        }
143        ArgParser::NoArgs => {
144            cx.emit_lint(
145                MALFORMED_DIAGNOSTIC_ATTRIBUTES,
146                AttributeLintKind::MissingOptionsForDiagnosticAttribute {
147                    attribute: mode.as_str(),
148                    options: mode.expected_options(),
149                },
150                span,
151            );
152        }
153        ArgParser::NameValue(_) => {
154            cx.emit_lint(
155                MALFORMED_DIAGNOSTIC_ATTRIBUTES,
156                AttributeLintKind::MalFormedDiagnosticAttribute {
157                    attribute: mode.as_str(),
158                    options: mode.allowed_options(),
159                    span,
160                },
161                span,
162            );
163        }
164    }
165    None
166}
167
168fn parse_directive_items<'p, S: Stage>(
169    cx: &mut AcceptContext<'_, '_, S>,
170    mode: Mode,
171    items: impl Iterator<Item = &'p MetaItemOrLitParser>,
172    is_root: bool,
173) -> Option<Directive> {
174    let condition = None;
175    let mut message: Option<(Span, _)> = None;
176    let mut label: Option<(Span, _)> = None;
177    let mut notes = ThinVec::new();
178    let mut parent_label = None;
179    let mut subcommands = ThinVec::new();
180
181    for item in items {
182        let span = item.span();
183
184        macro malformed() {{
185            cx.emit_lint(
186                MALFORMED_DIAGNOSTIC_ATTRIBUTES,
187                AttributeLintKind::MalFormedDiagnosticAttribute {
188                    attribute: mode.as_str(),
189                    options: mode.allowed_options(),
190                    span,
191                },
192                span,
193            );
194            continue;
195        }}
196
197        macro or_malformed($($code:tt)*) {{
198            let Some(ret) = (||{
199                Some($($code)*)
200            })() else {
201                malformed!()
202            };
203            ret
204        }}
205
206        macro duplicate($name: ident, $($first_span:tt)*) {{
207            cx.emit_lint(
208                MALFORMED_DIAGNOSTIC_ATTRIBUTES,
209                AttributeLintKind::IgnoredDiagnosticOption {
210                    first_span: $($first_span)*,
211                    later_span: span,
212                    option_name: $name,
213                },
214                span,
215            );
216        }}
217
218        let item: &MetaItemParser = {
    let Some(ret) =
        (||
                    {
                        Some(item.meta_item()?)
                    })() else {
            {
                cx.emit_lint(MALFORMED_DIAGNOSTIC_ATTRIBUTES,
                    AttributeLintKind::MalFormedDiagnosticAttribute {
                        attribute: mode.as_str(),
                        options: mode.allowed_options(),
                        span,
                    }, span);
                continue;
            }
        };
    ret
}or_malformed!(item.meta_item()?);
219        let name = {
    let Some(ret) =
        (||
                    {
                        Some(item.ident()?)
                    })() else {
            {
                cx.emit_lint(MALFORMED_DIAGNOSTIC_ATTRIBUTES,
                    AttributeLintKind::MalFormedDiagnosticAttribute {
                        attribute: mode.as_str(),
                        options: mode.allowed_options(),
                        span,
                    }, span);
                continue;
            }
        };
    ret
}or_malformed!(item.ident()?).name;
220
221        // Currently, as of April 2026, all arguments of all diagnostic attrs
222        // must have a value, like `message = "message"`. Thus in a well-formed
223        // diagnostic attribute this is never `None`.
224        //
225        // But we don't assert its presence yet because we don't want to mention it
226        // if someone does something like `#[diagnostic::on_unimplemented(doesnt_exist)]`.
227        // That happens in the big `match` below.
228        let value: Option<Ident> = match item.args().name_value() {
229            Some(nv) => Some({
    let Some(ret) =
        (||
                    {
                        Some(nv.value_as_ident()?)
                    })() else {
            {
                cx.emit_lint(MALFORMED_DIAGNOSTIC_ATTRIBUTES,
                    AttributeLintKind::MalFormedDiagnosticAttribute {
                        attribute: mode.as_str(),
                        options: mode.allowed_options(),
                        span,
                    }, span);
                continue;
            }
        };
    ret
}or_malformed!(nv.value_as_ident()?)),
230            None => None,
231        };
232
233        let mut parse_format = |input: Ident| {
234            let snippet = cx.sess.source_map().span_to_snippet(input.span).ok();
235            let is_snippet = snippet.is_some();
236            match parse_format_string(input.name, snippet, input.span, mode) {
237                Ok((f, warnings)) => {
238                    for warning in warnings {
239                        let (FormatWarning::InvalidSpecifier { span, .. }
240                        | FormatWarning::PositionalArgument { span, .. }
241                        | FormatWarning::DisallowedPlaceholder { span }) = warning;
242                        cx.emit_lint(
243                            MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
244                            AttributeLintKind::MalformedDiagnosticFormat { warning },
245                            span,
246                        );
247                    }
248
249                    f
250                }
251                Err(e) => {
252                    cx.emit_lint(
253                        MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
254                        AttributeLintKind::DiagnosticWrappedParserError {
255                            description: e.description,
256                            label: e.label,
257                            span: slice_span(input.span, e.span, is_snippet),
258                        },
259                        input.span,
260                    );
261                    // We could not parse the input, just use it as-is.
262                    FormatString {
263                        input: input.name,
264                        span: input.span,
265                        pieces: {
    let len = [()].len();
    let mut vec = ::thin_vec::ThinVec::with_capacity(len);
    vec.push(Piece::Lit(input.name));
    vec
}thin_vec![Piece::Lit(input.name)],
266                    }
267                }
268            }
269        };
270        match (mode, name) {
271            (_, sym::message) => {
272                let value = {
    let Some(ret) =
        (||
                    {
                        Some(value?)
                    })() else {
            {
                cx.emit_lint(MALFORMED_DIAGNOSTIC_ATTRIBUTES,
                    AttributeLintKind::MalFormedDiagnosticAttribute {
                        attribute: mode.as_str(),
                        options: mode.allowed_options(),
                        span,
                    }, span);
                continue;
            }
        };
    ret
}or_malformed!(value?);
273                if let Some(message) = &message {
274                    {
    cx.emit_lint(MALFORMED_DIAGNOSTIC_ATTRIBUTES,
        AttributeLintKind::IgnoredDiagnosticOption {
            first_span: message.0,
            later_span: span,
            option_name: name,
        }, span);
}duplicate!(name, message.0)
275                } else {
276                    message = Some((item.span(), parse_format(value)));
277                }
278            }
279            (_, sym::label) => {
280                let value = {
    let Some(ret) =
        (||
                    {
                        Some(value?)
                    })() else {
            {
                cx.emit_lint(MALFORMED_DIAGNOSTIC_ATTRIBUTES,
                    AttributeLintKind::MalFormedDiagnosticAttribute {
                        attribute: mode.as_str(),
                        options: mode.allowed_options(),
                        span,
                    }, span);
                continue;
            }
        };
    ret
}or_malformed!(value?);
281                if let Some(label) = &label {
282                    {
    cx.emit_lint(MALFORMED_DIAGNOSTIC_ATTRIBUTES,
        AttributeLintKind::IgnoredDiagnosticOption {
            first_span: label.0,
            later_span: span,
            option_name: name,
        }, span);
}duplicate!(name, label.0)
283                } else {
284                    label = Some((item.span(), parse_format(value)));
285                }
286            }
287            (_, sym::note) => {
288                let value = {
    let Some(ret) =
        (||
                    {
                        Some(value?)
                    })() else {
            {
                cx.emit_lint(MALFORMED_DIAGNOSTIC_ATTRIBUTES,
                    AttributeLintKind::MalFormedDiagnosticAttribute {
                        attribute: mode.as_str(),
                        options: mode.allowed_options(),
                        span,
                    }, span);
                continue;
            }
        };
    ret
}or_malformed!(value?);
289                notes.push(parse_format(value))
290            }
291            (Mode::RustcOnUnimplemented, sym::parent_label) => {
292                let value = {
    let Some(ret) =
        (||
                    {
                        Some(value?)
                    })() else {
            {
                cx.emit_lint(MALFORMED_DIAGNOSTIC_ATTRIBUTES,
                    AttributeLintKind::MalFormedDiagnosticAttribute {
                        attribute: mode.as_str(),
                        options: mode.allowed_options(),
                        span,
                    }, span);
                continue;
            }
        };
    ret
}or_malformed!(value?);
293                if parent_label.is_none() {
294                    parent_label = Some(parse_format(value));
295                } else {
296                    {
    cx.emit_lint(MALFORMED_DIAGNOSTIC_ATTRIBUTES,
        AttributeLintKind::IgnoredDiagnosticOption {
            first_span: span,
            later_span: span,
            option_name: name,
        }, span);
}duplicate!(name, span)
297                }
298            }
299            (Mode::RustcOnUnimplemented, sym::on) => {
300                if is_root {
301                    let items = {
    let Some(ret) =
        (||
                    {
                        Some(item.args().list()?)
                    })() else {
            {
                cx.emit_lint(MALFORMED_DIAGNOSTIC_ATTRIBUTES,
                    AttributeLintKind::MalFormedDiagnosticAttribute {
                        attribute: mode.as_str(),
                        options: mode.allowed_options(),
                        span,
                    }, span);
                continue;
            }
        };
    ret
}or_malformed!(item.args().list()?);
302                    let mut iter = items.mixed();
303                    let condition: &MetaItemOrLitParser = match iter.next() {
304                        Some(c) => c,
305                        None => {
306                            cx.emit_err(InvalidOnClause::Empty { span });
307                            continue;
308                        }
309                    };
310
311                    let condition = parse_condition(condition);
312
313                    if items.len() < 2 {
314                        // Something like `#[rustc_on_unimplemented(on(.., /* nothing */))]`
315                        // There's a condition but no directive behind it, this is a mistake.
316                        {
    cx.emit_lint(MALFORMED_DIAGNOSTIC_ATTRIBUTES,
        AttributeLintKind::MalFormedDiagnosticAttribute {
            attribute: mode.as_str(),
            options: mode.allowed_options(),
            span,
        }, span);
    continue;
};malformed!();
317                    }
318
319                    let mut directive =
320                        {
    let Some(ret) =
        (||
                    {
                        Some(parse_directive_items(cx, mode, iter, false)?)
                    })() else {
            {
                cx.emit_lint(MALFORMED_DIAGNOSTIC_ATTRIBUTES,
                    AttributeLintKind::MalFormedDiagnosticAttribute {
                        attribute: mode.as_str(),
                        options: mode.allowed_options(),
                        span,
                    }, span);
                continue;
            }
        };
    ret
}or_malformed!(parse_directive_items(cx, mode, iter, false)?);
321
322                    match condition {
323                        Ok(c) => {
324                            directive.condition = Some(c);
325                            subcommands.push(directive);
326                        }
327                        Err(e) => {
328                            cx.emit_err(e);
329                        }
330                    }
331                } else {
332                    {
    cx.emit_lint(MALFORMED_DIAGNOSTIC_ATTRIBUTES,
        AttributeLintKind::MalFormedDiagnosticAttribute {
            attribute: mode.as_str(),
            options: mode.allowed_options(),
            span,
        }, span);
    continue;
};malformed!();
333                }
334            }
335
336            _other => {
337                {
    cx.emit_lint(MALFORMED_DIAGNOSTIC_ATTRIBUTES,
        AttributeLintKind::MalFormedDiagnosticAttribute {
            attribute: mode.as_str(),
            options: mode.allowed_options(),
            span,
        }, span);
    continue;
};malformed!();
338            }
339        }
340    }
341
342    Some(Directive {
343        is_rustc_attr: #[allow(non_exhaustive_omitted_patterns)] match mode {
    Mode::RustcOnUnimplemented => true,
    _ => false,
}matches!(mode, Mode::RustcOnUnimplemented),
344        condition,
345        subcommands,
346        message,
347        label,
348        notes,
349        parent_label,
350    })
351}
352
353pub(crate) fn parse_format_string(
354    input: Symbol,
355    snippet: Option<String>,
356    span: Span,
357    mode: Mode,
358) -> Result<(FormatString, Vec<FormatWarning>), ParseError> {
359    let s = input.as_str();
360    let mut parser = Parser::new(s, None, snippet, false, ParseMode::Diagnostic);
361    let pieces: Vec<_> = parser.by_ref().collect();
362
363    if let Some(err) = parser.errors.into_iter().next() {
364        return Err(err);
365    }
366    let mut warnings = Vec::new();
367
368    let pieces = pieces
369        .into_iter()
370        .map(|piece| match piece {
371            RpfPiece::Lit(lit) => Piece::Lit(Symbol::intern(lit)),
372            RpfPiece::NextArgument(arg) => {
373                warn_on_format_spec(&arg.format, &mut warnings, span, parser.is_source_literal);
374                let arg = parse_arg(&arg, mode, &mut warnings, span, parser.is_source_literal);
375                Piece::Arg(arg)
376            }
377        })
378        .collect();
379
380    Ok((FormatString { input, pieces, span }, warnings))
381}
382
383fn parse_arg(
384    arg: &Argument<'_>,
385    mode: Mode,
386    warnings: &mut Vec<FormatWarning>,
387    input_span: Span,
388    is_source_literal: bool,
389) -> FormatArg {
390    let span = slice_span(input_span, arg.position_span.clone(), is_source_literal);
391    if #[allow(non_exhaustive_omitted_patterns)] match mode {
    Mode::DiagnosticOnUnknown => true,
    _ => false,
}matches!(mode, Mode::DiagnosticOnUnknown) {
392        warnings.push(FormatWarning::DisallowedPlaceholder { span });
393        return FormatArg::AsIs(sym::empty_braces);
394    }
395
396    match arg.position {
397        // Something like "hello {name}"
398        Position::ArgumentNamed(name) => match (mode, Symbol::intern(name)) {
399            // Only `#[rustc_on_unimplemented]` can use these
400            (Mode::RustcOnUnimplemented { .. }, sym::ItemContext) => FormatArg::ItemContext,
401            (Mode::RustcOnUnimplemented { .. }, sym::This) => FormatArg::This,
402            (Mode::RustcOnUnimplemented { .. }, sym::Trait) => FormatArg::Trait,
403            // Any attribute can use these
404            (_, kw::SelfUpper) => FormatArg::SelfUpper,
405            (_, generic_param) => FormatArg::GenericParam { generic_param, span },
406        },
407
408        // `{:1}` and `{}` are ignored
409        Position::ArgumentIs(idx) => {
410            warnings.push(FormatWarning::PositionalArgument {
411                span,
412                help: ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("use `{{{0}}}` to print a number in braces",
                idx))
    })format!("use `{{{idx}}}` to print a number in braces"),
413            });
414            FormatArg::AsIs(Symbol::intern(&::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{{{0}}}", idx))
    })format!("{{{idx}}}")))
415        }
416        Position::ArgumentImplicitlyIs(_) => {
417            warnings.push(FormatWarning::PositionalArgument {
418                span,
419                help: String::from("use `{{}}` to print empty braces"),
420            });
421            FormatArg::AsIs(sym::empty_braces)
422        }
423    }
424}
425
426/// `#[rustc_on_unimplemented]` and `#[diagnostic::...]` don't actually do anything
427/// with specifiers, so emit a warning if they are used.
428fn warn_on_format_spec(
429    spec: &FormatSpec<'_>,
430    warnings: &mut Vec<FormatWarning>,
431    input_span: Span,
432    is_source_literal: bool,
433) {
434    if spec.ty != "" {
435        let span = spec
436            .ty_span
437            .as_ref()
438            .map(|inner| slice_span(input_span, inner.clone(), is_source_literal))
439            .unwrap_or(input_span);
440        warnings.push(FormatWarning::InvalidSpecifier { span, name: spec.ty.into() })
441    }
442}
443
444fn slice_span(input: Span, Range { start, end }: Range<usize>, is_source_literal: bool) -> Span {
445    if is_source_literal { input.from_inner(InnerSpan { start, end }) } else { input }
446}
447
448pub(crate) fn parse_condition(
449    input: &MetaItemOrLitParser,
450) -> Result<OnUnimplementedCondition, InvalidOnClause> {
451    let span = input.span();
452    let pred = parse_predicate(input)?;
453    Ok(OnUnimplementedCondition { span, pred })
454}
455
456fn parse_predicate(input: &MetaItemOrLitParser) -> Result<Predicate, InvalidOnClause> {
457    let Some(meta_item) = input.meta_item() else {
458        return Err(InvalidOnClause::UnsupportedLiteral { span: input.span() });
459    };
460
461    let Some(predicate) = meta_item.ident() else {
462        return Err(InvalidOnClause::ExpectedIdentifier {
463            span: meta_item.path().span(),
464            path: meta_item.path().get_attribute_path(),
465        });
466    };
467
468    match meta_item.args() {
469        ArgParser::List(mis) => match predicate.name {
470            sym::any => Ok(Predicate::Any(parse_predicate_sequence(mis)?)),
471            sym::all => Ok(Predicate::All(parse_predicate_sequence(mis)?)),
472            sym::not => {
473                if let Some(single) = mis.single() {
474                    Ok(Predicate::Not(Box::new(parse_predicate(single)?)))
475                } else {
476                    Err(InvalidOnClause::ExpectedOnePredInNot { span: mis.span })
477                }
478            }
479            invalid_pred => {
480                Err(InvalidOnClause::InvalidPredicate { span: predicate.span, invalid_pred })
481            }
482        },
483        ArgParser::NameValue(p) => {
484            let Some(value) = p.value_as_ident() else {
485                return Err(InvalidOnClause::UnsupportedLiteral { span: p.args_span() });
486            };
487            let name = parse_name(predicate.name);
488            let value = parse_filter(value.name);
489            let kv = NameValue { name, value };
490            Ok(Predicate::Match(kv))
491        }
492        ArgParser::NoArgs => {
493            let flag = parse_flag(predicate)?;
494            Ok(Predicate::Flag(flag))
495        }
496    }
497}
498
499fn parse_predicate_sequence(
500    sequence: &MetaItemListParser,
501) -> Result<ThinVec<Predicate>, InvalidOnClause> {
502    sequence.mixed().map(parse_predicate).collect()
503}
504
505fn parse_flag(Ident { name, span }: Ident) -> Result<Flag, InvalidOnClause> {
506    match name {
507        sym::crate_local => Ok(Flag::CrateLocal),
508        sym::direct => Ok(Flag::Direct),
509        sym::from_desugaring => Ok(Flag::FromDesugaring),
510        invalid_flag => Err(InvalidOnClause::InvalidFlag { invalid_flag, span }),
511    }
512}
513
514fn parse_name(name: Symbol) -> Name {
515    match name {
516        kw::SelfUpper => Name::SelfUpper,
517        sym::from_desugaring => Name::FromDesugaring,
518        sym::cause => Name::Cause,
519        generic => Name::GenericArg(generic),
520    }
521}
522
523fn parse_filter(input: Symbol) -> FilterFormatString {
524    let pieces = Parser::new(input.as_str(), None, None, false, ParseMode::Diagnostic)
525        .map(|p| match p {
526            RpfPiece::Lit(s) => LitOrArg::Lit(Symbol::intern(s)),
527            // We just ignore formatspecs here
528            RpfPiece::NextArgument(a) => match a.position {
529                // In `TypeErrCtxt::on_unimplemented_note` we substitute `"{integral}"` even
530                // if the integer type has been resolved, to allow targeting all integers.
531                // `"{integer}"` and `"{float}"` come from numerics that haven't been inferred yet,
532                // from the `Display` impl of `InferTy` to be precise.
533                // `"{union|enum|struct}"` is used as a special selector for ADTs.
534                //
535                // Don't try to format these later!
536                Position::ArgumentNamed(
537                    arg @ ("integer" | "integral" | "float" | "union" | "enum" | "struct"),
538                ) => LitOrArg::Lit(Symbol::intern(&::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{{{0}}}", arg))
    })format!("{{{arg}}}"))),
539
540                Position::ArgumentNamed(arg) => LitOrArg::Arg(Symbol::intern(arg)),
541                Position::ArgumentImplicitlyIs(_) => LitOrArg::Lit(sym::empty_braces),
542                Position::ArgumentIs(idx) => LitOrArg::Lit(Symbol::intern(&::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("{{{0}}}", idx))
    })format!("{{{idx}}}"))),
543            },
544        })
545        .collect();
546    FilterFormatString { pieces }
547}
548
549#[derive(const _: () =
    {
        impl<'_sess, G> rustc_errors::Diagnostic<'_sess, G> for
            InvalidOnClause where G: rustc_errors::EmissionGuarantee {
            #[track_caller]
            fn into_diag(self, dcx: rustc_errors::DiagCtxtHandle<'_sess>,
                level: rustc_errors::Level) -> rustc_errors::Diag<'_sess, G> {
                match self {
                    InvalidOnClause::Empty { span: __binding_0 } => {
                        let mut diag =
                            rustc_errors::Diag::new(dcx, level,
                                rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("empty `on`-clause in `#[rustc_on_unimplemented]`")));
                        diag.code(E0232);
                        ;
                        diag.span(__binding_0);
                        diag.span_label(__binding_0,
                            rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("empty `on`-clause here")));
                        diag
                    }
                    InvalidOnClause::ExpectedOnePredInNot { span: __binding_0 }
                        => {
                        let mut diag =
                            rustc_errors::Diag::new(dcx, level,
                                rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("expected a single predicate in `not(..)`")));
                        diag.code(E0232);
                        ;
                        diag.span(__binding_0);
                        diag.span_label(__binding_0,
                            rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("unexpected quantity of predicates here")));
                        diag
                    }
                    InvalidOnClause::UnsupportedLiteral { span: __binding_0 } =>
                        {
                        let mut diag =
                            rustc_errors::Diag::new(dcx, level,
                                rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("literals inside `on`-clauses are not supported")));
                        diag.code(E0232);
                        ;
                        diag.span(__binding_0);
                        diag.span_label(__binding_0,
                            rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("unexpected literal here")));
                        diag
                    }
                    InvalidOnClause::ExpectedIdentifier {
                        span: __binding_0, path: __binding_1 } => {
                        let mut diag =
                            rustc_errors::Diag::new(dcx, level,
                                rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("expected an identifier inside this `on`-clause")));
                        diag.code(E0232);
                        ;
                        diag.arg("path", __binding_1);
                        diag.span(__binding_0);
                        diag.span_label(__binding_0,
                            rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("expected an identifier here, not `{$path}`")));
                        diag
                    }
                    InvalidOnClause::InvalidPredicate {
                        span: __binding_0, invalid_pred: __binding_1 } => {
                        let mut diag =
                            rustc_errors::Diag::new(dcx, level,
                                rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("this predicate is invalid")));
                        diag.code(E0232);
                        ;
                        diag.arg("invalid_pred", __binding_1);
                        diag.span(__binding_0);
                        diag.span_label(__binding_0,
                            rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("expected one of `any`, `all` or `not` here, not `{$invalid_pred}`")));
                        diag
                    }
                    InvalidOnClause::InvalidFlag {
                        span: __binding_0, invalid_flag: __binding_1 } => {
                        let mut diag =
                            rustc_errors::Diag::new(dcx, level,
                                rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("invalid flag in `on`-clause")));
                        diag.code(E0232);
                        ;
                        diag.arg("invalid_flag", __binding_1);
                        diag.span(__binding_0);
                        diag.span_label(__binding_0,
                            rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("expected one of the `crate_local`, `direct` or `from_desugaring` flags, not `{$invalid_flag}`")));
                        diag
                    }
                }
            }
        }
    };Diagnostic)]
550pub(crate) enum InvalidOnClause {
551    #[diag("empty `on`-clause in `#[rustc_on_unimplemented]`", code = E0232)]
552    Empty {
553        #[primary_span]
554        #[label("empty `on`-clause here")]
555        span: Span,
556    },
557    #[diag("expected a single predicate in `not(..)`", code = E0232)]
558    ExpectedOnePredInNot {
559        #[primary_span]
560        #[label("unexpected quantity of predicates here")]
561        span: Span,
562    },
563    #[diag("literals inside `on`-clauses are not supported", code = E0232)]
564    UnsupportedLiteral {
565        #[primary_span]
566        #[label("unexpected literal here")]
567        span: Span,
568    },
569    #[diag("expected an identifier inside this `on`-clause", code = E0232)]
570    ExpectedIdentifier {
571        #[primary_span]
572        #[label("expected an identifier here, not `{$path}`")]
573        span: Span,
574        path: AttrPath,
575    },
576    #[diag("this predicate is invalid", code = E0232)]
577    InvalidPredicate {
578        #[primary_span]
579        #[label("expected one of `any`, `all` or `not` here, not `{$invalid_pred}`")]
580        span: Span,
581        invalid_pred: Symbol,
582    },
583    #[diag("invalid flag in `on`-clause", code = E0232)]
584    InvalidFlag {
585        #[primary_span]
586        #[label(
587            "expected one of the `crate_local`, `direct` or `from_desugaring` flags, not `{$invalid_flag}`"
588        )]
589        span: Span,
590        invalid_flag: Symbol,
591    },
592}
593
594#[derive(const _: () =
    {
        impl<'_sess, G> rustc_errors::Diagnostic<'_sess, G> for
            DupesNotAllowed where G: rustc_errors::EmissionGuarantee {
            #[track_caller]
            fn into_diag(self, dcx: rustc_errors::DiagCtxtHandle<'_sess>,
                level: rustc_errors::Level) -> rustc_errors::Diag<'_sess, G> {
                match self {
                    DupesNotAllowed => {
                        let mut diag =
                            rustc_errors::Diag::new(dcx, level,
                                rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("using multiple `rustc_on_unimplemented` (or mixing it with `diagnostic::on_unimplemented`) is not supported")));
                        ;
                        diag
                    }
                }
            }
        }
    };Diagnostic)]
595#[diag(
596    "using multiple `rustc_on_unimplemented` (or mixing it with `diagnostic::on_unimplemented`) is not supported"
597)]
598pub(crate) struct DupesNotAllowed;