rustc_trait_selection/error_reporting/traits/
on_unimplemented_format.rs1use 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 Alignment, Argument, Count, FormatSpec, ParseError, ParseMode, Parser, Piece as RpfPiece,
9 Position,
10};
11use rustc_session::lint::builtin::UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES;
12use rustc_span::def_id::DefId;
13use rustc_span::{BytePos, Pos, Span, Symbol, kw, sym};
14
15#[derive(Debug)]
18pub struct FormatString {
19 #[allow(dead_code, reason = "Debug impl")]
20 input: Symbol,
21 span: Span,
22 pieces: Vec<Piece>,
23 pub warnings: Vec<FormatWarning>,
25}
26
27#[derive(Debug)]
28enum Piece {
29 Lit(String),
30 Arg(FormatArg),
31}
32
33#[derive(Debug)]
34enum FormatArg {
35 GenericParam {
37 generic_param: Symbol,
38 },
39 SelfUpper,
41 This,
43 Trait,
45 ItemContext,
47 AsIs(String),
49}
50
51pub enum Ctx<'tcx> {
52 RustcOnUnimplemented { tcx: TyCtxt<'tcx>, trait_def_id: DefId },
54 DiagnosticOnUnimplemented { tcx: TyCtxt<'tcx>, trait_def_id: DefId },
56}
57
58#[derive(Debug)]
59pub enum FormatWarning {
60 UnknownParam { argument_name: Symbol, span: Span },
61 PositionalArgument { span: Span, help: String },
62 InvalidSpecifier { name: String, span: Span },
63 FutureIncompat { span: Span, help: String },
64}
65
66impl FormatWarning {
67 pub fn emit_warning<'tcx>(&self, tcx: TyCtxt<'tcx>, item_def_id: DefId) {
68 match *self {
69 FormatWarning::UnknownParam { argument_name, span } => {
70 let this = tcx.item_ident(item_def_id);
71 if let Some(item_def_id) = item_def_id.as_local() {
72 tcx.emit_node_span_lint(
73 UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
74 tcx.local_def_id_to_hir_id(item_def_id),
75 span,
76 UnknownFormatParameterForOnUnimplementedAttr {
77 argument_name,
78 trait_name: this,
79 },
80 );
81 }
82 }
83 FormatWarning::PositionalArgument { span, .. } => {
84 if let Some(item_def_id) = item_def_id.as_local() {
85 tcx.emit_node_span_lint(
86 UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
87 tcx.local_def_id_to_hir_id(item_def_id),
88 span,
89 DisallowedPositionalArgument,
90 );
91 }
92 }
93 FormatWarning::InvalidSpecifier { span, .. } => {
94 if let Some(item_def_id) = item_def_id.as_local() {
95 tcx.emit_node_span_lint(
96 UNKNOWN_OR_MALFORMED_DIAGNOSTIC_ATTRIBUTES,
97 tcx.local_def_id_to_hir_id(item_def_id),
98 span,
99 InvalidFormatSpecifier,
100 );
101 }
102 }
103 FormatWarning::FutureIncompat { .. } => {
104 }
110 }
111 }
112}
113
114#[derive(Debug)]
149pub struct FormatArgs<'tcx> {
150 pub this: String,
151 pub trait_sugared: TraitRefPrintSugared<'tcx>,
152 pub item_context: &'static str,
153 pub generic_args: Vec<(Symbol, String)>,
154}
155
156impl FormatString {
157 pub fn span(&self) -> Span {
158 self.span
159 }
160
161 pub fn parse<'tcx>(
162 input: Symbol,
163 span: Span,
164 ctx: &Ctx<'tcx>,
165 ) -> Result<Self, Vec<ParseError>> {
166 let s = input.as_str();
167 let mut parser = Parser::new(s, None, None, false, ParseMode::Format);
168 let mut pieces = Vec::new();
169 let mut warnings = Vec::new();
170
171 for piece in &mut parser {
172 match piece {
173 RpfPiece::Lit(lit) => {
174 pieces.push(Piece::Lit(lit.into()));
175 }
176 RpfPiece::NextArgument(arg) => {
177 warn_on_format_spec(arg.format.clone(), &mut warnings, span);
178 let arg = parse_arg(&arg, ctx, &mut warnings, span);
179 pieces.push(Piece::Arg(arg));
180 }
181 }
182 }
183
184 if parser.errors.is_empty() {
185 Ok(FormatString { input, pieces, span, warnings })
186 } else {
187 Err(parser.errors)
188 }
189 }
190
191 pub fn format(&self, args: &FormatArgs<'_>) -> String {
192 let mut ret = String::new();
193 for piece in &self.pieces {
194 match piece {
195 Piece::Lit(s) | Piece::Arg(FormatArg::AsIs(s)) => ret.push_str(&s),
196
197 Piece::Arg(FormatArg::GenericParam { generic_param }) => {
199 let value = match args.generic_args.iter().find(|(p, _)| p == generic_param) {
201 Some((_, val)) => val.to_string(),
202 None => generic_param.to_string(),
203 };
204 ret.push_str(&value);
205 }
206 Piece::Arg(FormatArg::SelfUpper) => {
208 let slf = match args.generic_args.iter().find(|(p, _)| *p == kw::SelfUpper) {
209 Some((_, val)) => val.to_string(),
210 None => "Self".to_string(),
211 };
212 ret.push_str(&slf);
213 }
214
215 Piece::Arg(FormatArg::This) => ret.push_str(&args.this),
217 Piece::Arg(FormatArg::Trait) => {
218 let _ = fmt::write(&mut ret, format_args!("{}", &args.trait_sugared));
219 }
220 Piece::Arg(FormatArg::ItemContext) => ret.push_str(args.item_context),
221 }
222 }
223 ret
224 }
225}
226
227fn parse_arg<'tcx>(
228 arg: &Argument<'_>,
229 ctx: &Ctx<'tcx>,
230 warnings: &mut Vec<FormatWarning>,
231 input_span: Span,
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());
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 !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(format!("{{{}}}", argument_name.as_str()))
263 }
264 },
265
266 Position::ArgumentIs(idx) => {
268 warnings.push(FormatWarning::PositionalArgument {
269 span,
270 help: format!("use `{{{idx}}}` to print a number in braces"),
271 });
272 FormatArg::AsIs(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(spec: FormatSpec<'_>, warnings: &mut Vec<FormatWarning>, input_span: Span) {
287 if !matches!(
288 spec,
289 FormatSpec {
290 fill: None,
291 fill_span: None,
292 align: Alignment::AlignUnknown,
293 sign: None,
294 alternate: false,
295 zero_pad: false,
296 debug_hex: None,
297 precision: Count::CountImplied,
298 precision_span: None,
299 width: Count::CountImplied,
300 width_span: None,
301 ty: _,
302 ty_span: _,
303 },
304 ) {
305 let span = spec.ty_span.map(|inner| slice_span(input_span, inner)).unwrap_or(input_span);
306 warnings.push(FormatWarning::InvalidSpecifier { span, name: spec.ty.into() })
307 }
308}
309
310fn slice_span(input: Span, range: Range<usize>) -> Span {
311 let span = input.data();
312
313 Span::new(
314 span.lo + BytePos::from_usize(range.start),
315 span.lo + BytePos::from_usize(range.end),
316 span.ctxt,
317 span.parent,
318 )
319}
320
321pub mod errors {
322 use rustc_macros::LintDiagnostic;
323 use rustc_span::Ident;
324
325 use super::*;
326
327 #[derive(LintDiagnostic)]
328 #[diag(trait_selection_unknown_format_parameter_for_on_unimplemented_attr)]
329 #[help]
330 pub struct UnknownFormatParameterForOnUnimplementedAttr {
331 pub argument_name: Symbol,
332 pub trait_name: Ident,
333 }
334
335 #[derive(LintDiagnostic)]
336 #[diag(trait_selection_disallowed_positional_argument)]
337 #[help]
338 pub struct DisallowedPositionalArgument;
339
340 #[derive(LintDiagnostic)]
341 #[diag(trait_selection_invalid_format_specifier)]
342 #[help]
343 pub struct InvalidFormatSpecifier;
344
345 #[derive(LintDiagnostic)]
346 #[diag(trait_selection_missing_options_for_on_unimplemented_attr)]
347 #[help]
348 pub struct MissingOptionsForOnUnimplementedAttr;
349}