1use std::fmt;
2use std::ops::Range;
3
4use errors::*;
5use rustc_middle::ty::print::TraitRefPrintSugared;
6use rustc_middle::ty::{GenericParamDefKind, TyCtxt};
7use rustc_parse_format::{
8 Argument, FormatSpec, ParseError, ParseMode, Parser, Piece as RpfPiece, Position,
9};
10use rustc_session::lint::builtin::MALFORMED_DIAGNOSTIC_FORMAT_LITERALS;
11use rustc_span::def_id::DefId;
12use rustc_span::{InnerSpan, Span, Symbol, kw, sym};
13
14#[derive(#[automatically_derived]
impl ::core::fmt::Debug for FormatString {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field4_finish(f, "FormatString",
"input", &self.input, "span", &self.span, "pieces", &self.pieces,
"warnings", &&self.warnings)
}
}Debug)]
17pub struct FormatString {
18 #[allow(dead_code, reason = "Debug impl")]
19 input: Symbol,
20 span: Span,
21 pieces: Vec<Piece>,
22 pub warnings: Vec<FormatWarning>,
24}
25
26#[derive(#[automatically_derived]
impl ::core::fmt::Debug for Piece {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
Piece::Lit(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Lit",
&__self_0),
Piece::Arg(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Arg",
&__self_0),
}
}
}Debug)]
27enum Piece {
28 Lit(String),
29 Arg(FormatArg),
30}
31
32#[derive(#[automatically_derived]
impl ::core::fmt::Debug for FormatArg {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
FormatArg::GenericParam { generic_param: __self_0 } =>
::core::fmt::Formatter::debug_struct_field1_finish(f,
"GenericParam", "generic_param", &__self_0),
FormatArg::SelfUpper =>
::core::fmt::Formatter::write_str(f, "SelfUpper"),
FormatArg::This => ::core::fmt::Formatter::write_str(f, "This"),
FormatArg::Trait => ::core::fmt::Formatter::write_str(f, "Trait"),
FormatArg::ItemContext =>
::core::fmt::Formatter::write_str(f, "ItemContext"),
FormatArg::AsIs(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "AsIs",
&__self_0),
}
}
}Debug)]
33enum FormatArg {
34 GenericParam {
36 generic_param: Symbol,
37 },
38 SelfUpper,
40 This,
42 Trait,
44 ItemContext,
46 AsIs(String),
48}
49
50pub enum Ctx<'tcx> {
51 RustcOnUnimplemented { tcx: TyCtxt<'tcx>, trait_def_id: DefId },
53 DiagnosticOnUnimplemented { tcx: TyCtxt<'tcx>, trait_def_id: DefId },
55}
56
57#[derive(#[automatically_derived]
impl ::core::fmt::Debug for FormatWarning {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
FormatWarning::UnknownParam {
argument_name: __self_0, span: __self_1 } =>
::core::fmt::Formatter::debug_struct_field2_finish(f,
"UnknownParam", "argument_name", __self_0, "span",
&__self_1),
FormatWarning::PositionalArgument { span: __self_0, help: __self_1
} =>
::core::fmt::Formatter::debug_struct_field2_finish(f,
"PositionalArgument", "span", __self_0, "help", &__self_1),
FormatWarning::InvalidSpecifier { name: __self_0, span: __self_1 }
=>
::core::fmt::Formatter::debug_struct_field2_finish(f,
"InvalidSpecifier", "name", __self_0, "span", &__self_1),
FormatWarning::FutureIncompat { span: __self_0, help: __self_1 }
=>
::core::fmt::Formatter::debug_struct_field2_finish(f,
"FutureIncompat", "span", __self_0, "help", &__self_1),
}
}
}Debug)]
58pub enum FormatWarning {
59 UnknownParam { argument_name: Symbol, span: Span },
60 PositionalArgument { span: Span, help: String },
61 InvalidSpecifier { name: String, span: Span },
62 FutureIncompat { span: Span, help: String },
63}
64
65impl FormatWarning {
66 pub fn emit_warning<'tcx>(&self, tcx: TyCtxt<'tcx>, item_def_id: DefId) {
67 match *self {
68 FormatWarning::UnknownParam { argument_name, span } => {
69 let this = tcx.item_ident(item_def_id);
70 if let Some(item_def_id) = item_def_id.as_local() {
71 tcx.emit_node_span_lint(
72 MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
73 tcx.local_def_id_to_hir_id(item_def_id),
74 span,
75 UnknownFormatParameterForOnUnimplementedAttr {
76 argument_name,
77 trait_name: this,
78 },
79 );
80 }
81 }
82 FormatWarning::PositionalArgument { span, .. } => {
83 if let Some(item_def_id) = item_def_id.as_local() {
84 tcx.emit_node_span_lint(
85 MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
86 tcx.local_def_id_to_hir_id(item_def_id),
87 span,
88 DisallowedPositionalArgument,
89 );
90 }
91 }
92 FormatWarning::InvalidSpecifier { span, .. } => {
93 if let Some(item_def_id) = item_def_id.as_local() {
94 tcx.emit_node_span_lint(
95 MALFORMED_DIAGNOSTIC_FORMAT_LITERALS,
96 tcx.local_def_id_to_hir_id(item_def_id),
97 span,
98 InvalidFormatSpecifier,
99 );
100 }
101 }
102 FormatWarning::FutureIncompat { .. } => {
103 }
109 }
110 }
111}
112
113#[derive(#[automatically_derived]
impl<'tcx> ::core::fmt::Debug for FormatArgs<'tcx> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field4_finish(f, "FormatArgs",
"this", &self.this, "trait_sugared", &self.trait_sugared,
"item_context", &self.item_context, "generic_args",
&&self.generic_args)
}
}Debug)]
148pub struct FormatArgs<'tcx> {
149 pub this: String,
150 pub trait_sugared: TraitRefPrintSugared<'tcx>,
151 pub item_context: &'static str,
152 pub generic_args: Vec<(Symbol, String)>,
153}
154
155impl FormatString {
156 pub fn span(&self) -> Span {
157 self.span
158 }
159
160 pub fn parse<'tcx>(
161 input: Symbol,
162 snippet: Option<String>,
163 span: Span,
164 ctx: &Ctx<'tcx>,
165 ) -> Result<Self, ParseError> {
166 let s = input.as_str();
167 let mut parser = Parser::new(s, None, snippet, false, ParseMode::Diagnostic);
168 let pieces: Vec<_> = parser.by_ref().collect();
169
170 if let Some(err) = parser.errors.into_iter().next() {
171 return Err(err);
172 }
173 let mut warnings = Vec::new();
174
175 let pieces = pieces
176 .into_iter()
177 .map(|piece| match piece {
178 RpfPiece::Lit(lit) => Piece::Lit(lit.into()),
179 RpfPiece::NextArgument(arg) => {
180 warn_on_format_spec(&arg.format, &mut warnings, span, parser.is_source_literal);
181 let arg = parse_arg(&arg, ctx, &mut warnings, span, parser.is_source_literal);
182 Piece::Arg(arg)
183 }
184 })
185 .collect();
186
187 Ok(FormatString { input, pieces, span, warnings })
188 }
189
190 pub fn format(&self, args: &FormatArgs<'_>) -> String {
191 let mut ret = String::new();
192 for piece in &self.pieces {
193 match piece {
194 Piece::Lit(s) | Piece::Arg(FormatArg::AsIs(s)) => ret.push_str(&s),
195
196 Piece::Arg(FormatArg::GenericParam { generic_param }) => {
198 let value = match args.generic_args.iter().find(|(p, _)| p == generic_param) {
200 Some((_, val)) => val.to_string(),
201 None => generic_param.to_string(),
202 };
203 ret.push_str(&value);
204 }
205 Piece::Arg(FormatArg::SelfUpper) => {
207 let slf = match args.generic_args.iter().find(|(p, _)| *p == kw::SelfUpper) {
208 Some((_, val)) => val.to_string(),
209 None => "Self".to_string(),
210 };
211 ret.push_str(&slf);
212 }
213
214 Piece::Arg(FormatArg::This) => ret.push_str(&args.this),
216 Piece::Arg(FormatArg::Trait) => {
217 let _ = fmt::write(&mut ret, format_args!("{0}", &args.trait_sugared)format_args!("{}", &args.trait_sugared));
218 }
219 Piece::Arg(FormatArg::ItemContext) => ret.push_str(args.item_context),
220 }
221 }
222 ret
223 }
224}
225
226fn parse_arg<'tcx>(
227 arg: &Argument<'_>,
228 ctx: &Ctx<'tcx>,
229 warnings: &mut Vec<FormatWarning>,
230 input_span: Span,
231 is_source_literal: bool,
232) -> FormatArg {
233 let (Ctx::RustcOnUnimplemented { tcx, trait_def_id }
234 | Ctx::DiagnosticOnUnimplemented { tcx, trait_def_id }) = *ctx;
235
236 let span = slice_span(input_span, arg.position_span.clone(), is_source_literal);
237
238 match arg.position {
239 Position::ArgumentNamed(name) => match (ctx, Symbol::intern(name)) {
241 (Ctx::RustcOnUnimplemented { .. }, sym::ItemContext) => FormatArg::ItemContext,
243 (Ctx::RustcOnUnimplemented { .. }, sym::This) => FormatArg::This,
244 (Ctx::RustcOnUnimplemented { .. }, sym::Trait) => FormatArg::Trait,
245 (
247 Ctx::RustcOnUnimplemented { .. } | Ctx::DiagnosticOnUnimplemented { .. },
248 kw::SelfUpper,
249 ) => FormatArg::SelfUpper,
250 (
251 Ctx::RustcOnUnimplemented { .. } | Ctx::DiagnosticOnUnimplemented { .. },
252 generic_param,
253 ) if tcx.generics_of(trait_def_id).own_params.iter().any(|param| {
254 !#[allow(non_exhaustive_omitted_patterns)] match param.kind {
GenericParamDefKind::Lifetime => true,
_ => false,
}matches!(param.kind, GenericParamDefKind::Lifetime) && param.name == generic_param
255 }) =>
256 {
257 FormatArg::GenericParam { generic_param }
258 }
259
260 (_, argument_name) => {
261 warnings.push(FormatWarning::UnknownParam { argument_name, span });
262 FormatArg::AsIs(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{{{0}}}", argument_name.as_str()))
})format!("{{{}}}", argument_name.as_str()))
263 }
264 },
265
266 Position::ArgumentIs(idx) => {
268 warnings.push(FormatWarning::PositionalArgument {
269 span,
270 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"),
271 });
272 FormatArg::AsIs(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("{{{0}}}", idx))
})format!("{{{idx}}}"))
273 }
274 Position::ArgumentImplicitlyIs(_) => {
275 warnings.push(FormatWarning::PositionalArgument {
276 span,
277 help: String::from("use `{{}}` to print empty braces"),
278 });
279 FormatArg::AsIs(String::from("{}"))
280 }
281 }
282}
283
284fn warn_on_format_spec(
287 spec: &FormatSpec<'_>,
288 warnings: &mut Vec<FormatWarning>,
289 input_span: Span,
290 is_source_literal: bool,
291) {
292 if spec.ty != "" {
293 let span = spec
294 .ty_span
295 .as_ref()
296 .map(|inner| slice_span(input_span, inner.clone(), is_source_literal))
297 .unwrap_or(input_span);
298 warnings.push(FormatWarning::InvalidSpecifier { span, name: spec.ty.into() })
299 }
300}
301
302fn slice_span(input: Span, Range { start, end }: Range<usize>, is_source_literal: bool) -> Span {
303 if is_source_literal { input.from_inner(InnerSpan { start, end }) } else { input }
304}
305
306pub mod errors {
307 use rustc_macros::LintDiagnostic;
308 use rustc_span::Ident;
309
310 use super::*;
311
312 #[derive(const _: () =
{
impl<'__a> rustc_errors::LintDiagnostic<'__a, ()> for
UnknownFormatParameterForOnUnimplementedAttr {
#[track_caller]
fn decorate_lint<'__b>(self,
diag: &'__b mut rustc_errors::Diag<'__a, ()>) {
match self {
UnknownFormatParameterForOnUnimplementedAttr {
argument_name: __binding_0, trait_name: __binding_1 } => {
diag.primary_message(rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("there is no parameter `{$argument_name}` on trait `{$trait_name}`")));
diag.help(rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("expect either a generic argument name or {\"`{Self}`\"} as format argument")));
;
diag.arg("argument_name", __binding_0);
diag.arg("trait_name", __binding_1);
diag
}
};
}
}
};LintDiagnostic)]
313 #[diag("there is no parameter `{$argument_name}` on trait `{$trait_name}`")]
314 #[help("expect either a generic argument name or {\"`{Self}`\"} as format argument")]
315 pub struct UnknownFormatParameterForOnUnimplementedAttr {
316 pub argument_name: Symbol,
317 pub trait_name: Ident,
318 }
319
320 #[derive(const _: () =
{
impl<'__a> rustc_errors::LintDiagnostic<'__a, ()> for
DisallowedPositionalArgument {
#[track_caller]
fn decorate_lint<'__b>(self,
diag: &'__b mut rustc_errors::Diag<'__a, ()>) {
match self {
DisallowedPositionalArgument => {
diag.primary_message(rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("positional format arguments are not allowed here")));
diag.help(rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("only named format arguments with the name of one of the generic types are allowed in this context")));
;
diag
}
};
}
}
};LintDiagnostic)]
321 #[diag("positional format arguments are not allowed here")]
322 #[help(
323 "only named format arguments with the name of one of the generic types are allowed in this context"
324 )]
325 pub struct DisallowedPositionalArgument;
326
327 #[derive(const _: () =
{
impl<'__a> rustc_errors::LintDiagnostic<'__a, ()> for
InvalidFormatSpecifier {
#[track_caller]
fn decorate_lint<'__b>(self,
diag: &'__b mut rustc_errors::Diag<'__a, ()>) {
match self {
InvalidFormatSpecifier => {
diag.primary_message(rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("invalid format specifier")));
diag.help(rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("no format specifier are supported in this position")));
;
diag
}
};
}
}
};LintDiagnostic)]
328 #[diag("invalid format specifier")]
329 #[help("no format specifier are supported in this position")]
330 pub struct InvalidFormatSpecifier;
331
332 #[derive(const _: () =
{
impl<'__a> rustc_errors::LintDiagnostic<'__a, ()> for
MissingOptionsForOnUnimplementedAttr {
#[track_caller]
fn decorate_lint<'__b>(self,
diag: &'__b mut rustc_errors::Diag<'__a, ()>) {
match self {
MissingOptionsForOnUnimplementedAttr => {
diag.primary_message(rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("missing options for `on_unimplemented` attribute")));
diag.help(rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed("at least one of the `message`, `note` and `label` options are expected")));
;
diag
}
};
}
}
};LintDiagnostic)]
333 #[diag("missing options for `on_unimplemented` attribute")]
334 #[help("at least one of the `message`, `note` and `label` options are expected")]
335 pub struct MissingOptionsForOnUnimplementedAttr;
336}