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