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