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