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 RustcOnUnimplemented,
33 DiagnosticOnUnimplemented,
35 DiagnosticOnConst,
37 DiagnosticOnMove,
39 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 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 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 {
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 Position::ArgumentNamed(name) => match (mode, Symbol::intern(name)) {
348 (Mode::RustcOnUnimplemented { .. }, sym::ItemContext) => FormatArg::ItemContext,
350 (Mode::RustcOnUnimplemented { .. }, sym::This) => FormatArg::This,
351 (Mode::RustcOnUnimplemented { .. }, sym::Trait) => FormatArg::Trait,
352 (_, kw::SelfUpper) => FormatArg::SelfUpper,
354 (_, generic_param) => FormatArg::GenericParam { generic_param, span },
355 },
356
357 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
375fn 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 RpfPiece::NextArgument(a) => match a.position {
478 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;