• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::snippet::Style;
2 use crate::{
3     CodeSuggestion, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee, Level, MultiSpan,
4     SubdiagnosticMessage, Substitution, SubstitutionPart, SuggestionStyle,
5 };
6 use rustc_data_structures::fx::FxHashMap;
7 use rustc_error_messages::fluent_value_from_str_list_sep_by_and;
8 use rustc_error_messages::FluentValue;
9 use rustc_lint_defs::{Applicability, LintExpectationId};
10 use rustc_span::symbol::Symbol;
11 use rustc_span::{Span, DUMMY_SP};
12 use std::borrow::Cow;
13 use std::fmt::{self, Debug};
14 use std::hash::{Hash, Hasher};
15 use std::panic::Location;
16 
17 /// Error type for `Diagnostic`'s `suggestions` field, indicating that
18 /// `.disable_suggestions()` was called on the `Diagnostic`.
19 #[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)]
20 pub struct SuggestionsDisabled;
21 
22 /// Simplified version of `FluentArg` that can implement `Encodable` and `Decodable`. Collection of
23 /// `DiagnosticArg` are converted to `FluentArgs` (consuming the collection) at the start of
24 /// diagnostic emission.
25 pub type DiagnosticArg<'iter, 'source> =
26     (&'iter DiagnosticArgName<'source>, &'iter DiagnosticArgValue<'source>);
27 
28 /// Name of a diagnostic argument.
29 pub type DiagnosticArgName<'source> = Cow<'source, str>;
30 
31 /// Simplified version of `FluentValue` that can implement `Encodable` and `Decodable`. Converted
32 /// to a `FluentValue` by the emitter to be used in diagnostic translation.
33 #[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)]
34 pub enum DiagnosticArgValue<'source> {
35     Str(Cow<'source, str>),
36     Number(i128),
37     StrListSepByAnd(Vec<Cow<'source, str>>),
38 }
39 
40 /// Converts a value of a type into a `DiagnosticArg` (typically a field of an `IntoDiagnostic`
41 /// struct). Implemented as a custom trait rather than `From` so that it is implemented on the type
42 /// being converted rather than on `DiagnosticArgValue`, which enables types from other `rustc_*`
43 /// crates to implement this.
44 pub trait IntoDiagnosticArg {
into_diagnostic_arg(self) -> DiagnosticArgValue<'static>45     fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static>;
46 }
47 
48 impl<'source> IntoDiagnosticArg for DiagnosticArgValue<'source> {
into_diagnostic_arg(self) -> DiagnosticArgValue<'static>49     fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
50         match self {
51             DiagnosticArgValue::Str(s) => DiagnosticArgValue::Str(Cow::Owned(s.into_owned())),
52             DiagnosticArgValue::Number(n) => DiagnosticArgValue::Number(n),
53             DiagnosticArgValue::StrListSepByAnd(l) => DiagnosticArgValue::StrListSepByAnd(
54                 l.into_iter().map(|s| Cow::Owned(s.into_owned())).collect(),
55             ),
56         }
57     }
58 }
59 
60 impl<'source> Into<FluentValue<'source>> for DiagnosticArgValue<'source> {
into(self) -> FluentValue<'source>61     fn into(self) -> FluentValue<'source> {
62         match self {
63             DiagnosticArgValue::Str(s) => From::from(s),
64             DiagnosticArgValue::Number(n) => From::from(n),
65             DiagnosticArgValue::StrListSepByAnd(l) => fluent_value_from_str_list_sep_by_and(l),
66         }
67     }
68 }
69 
70 /// Trait implemented by error types. This should not be implemented manually. Instead, use
71 /// `#[derive(Subdiagnostic)]` -- see [rustc_macros::Subdiagnostic].
72 #[rustc_diagnostic_item = "AddToDiagnostic"]
73 pub trait AddToDiagnostic
74 where
75     Self: Sized,
76 {
77     /// Add a subdiagnostic to an existing diagnostic.
add_to_diagnostic(self, diag: &mut Diagnostic)78     fn add_to_diagnostic(self, diag: &mut Diagnostic) {
79         self.add_to_diagnostic_with(diag, |_, m| m);
80     }
81 
82     /// Add a subdiagnostic to an existing diagnostic where `f` is invoked on every message used
83     /// (to optionally perform eager translation).
add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, f: F) where F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage84     fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, f: F)
85     where
86         F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage;
87 }
88 
89 /// Trait implemented by lint types. This should not be implemented manually. Instead, use
90 /// `#[derive(LintDiagnostic)]` -- see [rustc_macros::LintDiagnostic].
91 #[rustc_diagnostic_item = "DecorateLint"]
92 pub trait DecorateLint<'a, G: EmissionGuarantee> {
93     /// Decorate and emit a lint.
decorate_lint<'b>( self, diag: &'b mut DiagnosticBuilder<'a, G>, ) -> &'b mut DiagnosticBuilder<'a, G>94     fn decorate_lint<'b>(
95         self,
96         diag: &'b mut DiagnosticBuilder<'a, G>,
97     ) -> &'b mut DiagnosticBuilder<'a, G>;
98 
msg(&self) -> DiagnosticMessage99     fn msg(&self) -> DiagnosticMessage;
100 }
101 
102 #[must_use]
103 #[derive(Clone, Debug, Encodable, Decodable)]
104 pub struct Diagnostic {
105     // NOTE(eddyb) this is private to disallow arbitrary after-the-fact changes,
106     // outside of what methods in this crate themselves allow.
107     pub(crate) level: Level,
108 
109     pub message: Vec<(DiagnosticMessage, Style)>,
110     pub code: Option<DiagnosticId>,
111     pub span: MultiSpan,
112     pub children: Vec<SubDiagnostic>,
113     pub suggestions: Result<Vec<CodeSuggestion>, SuggestionsDisabled>,
114     args: FxHashMap<DiagnosticArgName<'static>, DiagnosticArgValue<'static>>,
115 
116     /// This is not used for highlighting or rendering any error message. Rather, it can be used
117     /// as a sort key to sort a buffer of diagnostics. By default, it is the primary span of
118     /// `span` if there is one. Otherwise, it is `DUMMY_SP`.
119     pub sort_span: Span,
120 
121     /// If diagnostic is from Lint, custom hash function ignores notes
122     /// otherwise hash is based on the all the fields
123     pub is_lint: bool,
124 
125     /// With `-Ztrack_diagnostics` enabled,
126     /// we print where in rustc this error was emitted.
127     pub emitted_at: DiagnosticLocation,
128 }
129 
130 #[derive(Clone, Debug, Encodable, Decodable)]
131 pub struct DiagnosticLocation {
132     file: Cow<'static, str>,
133     line: u32,
134     col: u32,
135 }
136 
137 impl DiagnosticLocation {
138     #[track_caller]
caller() -> Self139     fn caller() -> Self {
140         let loc = Location::caller();
141         DiagnosticLocation { file: loc.file().into(), line: loc.line(), col: loc.column() }
142     }
143 }
144 
145 impl fmt::Display for DiagnosticLocation {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result146     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147         write!(f, "{}:{}:{}", self.file, self.line, self.col)
148     }
149 }
150 
151 #[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)]
152 pub enum DiagnosticId {
153     Error(String),
154     Lint { name: String, has_future_breakage: bool, is_force_warn: bool },
155 }
156 
157 /// A "sub"-diagnostic attached to a parent diagnostic.
158 /// For example, a note attached to an error.
159 #[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)]
160 pub struct SubDiagnostic {
161     pub level: Level,
162     pub message: Vec<(DiagnosticMessage, Style)>,
163     pub span: MultiSpan,
164     pub render_span: Option<MultiSpan>,
165 }
166 
167 #[derive(Debug, PartialEq, Eq)]
168 pub struct DiagnosticStyledString(pub Vec<StringPart>);
169 
170 impl DiagnosticStyledString {
new() -> DiagnosticStyledString171     pub fn new() -> DiagnosticStyledString {
172         DiagnosticStyledString(vec![])
173     }
push_normal<S: Into<String>>(&mut self, t: S)174     pub fn push_normal<S: Into<String>>(&mut self, t: S) {
175         self.0.push(StringPart::Normal(t.into()));
176     }
push_highlighted<S: Into<String>>(&mut self, t: S)177     pub fn push_highlighted<S: Into<String>>(&mut self, t: S) {
178         self.0.push(StringPart::Highlighted(t.into()));
179     }
push<S: Into<String>>(&mut self, t: S, highlight: bool)180     pub fn push<S: Into<String>>(&mut self, t: S, highlight: bool) {
181         if highlight {
182             self.push_highlighted(t);
183         } else {
184             self.push_normal(t);
185         }
186     }
normal<S: Into<String>>(t: S) -> DiagnosticStyledString187     pub fn normal<S: Into<String>>(t: S) -> DiagnosticStyledString {
188         DiagnosticStyledString(vec![StringPart::Normal(t.into())])
189     }
190 
highlighted<S: Into<String>>(t: S) -> DiagnosticStyledString191     pub fn highlighted<S: Into<String>>(t: S) -> DiagnosticStyledString {
192         DiagnosticStyledString(vec![StringPart::Highlighted(t.into())])
193     }
194 
content(&self) -> String195     pub fn content(&self) -> String {
196         self.0.iter().map(|x| x.content()).collect::<String>()
197     }
198 }
199 
200 #[derive(Debug, PartialEq, Eq)]
201 pub enum StringPart {
202     Normal(String),
203     Highlighted(String),
204 }
205 
206 impl StringPart {
content(&self) -> &str207     pub fn content(&self) -> &str {
208         match self {
209             &StringPart::Normal(ref s) | &StringPart::Highlighted(ref s) => s,
210         }
211     }
212 }
213 
214 impl Diagnostic {
215     #[track_caller]
new<M: Into<DiagnosticMessage>>(level: Level, message: M) -> Self216     pub fn new<M: Into<DiagnosticMessage>>(level: Level, message: M) -> Self {
217         Diagnostic::new_with_code(level, None, message)
218     }
219 
220     #[track_caller]
new_with_messages(level: Level, messages: Vec<(DiagnosticMessage, Style)>) -> Self221     pub fn new_with_messages(level: Level, messages: Vec<(DiagnosticMessage, Style)>) -> Self {
222         Diagnostic {
223             level,
224             message: messages,
225             code: None,
226             span: MultiSpan::new(),
227             children: vec![],
228             suggestions: Ok(vec![]),
229             args: Default::default(),
230             sort_span: DUMMY_SP,
231             is_lint: false,
232             emitted_at: DiagnosticLocation::caller(),
233         }
234     }
235 
236     #[track_caller]
new_with_code<M: Into<DiagnosticMessage>>( level: Level, code: Option<DiagnosticId>, message: M, ) -> Self237     pub fn new_with_code<M: Into<DiagnosticMessage>>(
238         level: Level,
239         code: Option<DiagnosticId>,
240         message: M,
241     ) -> Self {
242         Diagnostic {
243             level,
244             message: vec![(message.into(), Style::NoStyle)],
245             code,
246             span: MultiSpan::new(),
247             children: vec![],
248             suggestions: Ok(vec![]),
249             args: Default::default(),
250             sort_span: DUMMY_SP,
251             is_lint: false,
252             emitted_at: DiagnosticLocation::caller(),
253         }
254     }
255 
256     #[inline(always)]
level(&self) -> Level257     pub fn level(&self) -> Level {
258         self.level
259     }
260 
is_error(&self) -> bool261     pub fn is_error(&self) -> bool {
262         match self.level {
263             Level::Bug
264             | Level::DelayedBug
265             | Level::Fatal
266             | Level::Error { .. }
267             | Level::FailureNote => true,
268 
269             Level::Warning(_)
270             | Level::Note
271             | Level::OnceNote
272             | Level::Help
273             | Level::Allow
274             | Level::Expect(_) => false,
275         }
276     }
277 
update_unstable_expectation_id( &mut self, unstable_to_stable: &FxHashMap<LintExpectationId, LintExpectationId>, )278     pub fn update_unstable_expectation_id(
279         &mut self,
280         unstable_to_stable: &FxHashMap<LintExpectationId, LintExpectationId>,
281     ) {
282         if let Level::Expect(expectation_id) | Level::Warning(Some(expectation_id)) =
283             &mut self.level
284         {
285             if expectation_id.is_stable() {
286                 return;
287             }
288 
289             // The unstable to stable map only maps the unstable `AttrId` to a stable `HirId` with an attribute index.
290             // The lint index inside the attribute is manually transferred here.
291             let lint_index = expectation_id.get_lint_index();
292             expectation_id.set_lint_index(None);
293             let mut stable_id = unstable_to_stable
294                 .get(expectation_id)
295                 .expect("each unstable `LintExpectationId` must have a matching stable id")
296                 .normalize();
297 
298             stable_id.set_lint_index(lint_index);
299             *expectation_id = stable_id;
300         }
301     }
302 
has_future_breakage(&self) -> bool303     pub fn has_future_breakage(&self) -> bool {
304         match self.code {
305             Some(DiagnosticId::Lint { has_future_breakage, .. }) => has_future_breakage,
306             _ => false,
307         }
308     }
309 
is_force_warn(&self) -> bool310     pub fn is_force_warn(&self) -> bool {
311         match self.code {
312             Some(DiagnosticId::Lint { is_force_warn, .. }) => is_force_warn,
313             _ => false,
314         }
315     }
316 
317     /// Delay emission of this diagnostic as a bug.
318     ///
319     /// This can be useful in contexts where an error indicates a bug but
320     /// typically this only happens when other compilation errors have already
321     /// happened. In those cases this can be used to defer emission of this
322     /// diagnostic as a bug in the compiler only if no other errors have been
323     /// emitted.
324     ///
325     /// In the meantime, though, callsites are required to deal with the "bug"
326     /// locally in whichever way makes the most sense.
327     #[track_caller]
downgrade_to_delayed_bug(&mut self) -> &mut Self328     pub fn downgrade_to_delayed_bug(&mut self) -> &mut Self {
329         assert!(
330             self.is_error(),
331             "downgrade_to_delayed_bug: cannot downgrade {:?} to DelayedBug: not an error",
332             self.level
333         );
334         self.level = Level::DelayedBug;
335 
336         self
337     }
338 
339     /// Adds a span/label to be included in the resulting snippet.
340     ///
341     /// This is pushed onto the [`MultiSpan`] that was created when the diagnostic
342     /// was first built. That means it will be shown together with the original
343     /// span/label, *not* a span added by one of the `span_{note,warn,help,suggestions}` methods.
344     ///
345     /// This span is *not* considered a ["primary span"][`MultiSpan`]; only
346     /// the `Span` supplied when creating the diagnostic is primary.
347     #[rustc_lint_diagnostics]
span_label(&mut self, span: Span, label: impl Into<SubdiagnosticMessage>) -> &mut Self348     pub fn span_label(&mut self, span: Span, label: impl Into<SubdiagnosticMessage>) -> &mut Self {
349         self.span.push_span_label(span, self.subdiagnostic_message_to_diagnostic_message(label));
350         self
351     }
352 
353     /// Labels all the given spans with the provided label.
354     /// See [`Self::span_label()`] for more information.
span_labels(&mut self, spans: impl IntoIterator<Item = Span>, label: &str) -> &mut Self355     pub fn span_labels(&mut self, spans: impl IntoIterator<Item = Span>, label: &str) -> &mut Self {
356         for span in spans {
357             self.span_label(span, label.to_string());
358         }
359         self
360     }
361 
replace_span_with(&mut self, after: Span, keep_label: bool) -> &mut Self362     pub fn replace_span_with(&mut self, after: Span, keep_label: bool) -> &mut Self {
363         let before = self.span.clone();
364         self.set_span(after);
365         for span_label in before.span_labels() {
366             if let Some(label) = span_label.label {
367                 if span_label.is_primary && keep_label {
368                     self.span.push_span_label(after, label);
369                 } else {
370                     self.span.push_span_label(span_label.span, label);
371                 }
372             }
373         }
374         self
375     }
376 
note_expected_found( &mut self, expected_label: &dyn fmt::Display, expected: DiagnosticStyledString, found_label: &dyn fmt::Display, found: DiagnosticStyledString, ) -> &mut Self377     pub fn note_expected_found(
378         &mut self,
379         expected_label: &dyn fmt::Display,
380         expected: DiagnosticStyledString,
381         found_label: &dyn fmt::Display,
382         found: DiagnosticStyledString,
383     ) -> &mut Self {
384         self.note_expected_found_extra(expected_label, expected, found_label, found, &"", &"")
385     }
386 
note_unsuccessful_coercion( &mut self, expected: DiagnosticStyledString, found: DiagnosticStyledString, ) -> &mut Self387     pub fn note_unsuccessful_coercion(
388         &mut self,
389         expected: DiagnosticStyledString,
390         found: DiagnosticStyledString,
391     ) -> &mut Self {
392         let mut msg: Vec<_> =
393             vec![(Cow::from("required when trying to coerce from type `"), Style::NoStyle)];
394         msg.extend(expected.0.iter().map(|x| match *x {
395             StringPart::Normal(ref s) => (Cow::from(s.clone()), Style::NoStyle),
396             StringPart::Highlighted(ref s) => (Cow::from(s.clone()), Style::Highlight),
397         }));
398         msg.push((Cow::from("` to type '"), Style::NoStyle));
399         msg.extend(found.0.iter().map(|x| match *x {
400             StringPart::Normal(ref s) => (Cow::from(s.clone()), Style::NoStyle),
401             StringPart::Highlighted(ref s) => (Cow::from(s.clone()), Style::Highlight),
402         }));
403         msg.push((Cow::from("`"), Style::NoStyle));
404 
405         // For now, just attach these as notes
406         self.highlighted_note(msg);
407         self
408     }
409 
note_expected_found_extra( &mut self, expected_label: &dyn fmt::Display, expected: DiagnosticStyledString, found_label: &dyn fmt::Display, found: DiagnosticStyledString, expected_extra: &dyn fmt::Display, found_extra: &dyn fmt::Display, ) -> &mut Self410     pub fn note_expected_found_extra(
411         &mut self,
412         expected_label: &dyn fmt::Display,
413         expected: DiagnosticStyledString,
414         found_label: &dyn fmt::Display,
415         found: DiagnosticStyledString,
416         expected_extra: &dyn fmt::Display,
417         found_extra: &dyn fmt::Display,
418     ) -> &mut Self {
419         let expected_label = expected_label.to_string();
420         let expected_label = if expected_label.is_empty() {
421             "expected".to_string()
422         } else {
423             format!("expected {}", expected_label)
424         };
425         let found_label = found_label.to_string();
426         let found_label = if found_label.is_empty() {
427             "found".to_string()
428         } else {
429             format!("found {}", found_label)
430         };
431         let (found_padding, expected_padding) = if expected_label.len() > found_label.len() {
432             (expected_label.len() - found_label.len(), 0)
433         } else {
434             (0, found_label.len() - expected_label.len())
435         };
436         let mut msg: Vec<_> =
437             vec![(format!("{}{} `", " ".repeat(expected_padding), expected_label), Style::NoStyle)];
438         msg.extend(expected.0.iter().map(|x| match *x {
439             StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
440             StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
441         }));
442         msg.push((format!("`{}\n", expected_extra), Style::NoStyle));
443         msg.push((format!("{}{} `", " ".repeat(found_padding), found_label), Style::NoStyle));
444         msg.extend(found.0.iter().map(|x| match *x {
445             StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
446             StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
447         }));
448         msg.push((format!("`{}", found_extra), Style::NoStyle));
449 
450         // For now, just attach these as notes.
451         self.highlighted_note(msg);
452         self
453     }
454 
note_trait_signature(&mut self, name: Symbol, signature: String) -> &mut Self455     pub fn note_trait_signature(&mut self, name: Symbol, signature: String) -> &mut Self {
456         self.highlighted_note(vec![
457             (format!("`{}` from trait: `", name), Style::NoStyle),
458             (signature, Style::Highlight),
459             ("`".to_string(), Style::NoStyle),
460         ]);
461         self
462     }
463 
464     /// Add a note attached to this diagnostic.
465     #[rustc_lint_diagnostics]
note(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self466     pub fn note(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self {
467         self.sub(Level::Note, msg, MultiSpan::new(), None);
468         self
469     }
470 
highlighted_note<M: Into<SubdiagnosticMessage>>( &mut self, msg: Vec<(M, Style)>, ) -> &mut Self471     pub fn highlighted_note<M: Into<SubdiagnosticMessage>>(
472         &mut self,
473         msg: Vec<(M, Style)>,
474     ) -> &mut Self {
475         self.sub_with_highlights(Level::Note, msg, MultiSpan::new(), None);
476         self
477     }
478 
479     /// Prints the span with a note above it.
480     /// This is like [`Diagnostic::note()`], but it gets its own span.
note_once(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self481     pub fn note_once(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self {
482         self.sub(Level::OnceNote, msg, MultiSpan::new(), None);
483         self
484     }
485 
486     /// Prints the span with a note above it.
487     /// This is like [`Diagnostic::note()`], but it gets its own span.
488     #[rustc_lint_diagnostics]
span_note<S: Into<MultiSpan>>( &mut self, sp: S, msg: impl Into<SubdiagnosticMessage>, ) -> &mut Self489     pub fn span_note<S: Into<MultiSpan>>(
490         &mut self,
491         sp: S,
492         msg: impl Into<SubdiagnosticMessage>,
493     ) -> &mut Self {
494         self.sub(Level::Note, msg, sp.into(), None);
495         self
496     }
497 
498     /// Prints the span with a note above it.
499     /// This is like [`Diagnostic::note()`], but it gets its own span.
span_note_once<S: Into<MultiSpan>>( &mut self, sp: S, msg: impl Into<SubdiagnosticMessage>, ) -> &mut Self500     pub fn span_note_once<S: Into<MultiSpan>>(
501         &mut self,
502         sp: S,
503         msg: impl Into<SubdiagnosticMessage>,
504     ) -> &mut Self {
505         self.sub(Level::OnceNote, msg, sp.into(), None);
506         self
507     }
508 
509     /// Add a warning attached to this diagnostic.
510     #[rustc_lint_diagnostics]
warn(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self511     pub fn warn(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self {
512         self.sub(Level::Warning(None), msg, MultiSpan::new(), None);
513         self
514     }
515 
516     /// Prints the span with a warning above it.
517     /// This is like [`Diagnostic::warn()`], but it gets its own span.
518     #[rustc_lint_diagnostics]
span_warn<S: Into<MultiSpan>>( &mut self, sp: S, msg: impl Into<SubdiagnosticMessage>, ) -> &mut Self519     pub fn span_warn<S: Into<MultiSpan>>(
520         &mut self,
521         sp: S,
522         msg: impl Into<SubdiagnosticMessage>,
523     ) -> &mut Self {
524         self.sub(Level::Warning(None), msg, sp.into(), None);
525         self
526     }
527 
528     /// Add a help message attached to this diagnostic.
529     #[rustc_lint_diagnostics]
help(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self530     pub fn help(&mut self, msg: impl Into<SubdiagnosticMessage>) -> &mut Self {
531         self.sub(Level::Help, msg, MultiSpan::new(), None);
532         self
533     }
534 
535     /// Add a help message attached to this diagnostic with a customizable highlighted message.
highlighted_help(&mut self, msg: Vec<(String, Style)>) -> &mut Self536     pub fn highlighted_help(&mut self, msg: Vec<(String, Style)>) -> &mut Self {
537         self.sub_with_highlights(Level::Help, msg, MultiSpan::new(), None);
538         self
539     }
540 
541     /// Prints the span with some help above it.
542     /// This is like [`Diagnostic::help()`], but it gets its own span.
543     #[rustc_lint_diagnostics]
span_help<S: Into<MultiSpan>>( &mut self, sp: S, msg: impl Into<SubdiagnosticMessage>, ) -> &mut Self544     pub fn span_help<S: Into<MultiSpan>>(
545         &mut self,
546         sp: S,
547         msg: impl Into<SubdiagnosticMessage>,
548     ) -> &mut Self {
549         self.sub(Level::Help, msg, sp.into(), None);
550         self
551     }
552 
553     /// Disallow attaching suggestions this diagnostic.
554     /// Any suggestions attached e.g. with the `span_suggestion_*` methods
555     /// (before and after the call to `disable_suggestions`) will be ignored.
disable_suggestions(&mut self) -> &mut Self556     pub fn disable_suggestions(&mut self) -> &mut Self {
557         self.suggestions = Err(SuggestionsDisabled);
558         self
559     }
560 
561     /// Clear any existing suggestions.
clear_suggestions(&mut self) -> &mut Self562     pub fn clear_suggestions(&mut self) -> &mut Self {
563         if let Ok(suggestions) = &mut self.suggestions {
564             suggestions.clear();
565         }
566         self
567     }
568 
569     /// Helper for pushing to `self.suggestions`, if available (not disable).
push_suggestion(&mut self, suggestion: CodeSuggestion)570     fn push_suggestion(&mut self, suggestion: CodeSuggestion) {
571         if let Ok(suggestions) = &mut self.suggestions {
572             suggestions.push(suggestion);
573         }
574     }
575 
576     /// Show a suggestion that has multiple parts to it.
577     /// In other words, multiple changes need to be applied as part of this suggestion.
multipart_suggestion( &mut self, msg: impl Into<SubdiagnosticMessage>, suggestion: Vec<(Span, String)>, applicability: Applicability, ) -> &mut Self578     pub fn multipart_suggestion(
579         &mut self,
580         msg: impl Into<SubdiagnosticMessage>,
581         suggestion: Vec<(Span, String)>,
582         applicability: Applicability,
583     ) -> &mut Self {
584         self.multipart_suggestion_with_style(
585             msg,
586             suggestion,
587             applicability,
588             SuggestionStyle::ShowCode,
589         )
590     }
591 
592     /// Show a suggestion that has multiple parts to it, always as it's own subdiagnostic.
593     /// In other words, multiple changes need to be applied as part of this suggestion.
multipart_suggestion_verbose( &mut self, msg: impl Into<SubdiagnosticMessage>, suggestion: Vec<(Span, String)>, applicability: Applicability, ) -> &mut Self594     pub fn multipart_suggestion_verbose(
595         &mut self,
596         msg: impl Into<SubdiagnosticMessage>,
597         suggestion: Vec<(Span, String)>,
598         applicability: Applicability,
599     ) -> &mut Self {
600         self.multipart_suggestion_with_style(
601             msg,
602             suggestion,
603             applicability,
604             SuggestionStyle::ShowAlways,
605         )
606     }
607     /// [`Diagnostic::multipart_suggestion()`] but you can set the [`SuggestionStyle`].
multipart_suggestion_with_style( &mut self, msg: impl Into<SubdiagnosticMessage>, suggestion: Vec<(Span, String)>, applicability: Applicability, style: SuggestionStyle, ) -> &mut Self608     pub fn multipart_suggestion_with_style(
609         &mut self,
610         msg: impl Into<SubdiagnosticMessage>,
611         suggestion: Vec<(Span, String)>,
612         applicability: Applicability,
613         style: SuggestionStyle,
614     ) -> &mut Self {
615         let mut parts = suggestion
616             .into_iter()
617             .map(|(span, snippet)| SubstitutionPart { snippet, span })
618             .collect::<Vec<_>>();
619 
620         parts.sort_unstable_by_key(|part| part.span);
621 
622         assert!(!parts.is_empty());
623         debug_assert_eq!(
624             parts.iter().find(|part| part.span.is_empty() && part.snippet.is_empty()),
625             None,
626             "Span must not be empty and have no suggestion",
627         );
628         debug_assert_eq!(
629             parts.array_windows().find(|[a, b]| a.span.overlaps(b.span)),
630             None,
631             "suggestion must not have overlapping parts",
632         );
633 
634         self.push_suggestion(CodeSuggestion {
635             substitutions: vec![Substitution { parts }],
636             msg: self.subdiagnostic_message_to_diagnostic_message(msg),
637             style,
638             applicability,
639         });
640         self
641     }
642 
643     /// Prints out a message with for a multipart suggestion without showing the suggested code.
644     ///
645     /// This is intended to be used for suggestions that are obvious in what the changes need to
646     /// be from the message, showing the span label inline would be visually unpleasant
647     /// (marginally overlapping spans or multiline spans) and showing the snippet window wouldn't
648     /// improve understandability.
tool_only_multipart_suggestion( &mut self, msg: impl Into<SubdiagnosticMessage>, suggestion: Vec<(Span, String)>, applicability: Applicability, ) -> &mut Self649     pub fn tool_only_multipart_suggestion(
650         &mut self,
651         msg: impl Into<SubdiagnosticMessage>,
652         suggestion: Vec<(Span, String)>,
653         applicability: Applicability,
654     ) -> &mut Self {
655         self.multipart_suggestion_with_style(
656             msg,
657             suggestion,
658             applicability,
659             SuggestionStyle::CompletelyHidden,
660         )
661     }
662 
663     /// Prints out a message with a suggested edit of the code.
664     ///
665     /// In case of short messages and a simple suggestion, rustc displays it as a label:
666     ///
667     /// ```text
668     /// try adding parentheses: `(tup.0).1`
669     /// ```
670     ///
671     /// The message
672     ///
673     /// * should not end in any punctuation (a `:` is added automatically)
674     /// * should not be a question (avoid language like "did you mean")
675     /// * should not contain any phrases like "the following", "as shown", etc.
676     /// * may look like "to do xyz, use" or "to do xyz, use abc"
677     /// * may contain a name of a function, variable, or type, but not whole expressions
678     ///
679     /// See `CodeSuggestion` for more information.
span_suggestion( &mut self, sp: Span, msg: impl Into<SubdiagnosticMessage>, suggestion: impl ToString, applicability: Applicability, ) -> &mut Self680     pub fn span_suggestion(
681         &mut self,
682         sp: Span,
683         msg: impl Into<SubdiagnosticMessage>,
684         suggestion: impl ToString,
685         applicability: Applicability,
686     ) -> &mut Self {
687         self.span_suggestion_with_style(
688             sp,
689             msg,
690             suggestion,
691             applicability,
692             SuggestionStyle::ShowCode,
693         );
694         self
695     }
696 
697     /// [`Diagnostic::span_suggestion()`] but you can set the [`SuggestionStyle`].
span_suggestion_with_style( &mut self, sp: Span, msg: impl Into<SubdiagnosticMessage>, suggestion: impl ToString, applicability: Applicability, style: SuggestionStyle, ) -> &mut Self698     pub fn span_suggestion_with_style(
699         &mut self,
700         sp: Span,
701         msg: impl Into<SubdiagnosticMessage>,
702         suggestion: impl ToString,
703         applicability: Applicability,
704         style: SuggestionStyle,
705     ) -> &mut Self {
706         debug_assert!(
707             !(sp.is_empty() && suggestion.to_string().is_empty()),
708             "Span must not be empty and have no suggestion"
709         );
710         self.push_suggestion(CodeSuggestion {
711             substitutions: vec![Substitution {
712                 parts: vec![SubstitutionPart { snippet: suggestion.to_string(), span: sp }],
713             }],
714             msg: self.subdiagnostic_message_to_diagnostic_message(msg),
715             style,
716             applicability,
717         });
718         self
719     }
720 
721     /// Always show the suggested change.
span_suggestion_verbose( &mut self, sp: Span, msg: impl Into<SubdiagnosticMessage>, suggestion: impl ToString, applicability: Applicability, ) -> &mut Self722     pub fn span_suggestion_verbose(
723         &mut self,
724         sp: Span,
725         msg: impl Into<SubdiagnosticMessage>,
726         suggestion: impl ToString,
727         applicability: Applicability,
728     ) -> &mut Self {
729         self.span_suggestion_with_style(
730             sp,
731             msg,
732             suggestion,
733             applicability,
734             SuggestionStyle::ShowAlways,
735         );
736         self
737     }
738 
739     /// Prints out a message with multiple suggested edits of the code.
740     /// See also [`Diagnostic::span_suggestion()`].
span_suggestions( &mut self, sp: Span, msg: impl Into<SubdiagnosticMessage>, suggestions: impl IntoIterator<Item = String>, applicability: Applicability, ) -> &mut Self741     pub fn span_suggestions(
742         &mut self,
743         sp: Span,
744         msg: impl Into<SubdiagnosticMessage>,
745         suggestions: impl IntoIterator<Item = String>,
746         applicability: Applicability,
747     ) -> &mut Self {
748         self.span_suggestions_with_style(
749             sp,
750             msg,
751             suggestions,
752             applicability,
753             SuggestionStyle::ShowCode,
754         )
755     }
756 
757     /// [`Diagnostic::span_suggestions()`] but you can set the [`SuggestionStyle`].
span_suggestions_with_style( &mut self, sp: Span, msg: impl Into<SubdiagnosticMessage>, suggestions: impl IntoIterator<Item = String>, applicability: Applicability, style: SuggestionStyle, ) -> &mut Self758     pub fn span_suggestions_with_style(
759         &mut self,
760         sp: Span,
761         msg: impl Into<SubdiagnosticMessage>,
762         suggestions: impl IntoIterator<Item = String>,
763         applicability: Applicability,
764         style: SuggestionStyle,
765     ) -> &mut Self {
766         let mut suggestions: Vec<_> = suggestions.into_iter().collect();
767         suggestions.sort();
768 
769         debug_assert!(
770             !(sp.is_empty() && suggestions.iter().any(|suggestion| suggestion.is_empty())),
771             "Span must not be empty and have no suggestion"
772         );
773 
774         let substitutions = suggestions
775             .into_iter()
776             .map(|snippet| Substitution { parts: vec![SubstitutionPart { snippet, span: sp }] })
777             .collect();
778         self.push_suggestion(CodeSuggestion {
779             substitutions,
780             msg: self.subdiagnostic_message_to_diagnostic_message(msg),
781             style,
782             applicability,
783         });
784         self
785     }
786 
787     /// Prints out a message with multiple suggested edits of the code, where each edit consists of
788     /// multiple parts.
789     /// See also [`Diagnostic::multipart_suggestion()`].
multipart_suggestions( &mut self, msg: impl Into<SubdiagnosticMessage>, suggestions: impl IntoIterator<Item = Vec<(Span, String)>>, applicability: Applicability, ) -> &mut Self790     pub fn multipart_suggestions(
791         &mut self,
792         msg: impl Into<SubdiagnosticMessage>,
793         suggestions: impl IntoIterator<Item = Vec<(Span, String)>>,
794         applicability: Applicability,
795     ) -> &mut Self {
796         let substitutions = suggestions
797             .into_iter()
798             .map(|sugg| {
799                 let mut parts = sugg
800                     .into_iter()
801                     .map(|(span, snippet)| SubstitutionPart { snippet, span })
802                     .collect::<Vec<_>>();
803 
804                 parts.sort_unstable_by_key(|part| part.span);
805 
806                 assert!(!parts.is_empty());
807                 debug_assert_eq!(
808                     parts.iter().find(|part| part.span.is_empty() && part.snippet.is_empty()),
809                     None,
810                     "Span must not be empty and have no suggestion",
811                 );
812                 debug_assert_eq!(
813                     parts.array_windows().find(|[a, b]| a.span.overlaps(b.span)),
814                     None,
815                     "suggestion must not have overlapping parts",
816                 );
817 
818                 Substitution { parts }
819             })
820             .collect();
821 
822         self.push_suggestion(CodeSuggestion {
823             substitutions,
824             msg: self.subdiagnostic_message_to_diagnostic_message(msg),
825             style: SuggestionStyle::ShowCode,
826             applicability,
827         });
828         self
829     }
830 
831     /// Prints out a message with a suggested edit of the code. If the suggestion is presented
832     /// inline, it will only show the message and not the suggestion.
833     ///
834     /// See `CodeSuggestion` for more information.
span_suggestion_short( &mut self, sp: Span, msg: impl Into<SubdiagnosticMessage>, suggestion: impl ToString, applicability: Applicability, ) -> &mut Self835     pub fn span_suggestion_short(
836         &mut self,
837         sp: Span,
838         msg: impl Into<SubdiagnosticMessage>,
839         suggestion: impl ToString,
840         applicability: Applicability,
841     ) -> &mut Self {
842         self.span_suggestion_with_style(
843             sp,
844             msg,
845             suggestion,
846             applicability,
847             SuggestionStyle::HideCodeInline,
848         );
849         self
850     }
851 
852     /// Prints out a message for a suggestion without showing the suggested code.
853     ///
854     /// This is intended to be used for suggestions that are obvious in what the changes need to
855     /// be from the message, showing the span label inline would be visually unpleasant
856     /// (marginally overlapping spans or multiline spans) and showing the snippet window wouldn't
857     /// improve understandability.
span_suggestion_hidden( &mut self, sp: Span, msg: impl Into<SubdiagnosticMessage>, suggestion: impl ToString, applicability: Applicability, ) -> &mut Self858     pub fn span_suggestion_hidden(
859         &mut self,
860         sp: Span,
861         msg: impl Into<SubdiagnosticMessage>,
862         suggestion: impl ToString,
863         applicability: Applicability,
864     ) -> &mut Self {
865         self.span_suggestion_with_style(
866             sp,
867             msg,
868             suggestion,
869             applicability,
870             SuggestionStyle::HideCodeAlways,
871         );
872         self
873     }
874 
875     /// Adds a suggestion to the JSON output that will not be shown in the CLI.
876     ///
877     /// This is intended to be used for suggestions that are *very* obvious in what the changes
878     /// need to be from the message, but we still want other tools to be able to apply them.
879     #[rustc_lint_diagnostics]
tool_only_span_suggestion( &mut self, sp: Span, msg: impl Into<SubdiagnosticMessage>, suggestion: impl ToString, applicability: Applicability, ) -> &mut Self880     pub fn tool_only_span_suggestion(
881         &mut self,
882         sp: Span,
883         msg: impl Into<SubdiagnosticMessage>,
884         suggestion: impl ToString,
885         applicability: Applicability,
886     ) -> &mut Self {
887         self.span_suggestion_with_style(
888             sp,
889             msg,
890             suggestion,
891             applicability,
892             SuggestionStyle::CompletelyHidden,
893         );
894         self
895     }
896 
897     /// Add a subdiagnostic from a type that implements `Subdiagnostic` (see
898     /// [rustc_macros::Subdiagnostic]).
subdiagnostic(&mut self, subdiagnostic: impl AddToDiagnostic) -> &mut Self899     pub fn subdiagnostic(&mut self, subdiagnostic: impl AddToDiagnostic) -> &mut Self {
900         subdiagnostic.add_to_diagnostic(self);
901         self
902     }
903 
904     /// Add a subdiagnostic from a type that implements `Subdiagnostic` (see
905     /// [rustc_macros::Subdiagnostic]). Performs eager translation of any translatable messages
906     /// used in the subdiagnostic, so suitable for use with repeated messages (i.e. re-use of
907     /// interpolated variables).
eager_subdiagnostic( &mut self, handler: &crate::Handler, subdiagnostic: impl AddToDiagnostic, ) -> &mut Self908     pub fn eager_subdiagnostic(
909         &mut self,
910         handler: &crate::Handler,
911         subdiagnostic: impl AddToDiagnostic,
912     ) -> &mut Self {
913         subdiagnostic.add_to_diagnostic_with(self, |diag, msg| {
914             let args = diag.args();
915             let msg = diag.subdiagnostic_message_to_diagnostic_message(msg);
916             handler.eagerly_translate(msg, args)
917         });
918         self
919     }
920 
set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self921     pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self {
922         self.span = sp.into();
923         if let Some(span) = self.span.primary_span() {
924             self.sort_span = span;
925         }
926         self
927     }
928 
set_is_lint(&mut self) -> &mut Self929     pub fn set_is_lint(&mut self) -> &mut Self {
930         self.is_lint = true;
931         self
932     }
933 
code(&mut self, s: DiagnosticId) -> &mut Self934     pub fn code(&mut self, s: DiagnosticId) -> &mut Self {
935         self.code = Some(s);
936         self
937     }
938 
clear_code(&mut self) -> &mut Self939     pub fn clear_code(&mut self) -> &mut Self {
940         self.code = None;
941         self
942     }
943 
get_code(&self) -> Option<DiagnosticId>944     pub fn get_code(&self) -> Option<DiagnosticId> {
945         self.code.clone()
946     }
947 
set_primary_message(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self948     pub fn set_primary_message(&mut self, msg: impl Into<DiagnosticMessage>) -> &mut Self {
949         self.message[0] = (msg.into(), Style::NoStyle);
950         self
951     }
952 
953     // Exact iteration order of diagnostic arguments shouldn't make a difference to output because
954     // they're only used in interpolation.
955     #[allow(rustc::potential_query_instability)]
args(&self) -> impl Iterator<Item = DiagnosticArg<'_, 'static>>956     pub fn args(&self) -> impl Iterator<Item = DiagnosticArg<'_, 'static>> {
957         self.args.iter()
958     }
959 
set_arg( &mut self, name: impl Into<Cow<'static, str>>, arg: impl IntoDiagnosticArg, ) -> &mut Self960     pub fn set_arg(
961         &mut self,
962         name: impl Into<Cow<'static, str>>,
963         arg: impl IntoDiagnosticArg,
964     ) -> &mut Self {
965         self.args.insert(name.into(), arg.into_diagnostic_arg());
966         self
967     }
968 
replace_args( &mut self, args: FxHashMap<DiagnosticArgName<'static>, DiagnosticArgValue<'static>>, )969     pub fn replace_args(
970         &mut self,
971         args: FxHashMap<DiagnosticArgName<'static>, DiagnosticArgValue<'static>>,
972     ) {
973         self.args = args;
974     }
975 
styled_message(&self) -> &[(DiagnosticMessage, Style)]976     pub fn styled_message(&self) -> &[(DiagnosticMessage, Style)] {
977         &self.message
978     }
979 
980     /// Helper function that takes a `SubdiagnosticMessage` and returns a `DiagnosticMessage` by
981     /// combining it with the primary message of the diagnostic (if translatable, otherwise it just
982     /// passes the user's string along).
subdiagnostic_message_to_diagnostic_message( &self, attr: impl Into<SubdiagnosticMessage>, ) -> DiagnosticMessage983     pub(crate) fn subdiagnostic_message_to_diagnostic_message(
984         &self,
985         attr: impl Into<SubdiagnosticMessage>,
986     ) -> DiagnosticMessage {
987         let msg =
988             self.message.iter().map(|(msg, _)| msg).next().expect("diagnostic with no messages");
989         msg.with_subdiagnostic_message(attr.into())
990     }
991 
992     /// Convenience function for internal use, clients should use one of the
993     /// public methods above.
994     ///
995     /// Used by `proc_macro_server` for implementing `server::Diagnostic`.
sub( &mut self, level: Level, message: impl Into<SubdiagnosticMessage>, span: MultiSpan, render_span: Option<MultiSpan>, )996     pub fn sub(
997         &mut self,
998         level: Level,
999         message: impl Into<SubdiagnosticMessage>,
1000         span: MultiSpan,
1001         render_span: Option<MultiSpan>,
1002     ) {
1003         let sub = SubDiagnostic {
1004             level,
1005             message: vec![(
1006                 self.subdiagnostic_message_to_diagnostic_message(message),
1007                 Style::NoStyle,
1008             )],
1009             span,
1010             render_span,
1011         };
1012         self.children.push(sub);
1013     }
1014 
1015     /// Convenience function for internal use, clients should use one of the
1016     /// public methods above.
sub_with_highlights<M: Into<SubdiagnosticMessage>>( &mut self, level: Level, message: Vec<(M, Style)>, span: MultiSpan, render_span: Option<MultiSpan>, )1017     fn sub_with_highlights<M: Into<SubdiagnosticMessage>>(
1018         &mut self,
1019         level: Level,
1020         message: Vec<(M, Style)>,
1021         span: MultiSpan,
1022         render_span: Option<MultiSpan>,
1023     ) {
1024         let message = message
1025             .into_iter()
1026             .map(|m| (self.subdiagnostic_message_to_diagnostic_message(m.0), m.1))
1027             .collect();
1028         let sub = SubDiagnostic { level, message, span, render_span };
1029         self.children.push(sub);
1030     }
1031 
1032     /// Fields used for Hash, and PartialEq trait
keys( &self, ) -> ( &Level, &[(DiagnosticMessage, Style)], Vec<(&Cow<'static, str>, &DiagnosticArgValue<'static>)>, &Option<DiagnosticId>, &MultiSpan, &Result<Vec<CodeSuggestion>, SuggestionsDisabled>, Option<&[SubDiagnostic]>, )1033     fn keys(
1034         &self,
1035     ) -> (
1036         &Level,
1037         &[(DiagnosticMessage, Style)],
1038         Vec<(&Cow<'static, str>, &DiagnosticArgValue<'static>)>,
1039         &Option<DiagnosticId>,
1040         &MultiSpan,
1041         &Result<Vec<CodeSuggestion>, SuggestionsDisabled>,
1042         Option<&[SubDiagnostic]>,
1043     ) {
1044         (
1045             &self.level,
1046             &self.message,
1047             self.args().collect(),
1048             &self.code,
1049             &self.span,
1050             &self.suggestions,
1051             (if self.is_lint { None } else { Some(&self.children) }),
1052         )
1053     }
1054 }
1055 
1056 impl Hash for Diagnostic {
hash<H>(&self, state: &mut H) where H: Hasher,1057     fn hash<H>(&self, state: &mut H)
1058     where
1059         H: Hasher,
1060     {
1061         self.keys().hash(state);
1062     }
1063 }
1064 
1065 impl PartialEq for Diagnostic {
eq(&self, other: &Self) -> bool1066     fn eq(&self, other: &Self) -> bool {
1067         self.keys() == other.keys()
1068     }
1069 }
1070