]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_errors/src/diagnostic.rs
Tweak move error
[rust.git] / compiler / rustc_errors / src / diagnostic.rs
1 use crate::snippet::Style;
2 use crate::CodeSuggestion;
3 use crate::Level;
4 use crate::Substitution;
5 use crate::SubstitutionPart;
6 use crate::SuggestionStyle;
7 use crate::ToolMetadata;
8 use rustc_lint_defs::Applicability;
9 use rustc_serialize::json::Json;
10 use rustc_span::{MultiSpan, Span, DUMMY_SP};
11 use std::fmt;
12 use std::hash::{Hash, Hasher};
13
14 /// Error type for `Diagnostic`'s `suggestions` field, indicating that
15 /// `.disable_suggestions()` was called on the `Diagnostic`.
16 #[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)]
17 pub struct SuggestionsDisabled;
18
19 #[must_use]
20 #[derive(Clone, Debug, Encodable, Decodable)]
21 pub struct Diagnostic {
22     // NOTE(eddyb) this is private to disallow arbitrary after-the-fact changes,
23     // outside of what methods in this crate themselves allow.
24     crate level: Level,
25
26     pub message: Vec<(String, Style)>,
27     pub code: Option<DiagnosticId>,
28     pub span: MultiSpan,
29     pub children: Vec<SubDiagnostic>,
30     pub suggestions: Result<Vec<CodeSuggestion>, SuggestionsDisabled>,
31
32     /// This is not used for highlighting or rendering any error message.  Rather, it can be used
33     /// as a sort key to sort a buffer of diagnostics.  By default, it is the primary span of
34     /// `span` if there is one.  Otherwise, it is `DUMMY_SP`.
35     pub sort_span: Span,
36
37     /// If diagnostic is from Lint, custom hash function ignores notes
38     /// otherwise hash is based on the all the fields
39     pub is_lint: bool,
40 }
41
42 #[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)]
43 pub enum DiagnosticId {
44     Error(String),
45     Lint { name: String, has_future_breakage: bool, is_force_warn: bool },
46 }
47
48 /// A "sub"-diagnostic attached to a parent diagnostic.
49 /// For example, a note attached to an error.
50 #[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)]
51 pub struct SubDiagnostic {
52     pub level: Level,
53     pub message: Vec<(String, Style)>,
54     pub span: MultiSpan,
55     pub render_span: Option<MultiSpan>,
56 }
57
58 #[derive(Debug, PartialEq, Eq)]
59 pub struct DiagnosticStyledString(pub Vec<StringPart>);
60
61 impl DiagnosticStyledString {
62     pub fn new() -> DiagnosticStyledString {
63         DiagnosticStyledString(vec![])
64     }
65     pub fn push_normal<S: Into<String>>(&mut self, t: S) {
66         self.0.push(StringPart::Normal(t.into()));
67     }
68     pub fn push_highlighted<S: Into<String>>(&mut self, t: S) {
69         self.0.push(StringPart::Highlighted(t.into()));
70     }
71     pub fn push<S: Into<String>>(&mut self, t: S, highlight: bool) {
72         if highlight {
73             self.push_highlighted(t);
74         } else {
75             self.push_normal(t);
76         }
77     }
78     pub fn normal<S: Into<String>>(t: S) -> DiagnosticStyledString {
79         DiagnosticStyledString(vec![StringPart::Normal(t.into())])
80     }
81
82     pub fn highlighted<S: Into<String>>(t: S) -> DiagnosticStyledString {
83         DiagnosticStyledString(vec![StringPart::Highlighted(t.into())])
84     }
85
86     pub fn content(&self) -> String {
87         self.0.iter().map(|x| x.content()).collect::<String>()
88     }
89 }
90
91 #[derive(Debug, PartialEq, Eq)]
92 pub enum StringPart {
93     Normal(String),
94     Highlighted(String),
95 }
96
97 impl StringPart {
98     pub fn content(&self) -> &str {
99         match self {
100             &StringPart::Normal(ref s) | &StringPart::Highlighted(ref s) => s,
101         }
102     }
103 }
104
105 impl Diagnostic {
106     pub fn new(level: Level, message: &str) -> Self {
107         Diagnostic::new_with_code(level, None, message)
108     }
109
110     pub fn new_with_code(level: Level, code: Option<DiagnosticId>, message: &str) -> Self {
111         Diagnostic {
112             level,
113             message: vec![(message.to_owned(), Style::NoStyle)],
114             code,
115             span: MultiSpan::new(),
116             children: vec![],
117             suggestions: Ok(vec![]),
118             sort_span: DUMMY_SP,
119             is_lint: false,
120         }
121     }
122
123     #[inline(always)]
124     pub fn level(&self) -> Level {
125         self.level
126     }
127
128     pub fn is_error(&self) -> bool {
129         match self.level {
130             Level::Bug
131             | Level::DelayedBug
132             | Level::Fatal
133             | Level::Error { .. }
134             | Level::FailureNote => true,
135
136             Level::Warning | Level::Note | Level::Help | Level::Allow => false,
137         }
138     }
139
140     pub fn has_future_breakage(&self) -> bool {
141         match self.code {
142             Some(DiagnosticId::Lint { has_future_breakage, .. }) => has_future_breakage,
143             _ => false,
144         }
145     }
146
147     pub fn is_force_warn(&self) -> bool {
148         match self.code {
149             Some(DiagnosticId::Lint { is_force_warn, .. }) => is_force_warn,
150             _ => false,
151         }
152     }
153
154     /// Delay emission of this diagnostic as a bug.
155     ///
156     /// This can be useful in contexts where an error indicates a bug but
157     /// typically this only happens when other compilation errors have already
158     /// happened. In those cases this can be used to defer emission of this
159     /// diagnostic as a bug in the compiler only if no other errors have been
160     /// emitted.
161     ///
162     /// In the meantime, though, callsites are required to deal with the "bug"
163     /// locally in whichever way makes the most sense.
164     #[track_caller]
165     pub fn downgrade_to_delayed_bug(&mut self) -> &mut Self {
166         assert!(
167             self.is_error(),
168             "downgrade_to_delayed_bug: cannot downgrade {:?} to DelayedBug: not an error",
169             self.level
170         );
171         self.level = Level::DelayedBug;
172
173         self
174     }
175
176     /// Adds a span/label to be included in the resulting snippet.
177     ///
178     /// This is pushed onto the [`MultiSpan`] that was created when the diagnostic
179     /// was first built. That means it will be shown together with the original
180     /// span/label, *not* a span added by one of the `span_{note,warn,help,suggestions}` methods.
181     ///
182     /// This span is *not* considered a ["primary span"][`MultiSpan`]; only
183     /// the `Span` supplied when creating the diagnostic is primary.
184     pub fn span_label<T: Into<String>>(&mut self, span: Span, label: T) -> &mut Self {
185         self.span.push_span_label(span, label.into());
186         self
187     }
188
189     /// Labels all the given spans with the provided label.
190     /// See [`Self::span_label()`] for more information.
191     pub fn span_labels(
192         &mut self,
193         spans: impl IntoIterator<Item = Span>,
194         label: impl AsRef<str>,
195     ) -> &mut Self {
196         let label = label.as_ref();
197         for span in spans {
198             self.span_label(span, label);
199         }
200         self
201     }
202
203     pub fn replace_span_with(&mut self, after: Span) -> &mut Self {
204         let before = self.span.clone();
205         self.set_span(after);
206         for span_label in before.span_labels() {
207             if let Some(label) = span_label.label {
208                 self.span_label(after, label);
209             }
210         }
211         self
212     }
213
214     pub fn note_expected_found(
215         &mut self,
216         expected_label: &dyn fmt::Display,
217         expected: DiagnosticStyledString,
218         found_label: &dyn fmt::Display,
219         found: DiagnosticStyledString,
220     ) -> &mut Self {
221         self.note_expected_found_extra(expected_label, expected, found_label, found, &"", &"")
222     }
223
224     pub fn note_unsuccessful_coercion(
225         &mut self,
226         expected: DiagnosticStyledString,
227         found: DiagnosticStyledString,
228     ) -> &mut Self {
229         let mut msg: Vec<_> =
230             vec![("required when trying to coerce from type `".to_string(), Style::NoStyle)];
231         msg.extend(expected.0.iter().map(|x| match *x {
232             StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
233             StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
234         }));
235         msg.push(("` to type '".to_string(), Style::NoStyle));
236         msg.extend(found.0.iter().map(|x| match *x {
237             StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
238             StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
239         }));
240         msg.push(("`".to_string(), Style::NoStyle));
241
242         // For now, just attach these as notes
243         self.highlighted_note(msg);
244         self
245     }
246
247     pub fn note_expected_found_extra(
248         &mut self,
249         expected_label: &dyn fmt::Display,
250         expected: DiagnosticStyledString,
251         found_label: &dyn fmt::Display,
252         found: DiagnosticStyledString,
253         expected_extra: &dyn fmt::Display,
254         found_extra: &dyn fmt::Display,
255     ) -> &mut Self {
256         let expected_label = expected_label.to_string();
257         let expected_label = if expected_label.is_empty() {
258             "expected".to_string()
259         } else {
260             format!("expected {}", expected_label)
261         };
262         let found_label = found_label.to_string();
263         let found_label = if found_label.is_empty() {
264             "found".to_string()
265         } else {
266             format!("found {}", found_label)
267         };
268         let (found_padding, expected_padding) = if expected_label.len() > found_label.len() {
269             (expected_label.len() - found_label.len(), 0)
270         } else {
271             (0, found_label.len() - expected_label.len())
272         };
273         let mut msg: Vec<_> =
274             vec![(format!("{}{} `", " ".repeat(expected_padding), expected_label), Style::NoStyle)];
275         msg.extend(expected.0.iter().map(|x| match *x {
276             StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
277             StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
278         }));
279         msg.push((format!("`{}\n", expected_extra), Style::NoStyle));
280         msg.push((format!("{}{} `", " ".repeat(found_padding), found_label), Style::NoStyle));
281         msg.extend(found.0.iter().map(|x| match *x {
282             StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
283             StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
284         }));
285         msg.push((format!("`{}", found_extra), Style::NoStyle));
286
287         // For now, just attach these as notes.
288         self.highlighted_note(msg);
289         self
290     }
291
292     pub fn note_trait_signature(&mut self, name: String, signature: String) -> &mut Self {
293         self.highlighted_note(vec![
294             (format!("`{}` from trait: `", name), Style::NoStyle),
295             (signature, Style::Highlight),
296             ("`".to_string(), Style::NoStyle),
297         ]);
298         self
299     }
300
301     /// Add a note attached to this diagnostic.
302     pub fn note(&mut self, msg: &str) -> &mut Self {
303         self.sub(Level::Note, msg, MultiSpan::new(), None);
304         self
305     }
306
307     pub fn highlighted_note(&mut self, msg: Vec<(String, Style)>) -> &mut Self {
308         self.sub_with_highlights(Level::Note, msg, MultiSpan::new(), None);
309         self
310     }
311
312     /// Prints the span with a note above it.
313     /// This is like [`Diagnostic::note()`], but it gets its own span.
314     pub fn span_note<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self {
315         self.sub(Level::Note, msg, sp.into(), None);
316         self
317     }
318
319     /// Add a warning attached to this diagnostic.
320     pub fn warn(&mut self, msg: &str) -> &mut Self {
321         self.sub(Level::Warning, msg, MultiSpan::new(), None);
322         self
323     }
324
325     /// Prints the span with a warning above it.
326     /// This is like [`Diagnostic::warn()`], but it gets its own span.
327     pub fn span_warn<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self {
328         self.sub(Level::Warning, msg, sp.into(), None);
329         self
330     }
331
332     /// Add a help message attached to this diagnostic.
333     pub fn help(&mut self, msg: &str) -> &mut Self {
334         self.sub(Level::Help, msg, MultiSpan::new(), None);
335         self
336     }
337
338     /// Prints the span with some help above it.
339     /// This is like [`Diagnostic::help()`], but it gets its own span.
340     pub fn span_help<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self {
341         self.sub(Level::Help, msg, sp.into(), None);
342         self
343     }
344
345     /// Disallow attaching suggestions this diagnostic.
346     /// Any suggestions attached e.g. with the `span_suggestion_*` methods
347     /// (before and after the call to `disable_suggestions`) will be ignored.
348     pub fn disable_suggestions(&mut self) -> &mut Self {
349         self.suggestions = Err(SuggestionsDisabled);
350         self
351     }
352
353     /// Helper for pushing to `self.suggestions`, if available (not disable).
354     fn push_suggestion(&mut self, suggestion: CodeSuggestion) {
355         if let Ok(suggestions) = &mut self.suggestions {
356             suggestions.push(suggestion);
357         }
358     }
359
360     /// Show a suggestion that has multiple parts to it.
361     /// In other words, multiple changes need to be applied as part of this suggestion.
362     pub fn multipart_suggestion(
363         &mut self,
364         msg: &str,
365         suggestion: Vec<(Span, String)>,
366         applicability: Applicability,
367     ) -> &mut Self {
368         self.multipart_suggestion_with_style(
369             msg,
370             suggestion,
371             applicability,
372             SuggestionStyle::ShowCode,
373         )
374     }
375
376     /// Show a suggestion that has multiple parts to it, always as it's own subdiagnostic.
377     /// In other words, multiple changes need to be applied as part of this suggestion.
378     pub fn multipart_suggestion_verbose(
379         &mut self,
380         msg: &str,
381         suggestion: Vec<(Span, String)>,
382         applicability: Applicability,
383     ) -> &mut Self {
384         self.multipart_suggestion_with_style(
385             msg,
386             suggestion,
387             applicability,
388             SuggestionStyle::ShowAlways,
389         )
390     }
391     /// [`Diagnostic::multipart_suggestion()`] but you can set the [`SuggestionStyle`].
392     pub fn multipart_suggestion_with_style(
393         &mut self,
394         msg: &str,
395         suggestion: Vec<(Span, String)>,
396         applicability: Applicability,
397         style: SuggestionStyle,
398     ) -> &mut Self {
399         assert!(!suggestion.is_empty());
400         self.push_suggestion(CodeSuggestion {
401             substitutions: vec![Substitution {
402                 parts: suggestion
403                     .into_iter()
404                     .map(|(span, snippet)| SubstitutionPart { snippet, span })
405                     .collect(),
406             }],
407             msg: msg.to_owned(),
408             style,
409             applicability,
410             tool_metadata: Default::default(),
411         });
412         self
413     }
414
415     /// Prints out a message with for a multipart suggestion without showing the suggested code.
416     ///
417     /// This is intended to be used for suggestions that are obvious in what the changes need to
418     /// be from the message, showing the span label inline would be visually unpleasant
419     /// (marginally overlapping spans or multiline spans) and showing the snippet window wouldn't
420     /// improve understandability.
421     pub fn tool_only_multipart_suggestion(
422         &mut self,
423         msg: &str,
424         suggestion: Vec<(Span, String)>,
425         applicability: Applicability,
426     ) -> &mut Self {
427         assert!(!suggestion.is_empty());
428         self.push_suggestion(CodeSuggestion {
429             substitutions: vec![Substitution {
430                 parts: suggestion
431                     .into_iter()
432                     .map(|(span, snippet)| SubstitutionPart { snippet, span })
433                     .collect(),
434             }],
435             msg: msg.to_owned(),
436             style: SuggestionStyle::CompletelyHidden,
437             applicability,
438             tool_metadata: Default::default(),
439         });
440         self
441     }
442
443     /// Prints out a message with a suggested edit of the code.
444     ///
445     /// In case of short messages and a simple suggestion, rustc displays it as a label:
446     ///
447     /// ```text
448     /// try adding parentheses: `(tup.0).1`
449     /// ```
450     ///
451     /// The message
452     ///
453     /// * should not end in any punctuation (a `:` is added automatically)
454     /// * should not be a question (avoid language like "did you mean")
455     /// * should not contain any phrases like "the following", "as shown", etc.
456     /// * may look like "to do xyz, use" or "to do xyz, use abc"
457     /// * may contain a name of a function, variable, or type, but not whole expressions
458     ///
459     /// See `CodeSuggestion` for more information.
460     pub fn span_suggestion(
461         &mut self,
462         sp: Span,
463         msg: &str,
464         suggestion: String,
465         applicability: Applicability,
466     ) -> &mut Self {
467         self.span_suggestion_with_style(
468             sp,
469             msg,
470             suggestion,
471             applicability,
472             SuggestionStyle::ShowCode,
473         );
474         self
475     }
476
477     /// [`Diagnostic::span_suggestion()`] but you can set the [`SuggestionStyle`].
478     pub fn span_suggestion_with_style(
479         &mut self,
480         sp: Span,
481         msg: &str,
482         suggestion: String,
483         applicability: Applicability,
484         style: SuggestionStyle,
485     ) -> &mut Self {
486         self.push_suggestion(CodeSuggestion {
487             substitutions: vec![Substitution {
488                 parts: vec![SubstitutionPart { snippet: suggestion, span: sp }],
489             }],
490             msg: msg.to_owned(),
491             style,
492             applicability,
493             tool_metadata: Default::default(),
494         });
495         self
496     }
497
498     /// Always show the suggested change.
499     pub fn span_suggestion_verbose(
500         &mut self,
501         sp: Span,
502         msg: &str,
503         suggestion: String,
504         applicability: Applicability,
505     ) -> &mut Self {
506         self.span_suggestion_with_style(
507             sp,
508             msg,
509             suggestion,
510             applicability,
511             SuggestionStyle::ShowAlways,
512         );
513         self
514     }
515
516     /// Prints out a message with multiple suggested edits of the code.
517     /// See also [`Diagnostic::span_suggestion()`].
518     pub fn span_suggestions(
519         &mut self,
520         sp: Span,
521         msg: &str,
522         suggestions: impl Iterator<Item = String>,
523         applicability: Applicability,
524     ) -> &mut Self {
525         let mut suggestions: Vec<_> = suggestions.collect();
526         suggestions.sort();
527         let substitutions = suggestions
528             .into_iter()
529             .map(|snippet| Substitution { parts: vec![SubstitutionPart { snippet, span: sp }] })
530             .collect();
531         self.push_suggestion(CodeSuggestion {
532             substitutions,
533             msg: msg.to_owned(),
534             style: SuggestionStyle::ShowCode,
535             applicability,
536             tool_metadata: Default::default(),
537         });
538         self
539     }
540
541     /// Prints out a message with multiple suggested edits of the code.
542     /// See also [`Diagnostic::span_suggestion()`].
543     pub fn multipart_suggestions(
544         &mut self,
545         msg: &str,
546         suggestions: impl Iterator<Item = Vec<(Span, String)>>,
547         applicability: Applicability,
548     ) -> &mut Self {
549         self.push_suggestion(CodeSuggestion {
550             substitutions: suggestions
551                 .map(|sugg| Substitution {
552                     parts: sugg
553                         .into_iter()
554                         .map(|(span, snippet)| SubstitutionPart { snippet, span })
555                         .collect(),
556                 })
557                 .collect(),
558             msg: msg.to_owned(),
559             style: SuggestionStyle::ShowCode,
560             applicability,
561             tool_metadata: Default::default(),
562         });
563         self
564     }
565     /// Prints out a message with a suggested edit of the code. If the suggestion is presented
566     /// inline, it will only show the message and not the suggestion.
567     ///
568     /// See `CodeSuggestion` for more information.
569     pub fn span_suggestion_short(
570         &mut self,
571         sp: Span,
572         msg: &str,
573         suggestion: String,
574         applicability: Applicability,
575     ) -> &mut Self {
576         self.span_suggestion_with_style(
577             sp,
578             msg,
579             suggestion,
580             applicability,
581             SuggestionStyle::HideCodeInline,
582         );
583         self
584     }
585
586     /// Prints out a message for a suggestion without showing the suggested code.
587     ///
588     /// This is intended to be used for suggestions that are obvious in what the changes need to
589     /// be from the message, showing the span label inline would be visually unpleasant
590     /// (marginally overlapping spans or multiline spans) and showing the snippet window wouldn't
591     /// improve understandability.
592     pub fn span_suggestion_hidden(
593         &mut self,
594         sp: Span,
595         msg: &str,
596         suggestion: String,
597         applicability: Applicability,
598     ) -> &mut Self {
599         self.span_suggestion_with_style(
600             sp,
601             msg,
602             suggestion,
603             applicability,
604             SuggestionStyle::HideCodeAlways,
605         );
606         self
607     }
608
609     /// Adds a suggestion to the JSON output that will not be shown in the CLI.
610     ///
611     /// This is intended to be used for suggestions that are *very* obvious in what the changes
612     /// need to be from the message, but we still want other tools to be able to apply them.
613     pub fn tool_only_span_suggestion(
614         &mut self,
615         sp: Span,
616         msg: &str,
617         suggestion: String,
618         applicability: Applicability,
619     ) -> &mut Self {
620         self.span_suggestion_with_style(
621             sp,
622             msg,
623             suggestion,
624             applicability,
625             SuggestionStyle::CompletelyHidden,
626         );
627         self
628     }
629
630     /// Adds a suggestion intended only for a tool. The intent is that the metadata encodes
631     /// the suggestion in a tool-specific way, as it may not even directly involve Rust code.
632     pub fn tool_only_suggestion_with_metadata(
633         &mut self,
634         msg: &str,
635         applicability: Applicability,
636         tool_metadata: Json,
637     ) {
638         self.push_suggestion(CodeSuggestion {
639             substitutions: vec![],
640             msg: msg.to_owned(),
641             style: SuggestionStyle::CompletelyHidden,
642             applicability,
643             tool_metadata: ToolMetadata::new(tool_metadata),
644         })
645     }
646
647     pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self {
648         self.span = sp.into();
649         if let Some(span) = self.span.primary_span() {
650             self.sort_span = span;
651         }
652         self
653     }
654
655     pub fn set_is_lint(&mut self) -> &mut Self {
656         self.is_lint = true;
657         self
658     }
659
660     pub fn code(&mut self, s: DiagnosticId) -> &mut Self {
661         self.code = Some(s);
662         self
663     }
664
665     pub fn clear_code(&mut self) -> &mut Self {
666         self.code = None;
667         self
668     }
669
670     pub fn get_code(&self) -> Option<DiagnosticId> {
671         self.code.clone()
672     }
673
674     pub fn set_primary_message<M: Into<String>>(&mut self, msg: M) -> &mut Self {
675         self.message[0] = (msg.into(), Style::NoStyle);
676         self
677     }
678
679     pub fn message(&self) -> String {
680         self.message.iter().map(|i| i.0.as_str()).collect::<String>()
681     }
682
683     pub fn styled_message(&self) -> &Vec<(String, Style)> {
684         &self.message
685     }
686
687     /// Convenience function for internal use, clients should use one of the
688     /// public methods above.
689     ///
690     /// Used by `proc_macro_server` for implementing `server::Diagnostic`.
691     pub fn sub(
692         &mut self,
693         level: Level,
694         message: &str,
695         span: MultiSpan,
696         render_span: Option<MultiSpan>,
697     ) {
698         let sub = SubDiagnostic {
699             level,
700             message: vec![(message.to_owned(), Style::NoStyle)],
701             span,
702             render_span,
703         };
704         self.children.push(sub);
705     }
706
707     /// Convenience function for internal use, clients should use one of the
708     /// public methods above.
709     fn sub_with_highlights(
710         &mut self,
711         level: Level,
712         message: Vec<(String, Style)>,
713         span: MultiSpan,
714         render_span: Option<MultiSpan>,
715     ) {
716         let sub = SubDiagnostic { level, message, span, render_span };
717         self.children.push(sub);
718     }
719
720     /// Fields used for Hash, and PartialEq trait
721     fn keys(
722         &self,
723     ) -> (
724         &Level,
725         &Vec<(String, Style)>,
726         &Option<DiagnosticId>,
727         &MultiSpan,
728         &Result<Vec<CodeSuggestion>, SuggestionsDisabled>,
729         Option<&Vec<SubDiagnostic>>,
730     ) {
731         (
732             &self.level,
733             &self.message,
734             &self.code,
735             &self.span,
736             &self.suggestions,
737             (if self.is_lint { None } else { Some(&self.children) }),
738         )
739     }
740 }
741
742 impl Hash for Diagnostic {
743     fn hash<H>(&self, state: &mut H)
744     where
745         H: Hasher,
746     {
747         self.keys().hash(state);
748     }
749 }
750
751 impl PartialEq for Diagnostic {
752     fn eq(&self, other: &Self) -> bool {
753         self.keys() == other.keys()
754     }
755 }
756
757 impl SubDiagnostic {
758     pub fn message(&self) -> String {
759         self.message.iter().map(|i| i.0.as_str()).collect::<String>()
760     }
761
762     pub fn styled_message(&self) -> &Vec<(String, Style)> {
763         &self.message
764     }
765 }