1 use crate::snippet::Style;
2 use crate::CodeSuggestion;
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};
12 use std::hash::{Hash, Hasher};
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;
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.
26 pub message: Vec<(String, Style)>,
27 pub code: Option<DiagnosticId>,
29 pub children: Vec<SubDiagnostic>,
30 pub suggestions: Result<Vec<CodeSuggestion>, SuggestionsDisabled>,
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`.
37 /// If diagnostic is from Lint, custom hash function ignores notes
38 /// otherwise hash is based on the all the fields
42 #[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)]
43 pub enum DiagnosticId {
45 Lint { name: String, has_future_breakage: bool, is_force_warn: bool },
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 {
53 pub message: Vec<(String, Style)>,
55 pub render_span: Option<MultiSpan>,
58 #[derive(Debug, PartialEq, Eq)]
59 pub struct DiagnosticStyledString(pub Vec<StringPart>);
61 impl DiagnosticStyledString {
62 pub fn new() -> DiagnosticStyledString {
63 DiagnosticStyledString(vec![])
65 pub fn push_normal<S: Into<String>>(&mut self, t: S) {
66 self.0.push(StringPart::Normal(t.into()));
68 pub fn push_highlighted<S: Into<String>>(&mut self, t: S) {
69 self.0.push(StringPart::Highlighted(t.into()));
71 pub fn push<S: Into<String>>(&mut self, t: S, highlight: bool) {
73 self.push_highlighted(t);
78 pub fn normal<S: Into<String>>(t: S) -> DiagnosticStyledString {
79 DiagnosticStyledString(vec![StringPart::Normal(t.into())])
82 pub fn highlighted<S: Into<String>>(t: S) -> DiagnosticStyledString {
83 DiagnosticStyledString(vec![StringPart::Highlighted(t.into())])
86 pub fn content(&self) -> String {
87 self.0.iter().map(|x| x.content()).collect::<String>()
91 #[derive(Debug, PartialEq, Eq)]
98 pub fn content(&self) -> &str {
100 &StringPart::Normal(ref s) | &StringPart::Highlighted(ref s) => s,
106 pub fn new(level: Level, message: &str) -> Self {
107 Diagnostic::new_with_code(level, None, message)
110 pub fn new_with_code(level: Level, code: Option<DiagnosticId>, message: &str) -> Self {
113 message: vec![(message.to_owned(), Style::NoStyle)],
115 span: MultiSpan::new(),
117 suggestions: Ok(vec![]),
124 pub fn level(&self) -> Level {
128 pub fn is_error(&self) -> bool {
133 | Level::Error { .. }
134 | Level::FailureNote => true,
140 | Level::Expect(_) => false,
144 pub fn has_future_breakage(&self) -> bool {
146 Some(DiagnosticId::Lint { has_future_breakage, .. }) => has_future_breakage,
151 pub fn is_force_warn(&self) -> bool {
153 Some(DiagnosticId::Lint { is_force_warn, .. }) => is_force_warn,
158 /// Delay emission of this diagnostic as a bug.
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
166 /// In the meantime, though, callsites are required to deal with the "bug"
167 /// locally in whichever way makes the most sense.
169 pub fn downgrade_to_delayed_bug(&mut self) -> &mut Self {
172 "downgrade_to_delayed_bug: cannot downgrade {:?} to DelayedBug: not an error",
175 self.level = Level::DelayedBug;
180 /// Adds a span/label to be included in the resulting snippet.
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.
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());
193 /// Labels all the given spans with the provided label.
194 /// See [`Self::span_label()`] for more information.
197 spans: impl IntoIterator<Item = Span>,
198 label: impl AsRef<str>,
200 let label = label.as_ref();
202 self.span_label(span, label);
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);
218 pub fn note_expected_found(
220 expected_label: &dyn fmt::Display,
221 expected: DiagnosticStyledString,
222 found_label: &dyn fmt::Display,
223 found: DiagnosticStyledString,
225 self.note_expected_found_extra(expected_label, expected, found_label, found, &"", &"")
228 pub fn note_unsuccessful_coercion(
230 expected: DiagnosticStyledString,
231 found: DiagnosticStyledString,
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),
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),
244 msg.push(("`".to_string(), Style::NoStyle));
246 // For now, just attach these as notes
247 self.highlighted_note(msg);
251 pub fn note_expected_found_extra(
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,
260 let expected_label = expected_label.to_string();
261 let expected_label = if expected_label.is_empty() {
262 "expected".to_string()
264 format!("expected {}", expected_label)
266 let found_label = found_label.to_string();
267 let found_label = if found_label.is_empty() {
270 format!("found {}", found_label)
272 let (found_padding, expected_padding) = if expected_label.len() > found_label.len() {
273 (expected_label.len() - found_label.len(), 0)
275 (0, found_label.len() - expected_label.len())
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),
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),
289 msg.push((format!("`{}", found_extra), Style::NoStyle));
291 // For now, just attach these as notes.
292 self.highlighted_note(msg);
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),
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);
311 pub fn highlighted_note(&mut self, msg: Vec<(String, Style)>) -> &mut Self {
312 self.sub_with_highlights(Level::Note, msg, MultiSpan::new(), None);
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);
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);
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);
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);
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);
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);
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);
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(
369 suggestion: Vec<(Span, String)>,
370 applicability: Applicability,
372 self.multipart_suggestion_with_style(
376 SuggestionStyle::ShowCode,
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(
385 suggestion: Vec<(Span, String)>,
386 applicability: Applicability,
388 self.multipart_suggestion_with_style(
392 SuggestionStyle::ShowAlways,
395 /// [`Diagnostic::multipart_suggestion()`] but you can set the [`SuggestionStyle`].
396 pub fn multipart_suggestion_with_style(
399 suggestion: Vec<(Span, String)>,
400 applicability: Applicability,
401 style: SuggestionStyle,
403 assert!(!suggestion.is_empty());
404 self.push_suggestion(CodeSuggestion {
405 substitutions: vec![Substitution {
408 .map(|(span, snippet)| SubstitutionPart { snippet, span })
414 tool_metadata: Default::default(),
419 /// Prints out a message with for a multipart suggestion without showing the suggested code.
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(
428 suggestion: Vec<(Span, String)>,
429 applicability: Applicability,
431 assert!(!suggestion.is_empty());
432 self.push_suggestion(CodeSuggestion {
433 substitutions: vec![Substitution {
436 .map(|(span, snippet)| SubstitutionPart { snippet, span })
440 style: SuggestionStyle::CompletelyHidden,
442 tool_metadata: Default::default(),
447 /// Prints out a message with a suggested edit of the code.
449 /// In case of short messages and a simple suggestion, rustc displays it as a label:
452 /// try adding parentheses: `(tup.0).1`
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
463 /// See `CodeSuggestion` for more information.
464 pub fn span_suggestion(
469 applicability: Applicability,
471 self.span_suggestion_with_style(
476 SuggestionStyle::ShowCode,
481 /// [`Diagnostic::span_suggestion()`] but you can set the [`SuggestionStyle`].
482 pub fn span_suggestion_with_style(
487 applicability: Applicability,
488 style: SuggestionStyle,
490 self.push_suggestion(CodeSuggestion {
491 substitutions: vec![Substitution {
492 parts: vec![SubstitutionPart { snippet: suggestion, span: sp }],
497 tool_metadata: Default::default(),
502 /// Always show the suggested change.
503 pub fn span_suggestion_verbose(
508 applicability: Applicability,
510 self.span_suggestion_with_style(
515 SuggestionStyle::ShowAlways,
520 /// Prints out a message with multiple suggested edits of the code.
521 /// See also [`Diagnostic::span_suggestion()`].
522 pub fn span_suggestions(
526 suggestions: impl Iterator<Item = String>,
527 applicability: Applicability,
529 let mut suggestions: Vec<_> = suggestions.collect();
531 let substitutions = suggestions
533 .map(|snippet| Substitution { parts: vec![SubstitutionPart { snippet, span: sp }] })
535 self.push_suggestion(CodeSuggestion {
538 style: SuggestionStyle::ShowCode,
540 tool_metadata: Default::default(),
545 /// Prints out a message with multiple suggested edits of the code.
546 /// See also [`Diagnostic::span_suggestion()`].
547 pub fn multipart_suggestions(
550 suggestions: impl Iterator<Item = Vec<(Span, String)>>,
551 applicability: Applicability,
553 self.push_suggestion(CodeSuggestion {
554 substitutions: suggestions
555 .map(|sugg| Substitution {
558 .map(|(span, snippet)| SubstitutionPart { snippet, span })
563 style: SuggestionStyle::ShowCode,
565 tool_metadata: Default::default(),
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.
572 /// See `CodeSuggestion` for more information.
573 pub fn span_suggestion_short(
578 applicability: Applicability,
580 self.span_suggestion_with_style(
585 SuggestionStyle::HideCodeInline,
590 /// Prints out a message for a suggestion without showing the suggested code.
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(
601 applicability: Applicability,
603 self.span_suggestion_with_style(
608 SuggestionStyle::HideCodeAlways,
613 /// Adds a suggestion to the JSON output that will not be shown in the CLI.
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(
622 applicability: Applicability,
624 self.span_suggestion_with_style(
629 SuggestionStyle::CompletelyHidden,
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(
639 applicability: Applicability,
642 self.push_suggestion(CodeSuggestion {
643 substitutions: vec![],
645 style: SuggestionStyle::CompletelyHidden,
647 tool_metadata: ToolMetadata::new(tool_metadata),
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;
659 pub fn set_is_lint(&mut self) -> &mut Self {
664 pub fn code(&mut self, s: DiagnosticId) -> &mut Self {
669 pub fn clear_code(&mut self) -> &mut Self {
674 pub fn get_code(&self) -> Option<DiagnosticId> {
678 pub fn set_primary_message<M: Into<String>>(&mut self, msg: M) -> &mut Self {
679 self.message[0] = (msg.into(), Style::NoStyle);
683 pub fn message(&self) -> String {
684 self.message.iter().map(|i| i.0.as_str()).collect::<String>()
687 pub fn styled_message(&self) -> &Vec<(String, Style)> {
691 /// Convenience function for internal use, clients should use one of the
692 /// public methods above.
694 /// Used by `proc_macro_server` for implementing `server::Diagnostic`.
700 render_span: Option<MultiSpan>,
702 let sub = SubDiagnostic {
704 message: vec![(message.to_owned(), Style::NoStyle)],
708 self.children.push(sub);
711 /// Convenience function for internal use, clients should use one of the
712 /// public methods above.
713 fn sub_with_highlights(
716 message: Vec<(String, Style)>,
718 render_span: Option<MultiSpan>,
720 let sub = SubDiagnostic { level, message, span, render_span };
721 self.children.push(sub);
724 /// Fields used for Hash, and PartialEq trait
729 &Vec<(String, Style)>,
730 &Option<DiagnosticId>,
732 &Result<Vec<CodeSuggestion>, SuggestionsDisabled>,
733 Option<&Vec<SubDiagnostic>>,
741 (if self.is_lint { None } else { Some(&self.children) }),
746 impl Hash for Diagnostic {
747 fn hash<H>(&self, state: &mut H)
751 self.keys().hash(state);
755 impl PartialEq for Diagnostic {
756 fn eq(&self, other: &Self) -> bool {
757 self.keys() == other.keys()
762 pub fn message(&self) -> String {
763 self.message.iter().map(|i| i.0.as_str()).collect::<String>()
766 pub fn styled_message(&self) -> &Vec<(String, Style)> {