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