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