1use std::ops::Range;
2
3use rustc_ast::PathSegment;
4use rustc_attr_ir::diagnostic::{
5 Directive, Filter, FilterFormatString, Flag, FormatArg, FormatString, LitOrArg, Name,
6 NameValue, Piece, Predicate,
7};
8use rustc_errors::{Diagnostic, MultiSpan};
9use rustc_lint_defs::LintId;
10use rustc_parse_format::{
11 Argument, FormatSpec, ParseError, ParseMode, Parser, Piece as RpfPiece, Position,
12};
13use rustc_session::lint::builtin::{
14 MALFORMED_DIAGNOSTIC_ATTRIBUTES, MALFORMED_DIAGNOSTIC_FILTERS,
15 MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, UNKNOWN_DIAGNOSTIC_ATTRIBUTES,
16};
17use rustc_span::edit_distance::find_best_match_for_name;
18use rustc_span::{Ident, InnerSpan, Span, Symbol, kw, sym};
19use thin_vec::{ThinVec, thin_vec};
20
21use crate::context::AcceptContext;
22use crate::diagnostics::{
23 FormatWarning, IgnoredDiagnosticOption, InvalidOnClause, MalFormedDiagnosticAttributeLint,
24 MissingOptionsForDiagnosticAttribute, NonMetaItemDiagnosticAttribute, WrappedParserError,
25};
26use crate::parser::{ArgParser, MetaItemListParser, MetaItemOrLitParser, MetaItemParser};
27use crate::{EmitAttribute, diagnostics};
28
29pub(crate) mod do_not_recommend;
30pub(crate) mod on_const;
31pub(crate) mod on_move;
32pub(crate) mod on_type_error;
33pub(crate) mod on_unimplemented;
34pub(crate) mod on_unknown;
35pub(crate) mod on_unmatched_args;
36pub(crate) mod opaque;
37
38impl<'sess> crate::AttributeParser<'sess> {
39 pub(crate) fn unknown_diagnostic_attr(
40 &self,
41 segment: &PathSegment,
42 mut emit_lint: impl FnMut(LintId, MultiSpan, EmitAttribute),
43 ) {
44 const DIAGNOSTIC_ATTRIBUTES: [(
45 Symbol, Option<Symbol>, ); 8] = [
48 (sym::on_unimplemented, None),
49 (sym::do_not_recommend, None),
50 (sym::on_move, Some(sym::diagnostic_on_move)),
51 (sym::on_const, Some(sym::diagnostic_on_const)),
52 (sym::on_unknown, Some(sym::diagnostic_on_unknown)),
53 (sym::on_unmatched_args, Some(sym::diagnostic_on_unmatched_args)),
54 (sym::on_type_error, Some(sym::diagnostic_on_type_error)),
55 (sym::opaque, Some(sym::diagnostic_opaque)),
56 ];
57 let Some(features) = self.features else { return };
59 let span = segment.span();
60 let candidates = DIAGNOSTIC_ATTRIBUTES
61 .iter()
62 .filter_map(|(attr, feature)| {
63 feature.is_none_or(|f| features.enabled(f)).then_some(*attr)
64 })
65 .collect::<Vec<_>>();
66
67 let typo = find_best_match_for_name(&candidates, segment.ident.name, None)
68 .map(|typo_name| diagnostics::UnknownDiagnosticAttributeTypo { span, typo_name });
69 emit_lint(
70 LintId::of(UNKNOWN_DIAGNOSTIC_ATTRIBUTES),
71 span.into(),
72 EmitAttribute(Box::new(move |dcx, level, _| {
73 diagnostics::UnknownDiagnosticAttribute { typo }.into_diag(dcx, level)
74 })),
75 )
76 }
77}
78
79#[rustc_macro_transparency = "transparent"]
80macro gate_diagnostic_attr($feature:ident) {{
81 if let Some(features) = cx.features_option()
82 && !features.$feature()
83 {
84 args.ignore_args();
85 let nightly_build = cx.sess.is_nightly_build();
86 let span = cx.attr_span;
87 cx.emit_lint(
88 rustc_lint_defs::builtin::UNKNOWN_DIAGNOSTIC_ATTRIBUTES,
89 $crate::diagnostics::UnstableDiagnosticAttribute {
90 feature: sym::$feature,
91 nightly_build,
92 },
93 span,
94 );
95 return;
96 }
97}}
98
99#[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)]
100pub(crate) enum Mode {
101 RustcOnUnimplemented,
103 DiagnosticOnUnimplemented,
105 DiagnosticOnConst,
107 DiagnosticOnMove,
109 DiagnosticOnUnknown,
111 DiagnosticOnUnmatchedArgs,
113 DiagnosticOnTypeError,
115}
116
117impl Mode {
118 fn as_str(self) -> &'static str {
119 match self {
120 Self::RustcOnUnimplemented => "rustc_on_unimplemented",
121 Self::DiagnosticOnUnimplemented => "diagnostic::on_unimplemented",
122 Self::DiagnosticOnConst => "diagnostic::on_const",
123 Self::DiagnosticOnMove => "diagnostic::on_move",
124 Self::DiagnosticOnUnknown => "diagnostic::on_unknown",
125 Self::DiagnosticOnUnmatchedArgs => "diagnostic::on_unmatched_args",
126 Self::DiagnosticOnTypeError => "diagnostic::on_type_error",
127 }
128 }
129
130 fn expected_options(self) -> &'static str {
131 const DEFAULT: &str =
132 "at least one of the `message`, `note` and `label` options are expected";
133 const DIAGNOSTIC_ON_TYPE_ERROR_EXPECTED_OPTIONS: &str =
134 "at least a single `note` option is expected";
135 match self {
136 Self::RustcOnUnimplemented => {
137 "see <https://rustc-dev-guide.rust-lang.org/diagnostics.html#rustc_on_unimplemented>"
138 }
139 Self::DiagnosticOnUnimplemented
140 | Self::DiagnosticOnConst
141 | Self::DiagnosticOnMove
142 | Self::DiagnosticOnUnknown
143 | Self::DiagnosticOnUnmatchedArgs => DEFAULT,
144 Self::DiagnosticOnTypeError => DIAGNOSTIC_ON_TYPE_ERROR_EXPECTED_OPTIONS,
145 }
146 }
147
148 fn allowed_options(self) -> &'static str {
149 const DEFAULT: &str = "only `message`, `note` and `label` are allowed as options";
150 const DIAGNOSTIC_ON_TYPE_ERROR_ALLOWED_OPTIONS: &str =
151 "only `note` is allowed as option for `diagnostic::on_type_error`";
152 match self {
153 Self::RustcOnUnimplemented => {
154 "see <https://rustc-dev-guide.rust-lang.org/diagnostics.html#rustc_on_unimplemented>"
155 }
156 Self::DiagnosticOnUnimplemented
157 | Self::DiagnosticOnConst
158 | Self::DiagnosticOnMove
159 | Self::DiagnosticOnUnknown
160 | Self::DiagnosticOnUnmatchedArgs => DEFAULT,
161 Self::DiagnosticOnTypeError => DIAGNOSTIC_ON_TYPE_ERROR_ALLOWED_OPTIONS,
162 }
163 }
164
165 fn allowed_format_arguments(self) -> &'static str {
166 match self {
167 Self::RustcOnUnimplemented => {
168 "see <https://rustc-dev-guide.rust-lang.org/diagnostics.html#rustc_on_unimplemented> for allowed format arguments"
169 }
170 Self::DiagnosticOnUnimplemented => {
171 "only `Self` and generics of the trait are allowed as a format argument"
172 }
173 Self::DiagnosticOnConst => {
174 "only `Self` and generics of the implementation are allowed as a format argument"
175 }
176 Self::DiagnosticOnMove => {
177 "only `This`, `Self` and generics of the type are allowed as a format argument"
178 }
179 Self::DiagnosticOnUnknown => {
180 "only `This` is allowed as a format argument, referring to the failed import"
181 }
182 Self::DiagnosticOnUnmatchedArgs => {
183 "only `This` is allowed as a format argument, referring to the macro's name"
184 }
185 Self::DiagnosticOnTypeError => {
186 "only `note` is allowed as option for `diagnostic::on_type_error`"
187 }
188 }
189 }
190}
191
192fn merge_directives(
193 cx: &mut AcceptContext<'_, '_>,
194 first: &mut Option<(Span, Directive)>,
195 later: (Span, Directive),
196) {
197 if let Some((_, first)) = first {
198 let Directive { is_rustc_attr, filters, message, label, notes, parent_label } = later.1;
199
200 first.is_rustc_attr |= is_rustc_attr;
201 first.filters.extend(filters);
202 merge(cx, &mut first.message, message, sym::message);
203 merge(cx, &mut first.label, label, sym::label);
204 first.notes.extend(notes);
205 merge(cx, &mut first.parent_label, parent_label, sym::parent_label);
206 } else {
207 *first = Some(later);
208 }
209}
210
211fn merge<T>(
212 cx: &mut AcceptContext<'_, '_>,
213 first: &mut Option<(Span, T)>,
214 later: Option<(Span, T)>,
215 option_name: Symbol,
216) {
217 match (first, later) {
218 (Some(_) | None, None) => {}
219 (Some((first_span, _)), Some((later_span, _))) => {
220 let first_span = *first_span;
221 cx.emit_lint(
222 MALFORMED_DIAGNOSTIC_ATTRIBUTES,
223 IgnoredDiagnosticOption { first_span, later_span, option_name },
224 later_span,
225 );
226 }
227 (first @ None, Some(later)) => {
228 first.get_or_insert(later);
229 }
230 }
231}
232
233fn parse_list<'p>(
234 cx: &mut AcceptContext<'_, '_>,
235 args: &'p ArgParser,
236 mode: Mode,
237) -> Option<&'p MetaItemListParser> {
238 let span = cx.attr_span;
239 match args {
240 ArgParser::List(items) if items.len() != 0 => return Some(items),
241 ArgParser::List(list) => {
242 cx.emit_lint(
246 MALFORMED_DIAGNOSTIC_ATTRIBUTES,
247 NonMetaItemDiagnosticAttribute,
248 list.span,
249 );
250 }
251 ArgParser::NoArgs => {
252 cx.emit_lint(
253 MALFORMED_DIAGNOSTIC_ATTRIBUTES,
254 MissingOptionsForDiagnosticAttribute {
255 attribute: mode.as_str(),
256 options: mode.expected_options(),
257 },
258 span,
259 );
260 }
261 ArgParser::NameValue(_) => {
262 cx.emit_lint(
263 MALFORMED_DIAGNOSTIC_ATTRIBUTES,
264 MalFormedDiagnosticAttributeLint {
265 attribute: mode.as_str(),
266 options: mode.allowed_options(),
267 span,
268 },
269 span,
270 );
271 }
272 }
273 None
274}
275
276fn parse_directive_items<'p>(
277 cx: &mut AcceptContext<'_, '_>,
278 mode: Mode,
279 items: impl Iterator<Item = &'p MetaItemOrLitParser>,
280 is_root: bool,
281) -> Option<Directive> {
282 let mut message: Option<(Span, _)> = None;
283 let mut label: Option<(Span, _)> = None;
284 let mut notes = ThinVec::new();
285 let mut parent_label: Option<(Span, FormatString)> = None;
286 let mut filters = ThinVec::new();
287
288 for item in items {
289 let span = item.span();
290
291 macro malformed() {{
292 cx.emit_lint(
293 MALFORMED_DIAGNOSTIC_ATTRIBUTES,
294 MalFormedDiagnosticAttributeLint {
295 attribute: mode.as_str(),
296 options: mode.allowed_options(),
297 span,
298 },
299 span,
300 );
301 continue;
302 }}
303
304 macro or_malformed($($code:tt)*) {{
305 let Some(ret) = (
306 try {
307 $($code)*
308 }
309 ) else {
310 malformed!()
311 };
312 ret
313 }}
314
315 macro duplicate($name: ident, $($first_span:tt)*) {{
316 let first_span = $($first_span)*;
317 cx.emit_lint(
318 MALFORMED_DIAGNOSTIC_ATTRIBUTES,
319 IgnoredDiagnosticOption {
320 first_span,
321 later_span: span,
322 option_name: $name,
323 },
324 span,
325 );
326 }}
327
328 let item: &MetaItemParser = {
let Some(ret) =
(try {
item.meta_item()?
}) else {
{
cx.emit_lint(MALFORMED_DIAGNOSTIC_ATTRIBUTES,
MalFormedDiagnosticAttributeLint {
attribute: mode.as_str(),
options: mode.allowed_options(),
span,
}, span);
continue;
}
};
ret
}or_malformed!(item.meta_item()?);
329 let name = {
let Some(ret) =
(try {
item.ident()?
}) else {
{
cx.emit_lint(MALFORMED_DIAGNOSTIC_ATTRIBUTES,
MalFormedDiagnosticAttributeLint {
attribute: mode.as_str(),
options: mode.allowed_options(),
span,
}, span);
continue;
}
};
ret
}or_malformed!(item.ident()?).name;
330
331 let value: Option<Ident> = match item.args().as_name_value() {
339 Some(nv) => Some({
let Some(ret) =
(try {
nv.value_as_ident()?
}) else {
{
cx.emit_lint(MALFORMED_DIAGNOSTIC_ATTRIBUTES,
MalFormedDiagnosticAttributeLint {
attribute: mode.as_str(),
options: mode.allowed_options(),
span,
}, span);
continue;
}
};
ret
}or_malformed!(nv.value_as_ident()?)),
340 None => None,
341 };
342
343 let mut parse_format = |input: Ident| {
344 let snippet = cx.sess.source_map().span_to_snippet(input.span).ok();
345 let is_snippet = snippet.is_some();
346 match parse_format_string(input.name, snippet, input.span, mode) {
347 Ok((f, warnings)) => {
348 for warning in warnings {
349 let (FormatWarning::InvalidSpecifier { span }
350 | FormatWarning::PositionalArgument { span }
351 | FormatWarning::IndexedArgument { span }
352 | FormatWarning::DisallowedPlaceholder { span, .. }) = warning;
353 cx.emit_lint(MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, warning, span);
354 }
355
356 f
357 }
358 Err(e) => {
359 cx.emit_lint(
360 MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
361 WrappedParserError {
362 description: e.description,
363 label: e.label,
364 span: slice_span(input.span, e.span.clone(), is_snippet),
365 },
366 input.span,
367 );
368 FormatString {
370 input: input.name,
371 span: input.span,
372 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)],
373 }
374 }
375 }
376 };
377 match (mode, name) {
378 (
379 Mode::RustcOnUnimplemented
380 | Mode::DiagnosticOnUnimplemented
381 | Mode::DiagnosticOnConst
382 | Mode::DiagnosticOnMove
383 | Mode::DiagnosticOnUnknown
384 | Mode::DiagnosticOnUnmatchedArgs,
385 sym::message,
386 ) => {
387 let value = {
let Some(ret) =
(try {
value?
}) else {
{
cx.emit_lint(MALFORMED_DIAGNOSTIC_ATTRIBUTES,
MalFormedDiagnosticAttributeLint {
attribute: mode.as_str(),
options: mode.allowed_options(),
span,
}, span);
continue;
}
};
ret
}or_malformed!(value?);
388 if let Some(message) = &message {
389 {
let first_span = message.0;
cx.emit_lint(MALFORMED_DIAGNOSTIC_ATTRIBUTES,
IgnoredDiagnosticOption {
first_span,
later_span: span,
option_name: name,
}, span);
}duplicate!(name, message.0)
390 } else {
391 message = Some((item.span(), parse_format(value)));
392 }
393 }
394 (
395 Mode::RustcOnUnimplemented
396 | Mode::DiagnosticOnUnimplemented
397 | Mode::DiagnosticOnConst
398 | Mode::DiagnosticOnMove
399 | Mode::DiagnosticOnUnknown
400 | Mode::DiagnosticOnUnmatchedArgs,
401 sym::label,
402 ) => {
403 let value = {
let Some(ret) =
(try {
value?
}) else {
{
cx.emit_lint(MALFORMED_DIAGNOSTIC_ATTRIBUTES,
MalFormedDiagnosticAttributeLint {
attribute: mode.as_str(),
options: mode.allowed_options(),
span,
}, span);
continue;
}
};
ret
}or_malformed!(value?);
404 if let Some(label) = &label {
405 {
let first_span = label.0;
cx.emit_lint(MALFORMED_DIAGNOSTIC_ATTRIBUTES,
IgnoredDiagnosticOption {
first_span,
later_span: span,
option_name: name,
}, span);
}duplicate!(name, label.0)
406 } else {
407 label = Some((item.span(), parse_format(value)));
408 }
409 }
410 (_, sym::note) => {
411 let value = {
let Some(ret) =
(try {
value?
}) else {
{
cx.emit_lint(MALFORMED_DIAGNOSTIC_ATTRIBUTES,
MalFormedDiagnosticAttributeLint {
attribute: mode.as_str(),
options: mode.allowed_options(),
span,
}, span);
continue;
}
};
ret
}or_malformed!(value?);
412 notes.push(parse_format(value))
413 }
414 (Mode::RustcOnUnimplemented, sym::parent_label) => {
415 let value = {
let Some(ret) =
(try {
value?
}) else {
{
cx.emit_lint(MALFORMED_DIAGNOSTIC_ATTRIBUTES,
MalFormedDiagnosticAttributeLint {
attribute: mode.as_str(),
options: mode.allowed_options(),
span,
}, span);
continue;
}
};
ret
}or_malformed!(value?);
416 if let Some(parent_label) = &parent_label {
417 {
let first_span = parent_label.0;
cx.emit_lint(MALFORMED_DIAGNOSTIC_ATTRIBUTES,
IgnoredDiagnosticOption {
first_span,
later_span: span,
option_name: name,
}, span);
}duplicate!(name, parent_label.0)
418 } else {
419 let format = parse_format(value);
420 parent_label = Some((format.span, format));
421 }
422 }
423 (Mode::RustcOnUnimplemented, sym::on) => {
424 if is_root {
425 let items = {
let Some(ret) =
(try {
item.args().as_list()?
}) else {
{
cx.emit_lint(MALFORMED_DIAGNOSTIC_ATTRIBUTES,
MalFormedDiagnosticAttributeLint {
attribute: mode.as_str(),
options: mode.allowed_options(),
span,
}, span);
continue;
}
};
ret
}or_malformed!(item.args().as_list()?);
426 let mut iter = items.mixed();
427 let filter = if let Some(c) = iter.next() {
428 c
429 } else {
430 cx.emit_lint(
431 MALFORMED_DIAGNOSTIC_FILTERS,
432 InvalidOnClause::Empty { span },
433 span,
434 );
435 continue;
436 };
437
438 let filter = parse_filter(filter);
439
440 if items.len() < 2 {
441 {
cx.emit_lint(MALFORMED_DIAGNOSTIC_ATTRIBUTES,
MalFormedDiagnosticAttributeLint {
attribute: mode.as_str(),
options: mode.allowed_options(),
span,
}, span);
continue;
};malformed!();
444 }
445
446 match filter {
447 Ok(filter) => {
448 let directive =
449 {
let Some(ret) =
(try {
parse_directive_items(cx, mode, iter, false)?
}) else {
{
cx.emit_lint(MALFORMED_DIAGNOSTIC_ATTRIBUTES,
MalFormedDiagnosticAttributeLint {
attribute: mode.as_str(),
options: mode.allowed_options(),
span,
}, span);
continue;
}
};
ret
}or_malformed!(parse_directive_items(cx, mode, iter, false)?);
450 filters.push((filter, directive));
451 }
452 Err(e) => {
453 cx.emit_lint(MALFORMED_DIAGNOSTIC_FILTERS, e, span);
454 }
455 }
456 } else {
457 {
cx.emit_lint(MALFORMED_DIAGNOSTIC_ATTRIBUTES,
MalFormedDiagnosticAttributeLint {
attribute: mode.as_str(),
options: mode.allowed_options(),
span,
}, span);
continue;
};malformed!();
458 }
459 }
460 _other => {
461 {
cx.emit_lint(MALFORMED_DIAGNOSTIC_ATTRIBUTES,
MalFormedDiagnosticAttributeLint {
attribute: mode.as_str(),
options: mode.allowed_options(),
span,
}, span);
continue;
};malformed!();
462 }
463 }
464 }
465
466 Some(Directive {
467 is_rustc_attr: #[allow(non_exhaustive_omitted_patterns)] match mode {
Mode::RustcOnUnimplemented => true,
_ => false,
}matches!(mode, Mode::RustcOnUnimplemented),
468 filters,
469 message,
470 label,
471 notes,
472 parent_label,
473 })
474}
475
476pub(crate) fn parse_format_string(
477 input: Symbol,
478 snippet: Option<String>,
479 span: Span,
480 mode: Mode,
481) -> Result<(FormatString, Vec<FormatWarning>), ParseError> {
482 let s = input.as_str();
483 let mut parser = Parser::new(s, None, snippet, false, ParseMode::Diagnostic);
484 let pieces: Vec<_> = parser.by_ref().collect();
485
486 if let Some(err) = parser.errors.into_iter().next() {
487 return Err(err);
488 }
489 let mut warnings = Vec::new();
490
491 let pieces = pieces
492 .into_iter()
493 .map(|piece| match piece {
494 RpfPiece::Lit(lit) => Piece::Lit(Symbol::intern(lit)),
495 RpfPiece::NextArgument(arg) => {
496 Piece::Arg(parse_arg(&arg, mode, &mut warnings, span, parser.is_source_literal))
497 }
498 })
499 .collect();
500
501 Ok((FormatString { input, pieces, span }, warnings))
502}
503
504fn parse_arg(
505 arg: &Argument<'_>,
506 mode: Mode,
507 warnings: &mut Vec<FormatWarning>,
508 input_span: Span,
509 is_source_literal: bool,
510) -> FormatArg {
511 let span = slice_span(input_span, arg.position_span.clone(), is_source_literal);
512
513 let mut check_format = true;
514
515 let ret = match arg.position {
516 Position::ArgumentNamed(name) => match (mode, Symbol::intern(name)) {
518 (Mode::RustcOnUnimplemented, sym::ItemContext) => FormatArg::ItemContext,
519
520 (Mode::RustcOnUnimplemented, sym::This) => match arg.format.ty {
522 "resolved" => {
523 check_format = false;
524 FormatArg::ThisResolved
525 }
526 "path" => {
527 check_format = false;
528 FormatArg::ThisPath
529 }
530 _ => FormatArg::This,
531 },
532
533 (Mode::DiagnosticOnTypeError, sym::Found) => FormatArg::Found,
534 (Mode::DiagnosticOnTypeError, sym::Expected) => FormatArg::Expected,
535 (Mode::DiagnosticOnUnknown, sym::Unresolved) => FormatArg::Unresolved,
536
537 (
543 Mode::DiagnosticOnUnknown
544 | Mode::DiagnosticOnMove
545 | Mode::DiagnosticOnUnmatchedArgs
546 | Mode::DiagnosticOnTypeError,
547 sym::This,
548 ) => FormatArg::This,
549
550 (
556 Mode::RustcOnUnimplemented
557 | Mode::DiagnosticOnUnimplemented
558 | Mode::DiagnosticOnMove
559 | Mode::DiagnosticOnConst,
560 kw::SelfUpper,
561 ) => FormatArg::SelfUpper,
562
563 (
569 Mode::RustcOnUnimplemented
570 | Mode::DiagnosticOnUnimplemented
571 | Mode::DiagnosticOnMove
572 | Mode::DiagnosticOnConst
573 | Mode::DiagnosticOnTypeError,
574 generic_param,
575 ) => FormatArg::GenericParam { generic_param, span },
576
577 (Mode::DiagnosticOnUnknown | Mode::DiagnosticOnUnmatchedArgs, as_is) => {
579 warnings.push(FormatWarning::DisallowedPlaceholder {
580 span,
581 attr: mode.as_str(),
582 allowed: mode.allowed_format_arguments(),
583 });
584 FormatArg::AsIs(Symbol::intern(&::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{{{0}}}", as_is))
})format!("{{{as_is}}}")))
585 }
586 },
587
588 Position::ArgumentIs(idx) => {
590 warnings.push(FormatWarning::IndexedArgument { span });
591 FormatArg::AsIs(Symbol::intern(&::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{{{0}}}", idx))
})format!("{{{idx}}}")))
592 }
593 Position::ArgumentImplicitlyIs(_) => {
594 warnings.push(FormatWarning::PositionalArgument { span });
595 FormatArg::AsIs(sym::empty_braces)
596 }
597 };
598 if check_format {
599 warn_on_format_spec(&arg.format, warnings, input_span, is_source_literal);
600 }
601 ret
602}
603
604fn warn_on_format_spec(
607 spec: &FormatSpec<'_>,
608 warnings: &mut Vec<FormatWarning>,
609 input_span: Span,
610 is_source_literal: bool,
611) {
612 if let Some(ty_span) = &spec.ty_span {
613 let span = slice_span(input_span, ty_span.clone(), is_source_literal);
614 warnings.push(FormatWarning::InvalidSpecifier { span })
615 }
616}
617
618fn slice_span(input: Span, Range { start, end }: Range<usize>, is_source_literal: bool) -> Span {
619 if is_source_literal { input.from_inner(InnerSpan { start, end }) } else { input }
620}
621
622pub(crate) fn parse_filter(input: &MetaItemOrLitParser) -> Result<Filter, InvalidOnClause> {
623 let span = input.span();
624 let pred = parse_predicate(input)?;
625 Ok(Filter { span, pred })
626}
627
628fn parse_predicate(input: &MetaItemOrLitParser) -> Result<Predicate, InvalidOnClause> {
629 let Some(meta_item) = input.meta_item() else {
630 return Err(InvalidOnClause::UnsupportedLiteral { span: input.span() });
631 };
632
633 let Some(predicate) = meta_item.ident() else {
634 return Err(InvalidOnClause::ExpectedIdentifier {
635 span: meta_item.path().span(),
636 path: meta_item.path().get_attribute_path(),
637 });
638 };
639
640 match meta_item.args() {
641 ArgParser::List(mis) => match predicate.name {
642 sym::any => Ok(Predicate::Any(parse_predicate_sequence(mis)?)),
643 sym::all => Ok(Predicate::All(parse_predicate_sequence(mis)?)),
644 sym::not => {
645 if let Some(single) = mis.as_single() {
646 Ok(Predicate::Not(Box::new(parse_predicate(single)?)))
647 } else {
648 Err(InvalidOnClause::ExpectedOnePredInNot { span: mis.span })
649 }
650 }
651 invalid_pred => {
652 Err(InvalidOnClause::InvalidPredicate { span: predicate.span, invalid_pred })
653 }
654 },
655 ArgParser::NameValue(p) => {
656 let Some(value) = p.value_as_ident() else {
657 return Err(InvalidOnClause::UnsupportedLiteral { span: p.args_span() });
658 };
659 let name = parse_name(predicate.name);
660 let value = parse_filter_format(value.name);
661 let kv = NameValue { name, value };
662 Ok(Predicate::Match(kv))
663 }
664 ArgParser::NoArgs => {
665 let flag = parse_flag(predicate)?;
666 Ok(Predicate::Flag(flag))
667 }
668 }
669}
670
671fn parse_predicate_sequence(
672 sequence: &MetaItemListParser,
673) -> Result<ThinVec<Predicate>, InvalidOnClause> {
674 sequence.mixed().map(parse_predicate).collect()
675}
676
677fn parse_flag(Ident { name, span }: Ident) -> Result<Flag, InvalidOnClause> {
678 match name {
679 sym::crate_local => Ok(Flag::CrateLocal),
680 sym::direct => Ok(Flag::Direct),
681 sym::from_desugaring => Ok(Flag::FromDesugaring),
682 invalid_flag => Err(InvalidOnClause::InvalidFlag { invalid_flag, span }),
683 }
684}
685
686fn parse_name(name: Symbol) -> Name {
687 match name {
688 kw::SelfUpper => Name::SelfUpper,
689 sym::from_desugaring => Name::FromDesugaring,
690 sym::cause => Name::Cause,
691 generic => Name::GenericArg(generic),
692 }
693}
694
695fn parse_filter_format(input: Symbol) -> FilterFormatString {
696 let pieces = Parser::new(input.as_str(), None, None, false, ParseMode::Diagnostic)
697 .map(|p| match p {
698 RpfPiece::Lit(s) => LitOrArg::Lit(Symbol::intern(s)),
699 RpfPiece::NextArgument(a) => match a.position {
701 Position::ArgumentNamed(
709 arg @ ("integer" | "integral" | "float" | "union" | "enum" | "struct"),
710 ) => LitOrArg::Lit(Symbol::intern(&::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{{{0}}}", arg))
})format!("{{{arg}}}"))),
711
712 Position::ArgumentNamed(arg) => LitOrArg::Arg(Symbol::intern(arg)),
713 Position::ArgumentImplicitlyIs(_) => LitOrArg::Lit(sym::empty_braces),
714 Position::ArgumentIs(idx) => LitOrArg::Lit(Symbol::intern(&::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{{{0}}}", idx))
})format!("{{{idx}}}"))),
715 },
716 })
717 .collect();
718 FilterFormatString { pieces }
719}