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