1 use crate::snippet::Style;
2 use crate::Applicability;
3 use crate::CodeSuggestion;
5 use crate::Substitution;
6 use crate::SubstitutionPart;
7 use crate::SuggestionStyle;
8 use rustc_span::{MultiSpan, Span, DUMMY_SP};
12 #[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)]
13 pub struct Diagnostic {
15 pub message: Vec<(String, Style)>,
16 pub code: Option<DiagnosticId>,
18 pub children: Vec<SubDiagnostic>,
19 pub suggestions: Vec<CodeSuggestion>,
21 /// This is not used for highlighting or rendering any error message. Rather, it can be used
22 /// as a sort key to sort a buffer of diagnostics. By default, it is the primary span of
23 /// `span` if there is one. Otherwise, it is `DUMMY_SP`.
27 #[derive(Clone, Debug, PartialEq, Eq, Hash, Encodable, Decodable)]
28 pub enum DiagnosticId {
33 /// For example a note attached to an error.
34 #[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)]
35 pub struct SubDiagnostic {
37 pub message: Vec<(String, Style)>,
39 pub render_span: Option<MultiSpan>,
42 #[derive(Debug, PartialEq, Eq)]
43 pub struct DiagnosticStyledString(pub Vec<StringPart>);
45 impl DiagnosticStyledString {
46 pub fn new() -> DiagnosticStyledString {
47 DiagnosticStyledString(vec![])
49 pub fn push_normal<S: Into<String>>(&mut self, t: S) {
50 self.0.push(StringPart::Normal(t.into()));
52 pub fn push_highlighted<S: Into<String>>(&mut self, t: S) {
53 self.0.push(StringPart::Highlighted(t.into()));
55 pub fn push<S: Into<String>>(&mut self, t: S, highlight: bool) {
57 self.push_highlighted(t);
62 pub fn normal<S: Into<String>>(t: S) -> DiagnosticStyledString {
63 DiagnosticStyledString(vec![StringPart::Normal(t.into())])
66 pub fn highlighted<S: Into<String>>(t: S) -> DiagnosticStyledString {
67 DiagnosticStyledString(vec![StringPart::Highlighted(t.into())])
70 pub fn content(&self) -> String {
71 self.0.iter().map(|x| x.content()).collect::<String>()
75 #[derive(Debug, PartialEq, Eq)]
82 pub fn content(&self) -> &str {
84 &StringPart::Normal(ref s) | &StringPart::Highlighted(ref s) => s,
90 pub fn new(level: Level, message: &str) -> Self {
91 Diagnostic::new_with_code(level, None, message)
94 pub fn new_with_code(level: Level, code: Option<DiagnosticId>, message: &str) -> Self {
97 message: vec![(message.to_owned(), Style::NoStyle)],
99 span: MultiSpan::new(),
106 pub fn is_error(&self) -> bool {
108 Level::Bug | Level::Fatal | Level::Error | Level::FailureNote => true,
110 Level::Warning | Level::Note | Level::Help | Level::Cancelled => false,
114 /// Cancel the diagnostic (a structured diagnostic must either be emitted or
115 /// canceled or it will panic when dropped).
116 pub fn cancel(&mut self) {
117 self.level = Level::Cancelled;
120 pub fn cancelled(&self) -> bool {
121 self.level == Level::Cancelled
124 /// Adds a span/label to be included in the resulting snippet.
126 /// This is pushed onto the [`MultiSpan`] that was created when the diagnostic
127 /// was first built. That means it will be shown together with the original
128 /// span/label, *not* a span added by one of the `span_{note,warn,help,suggestions}` methods.
130 /// This span is *not* considered a ["primary span"][`MultiSpan`]; only
131 /// the `Span` supplied when creating the diagnostic is primary.
133 /// [`MultiSpan`]: ../rustc_span/struct.MultiSpan.html
134 pub fn span_label<T: Into<String>>(&mut self, span: Span, label: T) -> &mut Self {
135 self.span.push_span_label(span, label.into());
139 pub fn replace_span_with(&mut self, after: Span) -> &mut Self {
140 let before = self.span.clone();
141 self.set_span(after);
142 for span_label in before.span_labels() {
143 if let Some(label) = span_label.label {
144 self.span_label(after, label);
150 pub fn note_expected_found(
152 expected_label: &dyn fmt::Display,
153 expected: DiagnosticStyledString,
154 found_label: &dyn fmt::Display,
155 found: DiagnosticStyledString,
157 self.note_expected_found_extra(expected_label, expected, found_label, found, &"", &"")
160 pub fn note_unsuccessfull_coercion(
162 expected: DiagnosticStyledString,
163 found: DiagnosticStyledString,
165 let mut msg: Vec<_> =
166 vec![("required when trying to coerce from type `".to_string(), Style::NoStyle)];
167 msg.extend(expected.0.iter().map(|x| match *x {
168 StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
169 StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
171 msg.push(("` to type '".to_string(), Style::NoStyle));
172 msg.extend(found.0.iter().map(|x| match *x {
173 StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
174 StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
176 msg.push(("`".to_string(), Style::NoStyle));
178 // For now, just attach these as notes
179 self.highlighted_note(msg);
183 pub fn note_expected_found_extra(
185 expected_label: &dyn fmt::Display,
186 expected: DiagnosticStyledString,
187 found_label: &dyn fmt::Display,
188 found: DiagnosticStyledString,
189 expected_extra: &dyn fmt::Display,
190 found_extra: &dyn fmt::Display,
192 let expected_label = expected_label.to_string();
193 let expected_label = if expected_label.is_empty() {
194 "expected".to_string()
196 format!("expected {}", expected_label)
198 let found_label = found_label.to_string();
199 let found_label = if found_label.is_empty() {
202 format!("found {}", found_label)
204 let (found_padding, expected_padding) = if expected_label.len() > found_label.len() {
205 (expected_label.len() - found_label.len(), 0)
207 (0, found_label.len() - expected_label.len())
209 let mut msg: Vec<_> =
210 vec![(format!("{}{} `", " ".repeat(expected_padding), expected_label), Style::NoStyle)];
211 msg.extend(expected.0.iter().map(|x| match *x {
212 StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
213 StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
215 msg.push((format!("`{}\n", expected_extra), Style::NoStyle));
216 msg.push((format!("{}{} `", " ".repeat(found_padding), found_label), Style::NoStyle));
217 msg.extend(found.0.iter().map(|x| match *x {
218 StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
219 StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
221 msg.push((format!("`{}", found_extra), Style::NoStyle));
223 // For now, just attach these as notes.
224 self.highlighted_note(msg);
228 pub fn note_trait_signature(&mut self, name: String, signature: String) -> &mut Self {
229 self.highlighted_note(vec![
230 (format!("`{}` from trait: `", name), Style::NoStyle),
231 (signature, Style::Highlight),
232 ("`".to_string(), Style::NoStyle),
237 pub fn note(&mut self, msg: &str) -> &mut Self {
238 self.sub(Level::Note, msg, MultiSpan::new(), None);
242 pub fn highlighted_note(&mut self, msg: Vec<(String, Style)>) -> &mut Self {
243 self.sub_with_highlights(Level::Note, msg, MultiSpan::new(), None);
247 /// Prints the span with a note above it.
248 pub fn span_note<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self {
249 self.sub(Level::Note, msg, sp.into(), None);
253 pub fn warn(&mut self, msg: &str) -> &mut Self {
254 self.sub(Level::Warning, msg, MultiSpan::new(), None);
258 /// Prints the span with a warn above it.
259 pub fn span_warn<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self {
260 self.sub(Level::Warning, msg, sp.into(), None);
264 pub fn help(&mut self, msg: &str) -> &mut Self {
265 self.sub(Level::Help, msg, MultiSpan::new(), None);
269 /// Prints the span with some help above it.
270 pub fn span_help<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self {
271 self.sub(Level::Help, msg, sp.into(), None);
275 pub fn multipart_suggestion(
278 suggestion: Vec<(Span, String)>,
279 applicability: Applicability,
281 self.suggestions.push(CodeSuggestion {
282 substitutions: vec![Substitution {
285 .map(|(span, snippet)| SubstitutionPart { snippet, span })
289 style: SuggestionStyle::ShowCode,
295 pub fn multipart_suggestions(
298 suggestions: Vec<Vec<(Span, String)>>,
299 applicability: Applicability,
301 self.suggestions.push(CodeSuggestion {
302 substitutions: suggestions
304 .map(|suggestion| Substitution {
307 .map(|(span, snippet)| SubstitutionPart { snippet, span })
312 style: SuggestionStyle::ShowCode,
318 /// Prints out a message with for a multipart suggestion without showing the suggested code.
320 /// This is intended to be used for suggestions that are obvious in what the changes need to
321 /// be from the message, showing the span label inline would be visually unpleasant
322 /// (marginally overlapping spans or multiline spans) and showing the snippet window wouldn't
323 /// improve understandability.
324 pub fn tool_only_multipart_suggestion(
327 suggestion: Vec<(Span, String)>,
328 applicability: Applicability,
330 self.suggestions.push(CodeSuggestion {
331 substitutions: vec![Substitution {
334 .map(|(span, snippet)| SubstitutionPart { snippet, span })
338 style: SuggestionStyle::CompletelyHidden,
344 /// Prints out a message with a suggested edit of the code.
346 /// In case of short messages and a simple suggestion, rustc displays it as a label:
349 /// try adding parentheses: `(tup.0).1`
354 /// * should not end in any punctuation (a `:` is added automatically)
355 /// * should not be a question (avoid language like "did you mean")
356 /// * should not contain any phrases like "the following", "as shown", etc.
357 /// * may look like "to do xyz, use" or "to do xyz, use abc"
358 /// * may contain a name of a function, variable, or type, but not whole expressions
360 /// See `CodeSuggestion` for more information.
361 pub fn span_suggestion(
366 applicability: Applicability,
368 self.span_suggestion_with_style(
373 SuggestionStyle::ShowCode,
378 pub fn span_suggestion_with_style(
383 applicability: Applicability,
384 style: SuggestionStyle,
386 self.suggestions.push(CodeSuggestion {
387 substitutions: vec![Substitution {
388 parts: vec![SubstitutionPart { snippet: suggestion, span: sp }],
397 pub fn span_suggestion_verbose(
402 applicability: Applicability,
404 self.span_suggestion_with_style(
409 SuggestionStyle::ShowAlways,
414 /// Prints out a message with multiple suggested edits of the code.
415 pub fn span_suggestions(
419 suggestions: impl Iterator<Item = String>,
420 applicability: Applicability,
422 self.suggestions.push(CodeSuggestion {
423 substitutions: suggestions
424 .map(|snippet| Substitution { parts: vec![SubstitutionPart { snippet, span: sp }] })
427 style: SuggestionStyle::ShowCode,
433 /// Prints out a message with a suggested edit of the code. If the suggestion is presented
434 /// inline, it will only show the message and not the suggestion.
436 /// See `CodeSuggestion` for more information.
437 pub fn span_suggestion_short(
442 applicability: Applicability,
444 self.span_suggestion_with_style(
449 SuggestionStyle::HideCodeInline,
454 /// Prints out a message with for a suggestion without showing the suggested code.
456 /// This is intended to be used for suggestions that are obvious in what the changes need to
457 /// be from the message, showing the span label inline would be visually unpleasant
458 /// (marginally overlapping spans or multiline spans) and showing the snippet window wouldn't
459 /// improve understandability.
460 pub fn span_suggestion_hidden(
465 applicability: Applicability,
467 self.span_suggestion_with_style(
472 SuggestionStyle::HideCodeAlways,
477 /// Adds a suggestion to the json output, but otherwise remains silent/undisplayed in the cli.
479 /// This is intended to be used for suggestions that are *very* obvious in what the changes
480 /// need to be from the message, but we still want other tools to be able to apply them.
481 pub fn tool_only_span_suggestion(
486 applicability: Applicability,
488 self.span_suggestion_with_style(
493 SuggestionStyle::CompletelyHidden,
498 pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self {
499 self.span = sp.into();
500 if let Some(span) = self.span.primary_span() {
501 self.sort_span = span;
506 pub fn code(&mut self, s: DiagnosticId) -> &mut Self {
511 pub fn clear_code(&mut self) -> &mut Self {
516 pub fn get_code(&self) -> Option<DiagnosticId> {
520 pub fn set_primary_message<M: Into<String>>(&mut self, msg: M) -> &mut Self {
521 self.message[0] = (msg.into(), Style::NoStyle);
525 pub fn message(&self) -> String {
526 self.message.iter().map(|i| i.0.as_str()).collect::<String>()
529 pub fn styled_message(&self) -> &Vec<(String, Style)> {
533 /// Convenience function for internal use, clients should use one of the
534 /// public methods above.
540 render_span: Option<MultiSpan>,
542 let sub = SubDiagnostic {
544 message: vec![(message.to_owned(), Style::NoStyle)],
548 self.children.push(sub);
551 /// Convenience function for internal use, clients should use one of the
552 /// public methods above.
553 fn sub_with_highlights(
556 message: Vec<(String, Style)>,
558 render_span: Option<MultiSpan>,
560 let sub = SubDiagnostic { level, message, span, render_span };
561 self.children.push(sub);
566 pub fn message(&self) -> String {
567 self.message.iter().map(|i| i.0.as_str()).collect::<String>()
570 pub fn styled_message(&self) -> &Vec<(String, Style)> {