rustc_errors/
diagnostic_impls.rs

1use std::backtrace::Backtrace;
2use std::borrow::Cow;
3use std::fmt;
4use std::num::ParseIntError;
5use std::path::{Path, PathBuf};
6use std::process::ExitStatus;
7
8use rustc_abi::TargetDataLayoutErrors;
9use rustc_ast::util::parser::ExprPrecedence;
10use rustc_ast_pretty::pprust;
11use rustc_hir::RustcVersion;
12use rustc_hir::attrs::{MirDialect, MirPhase};
13use rustc_macros::Subdiagnostic;
14use rustc_span::edition::Edition;
15use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol};
16use rustc_target::spec::{PanicStrategy, SplitDebuginfo, StackProtector, TargetTuple};
17use rustc_type_ir::{ClosureKind, FloatTy};
18use {rustc_ast as ast, rustc_hir as hir};
19
20use crate::diagnostic::DiagLocation;
21use crate::{
22    Diag, DiagArgValue, DiagCtxtHandle, Diagnostic, EmissionGuarantee, ErrCode, IntoDiagArg, Level,
23    Subdiagnostic, fluent_generated as fluent,
24};
25
26pub struct DiagArgFromDisplay<'a>(pub &'a dyn fmt::Display);
27
28impl IntoDiagArg for DiagArgFromDisplay<'_> {
29    fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> DiagArgValue {
30        self.0.to_string().into_diag_arg(path)
31    }
32}
33
34impl<'a> From<&'a dyn fmt::Display> for DiagArgFromDisplay<'a> {
35    fn from(t: &'a dyn fmt::Display) -> Self {
36        DiagArgFromDisplay(t)
37    }
38}
39
40impl<'a, T: fmt::Display> From<&'a T> for DiagArgFromDisplay<'a> {
41    fn from(t: &'a T) -> Self {
42        DiagArgFromDisplay(t)
43    }
44}
45
46impl<'a, T: Clone + IntoDiagArg> IntoDiagArg for &'a T {
47    fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> DiagArgValue {
48        self.clone().into_diag_arg(path)
49    }
50}
51
52#[macro_export]
53macro_rules! into_diag_arg_using_display {
54    ($( $ty:ty ),+ $(,)?) => {
55        $(
56            impl IntoDiagArg for $ty {
57                fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> DiagArgValue {
58                    self.to_string().into_diag_arg(path)
59                }
60            }
61        )+
62    }
63}
64
65macro_rules! into_diag_arg_for_number {
66    ($( $ty:ty ),+ $(,)?) => {
67        $(
68            impl IntoDiagArg for $ty {
69                fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> DiagArgValue {
70                    // Convert to a string if it won't fit into `Number`.
71                    #[allow(irrefutable_let_patterns)]
72                    if let Ok(n) = TryInto::<i32>::try_into(self) {
73                        DiagArgValue::Number(n)
74                    } else {
75                        self.to_string().into_diag_arg(path)
76                    }
77                }
78            }
79        )+
80    }
81}
82
83into_diag_arg_using_display!(
84    ast::ParamKindOrd,
85    std::io::Error,
86    Box<dyn std::error::Error>,
87    std::num::NonZero<u32>,
88    hir::Target,
89    Edition,
90    Ident,
91    MacroRulesNormalizedIdent,
92    ParseIntError,
93    StackProtector,
94    &TargetTuple,
95    SplitDebuginfo,
96    ExitStatus,
97    ErrCode,
98    rustc_abi::ExternAbi,
99);
100
101impl IntoDiagArg for RustcVersion {
102    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
103        DiagArgValue::Str(Cow::Owned(self.to_string()))
104    }
105}
106
107impl<I: rustc_type_ir::Interner> IntoDiagArg for rustc_type_ir::TraitRef<I> {
108    fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> DiagArgValue {
109        self.to_string().into_diag_arg(path)
110    }
111}
112
113impl<I: rustc_type_ir::Interner> IntoDiagArg for rustc_type_ir::ExistentialTraitRef<I> {
114    fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> DiagArgValue {
115        self.to_string().into_diag_arg(path)
116    }
117}
118
119impl<I: rustc_type_ir::Interner> IntoDiagArg for rustc_type_ir::UnevaluatedConst<I> {
120    fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> DiagArgValue {
121        format!("{self:?}").into_diag_arg(path)
122    }
123}
124
125impl<I: rustc_type_ir::Interner> IntoDiagArg for rustc_type_ir::FnSig<I> {
126    fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> DiagArgValue {
127        format!("{self:?}").into_diag_arg(path)
128    }
129}
130
131impl<I: rustc_type_ir::Interner, T> IntoDiagArg for rustc_type_ir::Binder<I, T>
132where
133    T: IntoDiagArg,
134{
135    fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> DiagArgValue {
136        self.skip_binder().into_diag_arg(path)
137    }
138}
139
140into_diag_arg_for_number!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize);
141
142impl IntoDiagArg for bool {
143    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
144        if self {
145            DiagArgValue::Str(Cow::Borrowed("true"))
146        } else {
147            DiagArgValue::Str(Cow::Borrowed("false"))
148        }
149    }
150}
151
152impl IntoDiagArg for char {
153    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
154        DiagArgValue::Str(Cow::Owned(format!("{self:?}")))
155    }
156}
157
158impl IntoDiagArg for Vec<char> {
159    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
160        DiagArgValue::StrListSepByAnd(
161            self.into_iter().map(|c| Cow::Owned(format!("{c:?}"))).collect(),
162        )
163    }
164}
165
166impl IntoDiagArg for Symbol {
167    fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> DiagArgValue {
168        self.to_ident_string().into_diag_arg(path)
169    }
170}
171
172impl<'a> IntoDiagArg for &'a str {
173    fn into_diag_arg(self, path: &mut Option<std::path::PathBuf>) -> DiagArgValue {
174        self.to_string().into_diag_arg(path)
175    }
176}
177
178impl IntoDiagArg for String {
179    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
180        DiagArgValue::Str(Cow::Owned(self))
181    }
182}
183
184impl<'a> IntoDiagArg for Cow<'a, str> {
185    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
186        DiagArgValue::Str(Cow::Owned(self.into_owned()))
187    }
188}
189
190impl<'a> IntoDiagArg for &'a Path {
191    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
192        DiagArgValue::Str(Cow::Owned(self.display().to_string()))
193    }
194}
195
196impl IntoDiagArg for PathBuf {
197    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
198        DiagArgValue::Str(Cow::Owned(self.display().to_string()))
199    }
200}
201
202impl IntoDiagArg for PanicStrategy {
203    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
204        DiagArgValue::Str(Cow::Owned(self.desc().to_string()))
205    }
206}
207
208impl IntoDiagArg for hir::ConstContext {
209    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
210        DiagArgValue::Str(Cow::Borrowed(match self {
211            hir::ConstContext::ConstFn => "const_fn",
212            hir::ConstContext::Static(_) => "static",
213            hir::ConstContext::Const { .. } => "const",
214        }))
215    }
216}
217
218impl IntoDiagArg for ast::Expr {
219    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
220        DiagArgValue::Str(Cow::Owned(pprust::expr_to_string(&self)))
221    }
222}
223
224impl IntoDiagArg for ast::Path {
225    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
226        DiagArgValue::Str(Cow::Owned(pprust::path_to_string(&self)))
227    }
228}
229
230impl IntoDiagArg for ast::token::Token {
231    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
232        DiagArgValue::Str(pprust::token_to_string(&self))
233    }
234}
235
236impl IntoDiagArg for ast::token::TokenKind {
237    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
238        DiagArgValue::Str(pprust::token_kind_to_string(&self))
239    }
240}
241
242impl IntoDiagArg for FloatTy {
243    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
244        DiagArgValue::Str(Cow::Borrowed(self.name_str()))
245    }
246}
247
248impl IntoDiagArg for std::ffi::CString {
249    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
250        DiagArgValue::Str(Cow::Owned(self.to_string_lossy().into_owned()))
251    }
252}
253
254impl IntoDiagArg for rustc_data_structures::small_c_str::SmallCStr {
255    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
256        DiagArgValue::Str(Cow::Owned(self.to_string_lossy().into_owned()))
257    }
258}
259
260impl IntoDiagArg for ast::Visibility {
261    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
262        let s = pprust::vis_to_string(&self);
263        let s = s.trim_end().to_string();
264        DiagArgValue::Str(Cow::Owned(s))
265    }
266}
267
268impl IntoDiagArg for rustc_lint_defs::Level {
269    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
270        DiagArgValue::Str(Cow::Borrowed(self.to_cmd_flag()))
271    }
272}
273
274impl<Id> IntoDiagArg for hir::def::Res<Id> {
275    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
276        DiagArgValue::Str(Cow::Borrowed(self.descr()))
277    }
278}
279
280impl IntoDiagArg for DiagLocation {
281    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
282        DiagArgValue::Str(Cow::from(self.to_string()))
283    }
284}
285
286impl IntoDiagArg for Backtrace {
287    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
288        DiagArgValue::Str(Cow::from(self.to_string()))
289    }
290}
291
292impl IntoDiagArg for Level {
293    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
294        DiagArgValue::Str(Cow::from(self.to_string()))
295    }
296}
297
298impl IntoDiagArg for ClosureKind {
299    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
300        DiagArgValue::Str(self.as_str().into())
301    }
302}
303
304impl IntoDiagArg for hir::def::Namespace {
305    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
306        DiagArgValue::Str(Cow::Borrowed(self.descr()))
307    }
308}
309
310impl IntoDiagArg for ExprPrecedence {
311    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
312        DiagArgValue::Number(self as i32)
313    }
314}
315
316impl IntoDiagArg for MirDialect {
317    fn into_diag_arg(self, _path: &mut Option<PathBuf>) -> DiagArgValue {
318        let arg = match self {
319            MirDialect::Analysis => "analysis",
320            MirDialect::Built => "built",
321            MirDialect::Runtime => "runtime",
322        };
323        DiagArgValue::Str(Cow::Borrowed(arg))
324    }
325}
326
327impl IntoDiagArg for MirPhase {
328    fn into_diag_arg(self, _path: &mut Option<PathBuf>) -> DiagArgValue {
329        let arg = match self {
330            MirPhase::Initial => "initial",
331            MirPhase::PostCleanup => "post-cleanup",
332            MirPhase::Optimized => "optimized",
333        };
334        DiagArgValue::Str(Cow::Borrowed(arg))
335    }
336}
337
338#[derive(Clone)]
339pub struct DiagSymbolList<S = Symbol>(Vec<S>);
340
341impl<S> From<Vec<S>> for DiagSymbolList<S> {
342    fn from(v: Vec<S>) -> Self {
343        DiagSymbolList(v)
344    }
345}
346
347impl<S> FromIterator<S> for DiagSymbolList<S> {
348    fn from_iter<T: IntoIterator<Item = S>>(iter: T) -> Self {
349        iter.into_iter().collect::<Vec<_>>().into()
350    }
351}
352
353impl<S: std::fmt::Display> IntoDiagArg for DiagSymbolList<S> {
354    fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
355        DiagArgValue::StrListSepByAnd(
356            self.0.into_iter().map(|sym| Cow::Owned(format!("`{sym}`"))).collect(),
357        )
358    }
359}
360
361impl<G: EmissionGuarantee> Diagnostic<'_, G> for TargetDataLayoutErrors<'_> {
362    fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
363        match self {
364            TargetDataLayoutErrors::InvalidAddressSpace { addr_space, err, cause } => {
365                Diag::new(dcx, level, fluent::errors_target_invalid_address_space)
366                    .with_arg("addr_space", addr_space)
367                    .with_arg("cause", cause)
368                    .with_arg("err", err)
369            }
370            TargetDataLayoutErrors::InvalidBits { kind, bit, cause, err } => {
371                Diag::new(dcx, level, fluent::errors_target_invalid_bits)
372                    .with_arg("kind", kind)
373                    .with_arg("bit", bit)
374                    .with_arg("cause", cause)
375                    .with_arg("err", err)
376            }
377            TargetDataLayoutErrors::MissingAlignment { cause } => {
378                Diag::new(dcx, level, fluent::errors_target_missing_alignment)
379                    .with_arg("cause", cause)
380            }
381            TargetDataLayoutErrors::InvalidAlignment { cause, err } => {
382                Diag::new(dcx, level, fluent::errors_target_invalid_alignment)
383                    .with_arg("cause", cause)
384                    .with_arg("err_kind", err.diag_ident())
385                    .with_arg("align", err.align())
386            }
387            TargetDataLayoutErrors::InconsistentTargetArchitecture { dl, target } => {
388                Diag::new(dcx, level, fluent::errors_target_inconsistent_architecture)
389                    .with_arg("dl", dl)
390                    .with_arg("target", target)
391            }
392            TargetDataLayoutErrors::InconsistentTargetPointerWidth { pointer_size, target } => {
393                Diag::new(dcx, level, fluent::errors_target_inconsistent_pointer_width)
394                    .with_arg("pointer_size", pointer_size)
395                    .with_arg("target", target)
396            }
397            TargetDataLayoutErrors::InvalidBitsSize { err } => {
398                Diag::new(dcx, level, fluent::errors_target_invalid_bits_size).with_arg("err", err)
399            }
400            TargetDataLayoutErrors::UnknownPointerSpecification { err } => {
401                Diag::new(dcx, level, fluent::errors_target_invalid_datalayout_pointer_spec)
402                    .with_arg("err", err)
403            }
404        }
405    }
406}
407
408/// Utility struct used to apply a single label while highlighting multiple spans
409pub struct SingleLabelManySpans {
410    pub spans: Vec<Span>,
411    pub label: &'static str,
412}
413impl Subdiagnostic for SingleLabelManySpans {
414    fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
415        diag.span_labels(self.spans, self.label);
416    }
417}
418
419#[derive(Subdiagnostic)]
420#[label(errors_expected_lifetime_parameter)]
421pub struct ExpectedLifetimeParameter {
422    #[primary_span]
423    pub span: Span,
424    pub count: usize,
425}
426
427#[derive(Subdiagnostic)]
428#[suggestion(errors_indicate_anonymous_lifetime, code = "{suggestion}", style = "verbose")]
429pub struct IndicateAnonymousLifetime {
430    #[primary_span]
431    pub span: Span,
432    pub count: usize,
433    pub suggestion: String,
434}
435
436#[derive(Subdiagnostic)]
437pub struct ElidedLifetimeInPathSubdiag {
438    #[subdiagnostic]
439    pub expected: ExpectedLifetimeParameter,
440    #[subdiagnostic]
441    pub indicate: Option<IndicateAnonymousLifetime>,
442}