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