]> git.lizzy.rs Git - rust.git/blob - src/librustc_errors/diagnostic.rs
On recursive ADT, provide indirection structured suggestion
[rust.git] / src / librustc_errors / diagnostic.rs
1 use crate::snippet::Style;
2 use crate::Applicability;
3 use crate::CodeSuggestion;
4 use crate::Level;
5 use crate::Substitution;
6 use crate::SubstitutionPart;
7 use crate::SuggestionStyle;
8 use rustc_span::{MultiSpan, Span, DUMMY_SP};
9 use std::fmt;
10
11 #[must_use]
12 #[derive(Clone, Debug, PartialEq, Hash, RustcEncodable, RustcDecodable)]
13 pub struct Diagnostic {
14     pub level: Level,
15     pub message: Vec<(String, Style)>,
16     pub code: Option<DiagnosticId>,
17     pub span: MultiSpan,
18     pub children: Vec<SubDiagnostic>,
19     pub suggestions: Vec<CodeSuggestion>,
20
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`.
24     pub sort_span: Span,
25 }
26
27 #[derive(Clone, Debug, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)]
28 pub enum DiagnosticId {
29     Error(String),
30     Lint(String),
31 }
32
33 /// For example a note attached to an error.
34 #[derive(Clone, Debug, PartialEq, Hash, RustcEncodable, RustcDecodable)]
35 pub struct SubDiagnostic {
36     pub level: Level,
37     pub message: Vec<(String, Style)>,
38     pub span: MultiSpan,
39     pub render_span: Option<MultiSpan>,
40 }
41
42 #[derive(Debug, PartialEq, Eq)]
43 pub struct DiagnosticStyledString(pub Vec<StringPart>);
44
45 impl DiagnosticStyledString {
46     pub fn new() -> DiagnosticStyledString {
47         DiagnosticStyledString(vec![])
48     }
49     pub fn push_normal<S: Into<String>>(&mut self, t: S) {
50         self.0.push(StringPart::Normal(t.into()));
51     }
52     pub fn push_highlighted<S: Into<String>>(&mut self, t: S) {
53         self.0.push(StringPart::Highlighted(t.into()));
54     }
55     pub fn push<S: Into<String>>(&mut self, t: S, highlight: bool) {
56         if highlight {
57             self.push_highlighted(t);
58         } else {
59             self.push_normal(t);
60         }
61     }
62     pub fn normal<S: Into<String>>(t: S) -> DiagnosticStyledString {
63         DiagnosticStyledString(vec![StringPart::Normal(t.into())])
64     }
65
66     pub fn highlighted<S: Into<String>>(t: S) -> DiagnosticStyledString {
67         DiagnosticStyledString(vec![StringPart::Highlighted(t.into())])
68     }
69
70     pub fn content(&self) -> String {
71         self.0.iter().map(|x| x.content()).collect::<String>()
72     }
73 }
74
75 #[derive(Debug, PartialEq, Eq)]
76 pub enum StringPart {
77     Normal(String),
78     Highlighted(String),
79 }
80
81 impl StringPart {
82     pub fn content(&self) -> &str {
83         match self {
84             &StringPart::Normal(ref s) | &StringPart::Highlighted(ref s) => s,
85         }
86     }
87 }
88
89 impl Diagnostic {
90     pub fn new(level: Level, message: &str) -> Self {
91         Diagnostic::new_with_code(level, None, message)
92     }
93
94     pub fn new_with_code(level: Level, code: Option<DiagnosticId>, message: &str) -> Self {
95         Diagnostic {
96             level,
97             message: vec![(message.to_owned(), Style::NoStyle)],
98             code,
99             span: MultiSpan::new(),
100             children: vec![],
101             suggestions: vec![],
102             sort_span: DUMMY_SP,
103         }
104     }
105
106     pub fn is_error(&self) -> bool {
107         match self.level {
108             Level::Bug | Level::Fatal | Level::Error | Level::FailureNote => true,
109
110             Level::Warning | Level::Note | Level::Help | Level::Cancelled => false,
111         }
112     }
113
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;
118     }
119
120     pub fn cancelled(&self) -> bool {
121         self.level == Level::Cancelled
122     }
123
124     /// Set the sorting span.
125     pub fn set_sort_span(&mut self, sp: Span) {
126         self.sort_span = sp;
127     }
128
129     /// Adds a span/label to be included in the resulting snippet.
130     /// This label will be shown together with the original span/label used when creating the
131     /// diagnostic, *not* a span added by one of the `span_*` methods.
132     ///
133     /// This is pushed onto the `MultiSpan` that was created when the
134     /// diagnostic was first built. If you don't call this function at
135     /// all, and you just supplied a `Span` to create the diagnostic,
136     /// then the snippet will just include that `Span`, which is
137     /// called the primary span.
138     pub fn span_label<T: Into<String>>(&mut self, span: Span, label: T) -> &mut Self {
139         self.span.push_span_label(span, label.into());
140         self
141     }
142
143     pub fn replace_span_with(&mut self, after: Span) -> &mut Self {
144         let before = self.span.clone();
145         self.set_span(after);
146         for span_label in before.span_labels() {
147             if let Some(label) = span_label.label {
148                 self.span_label(after, label);
149             }
150         }
151         self
152     }
153
154     pub fn note_expected_found(
155         &mut self,
156         expected_label: &dyn fmt::Display,
157         expected: DiagnosticStyledString,
158         found_label: &dyn fmt::Display,
159         found: DiagnosticStyledString,
160     ) -> &mut Self {
161         self.note_expected_found_extra(expected_label, expected, found_label, found, &"", &"")
162     }
163
164     pub fn note_unsuccessfull_coercion(
165         &mut self,
166         expected: DiagnosticStyledString,
167         found: DiagnosticStyledString,
168     ) -> &mut Self {
169         let mut msg: Vec<_> =
170             vec![("required when trying to coerce from type `".to_string(), Style::NoStyle)];
171         msg.extend(expected.0.iter().map(|x| match *x {
172             StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
173             StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
174         }));
175         msg.push(("` to type '".to_string(), Style::NoStyle));
176         msg.extend(found.0.iter().map(|x| match *x {
177             StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
178             StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
179         }));
180         msg.push(("`".to_string(), Style::NoStyle));
181
182         // For now, just attach these as notes
183         self.highlighted_note(msg);
184         self
185     }
186
187     pub fn note_expected_found_extra(
188         &mut self,
189         expected_label: &dyn fmt::Display,
190         expected: DiagnosticStyledString,
191         found_label: &dyn fmt::Display,
192         found: DiagnosticStyledString,
193         expected_extra: &dyn fmt::Display,
194         found_extra: &dyn fmt::Display,
195     ) -> &mut Self {
196         let expected_label = expected_label.to_string();
197         let expected_label = if expected_label.is_empty() {
198             "expected".to_string()
199         } else {
200             format!("expected {}", expected_label)
201         };
202         let found_label = found_label.to_string();
203         let found_label = if found_label.is_empty() {
204             "found".to_string()
205         } else {
206             format!("found {}", found_label)
207         };
208         let (found_padding, expected_padding) = if expected_label.len() > found_label.len() {
209             (expected_label.len() - found_label.len(), 0)
210         } else {
211             (0, found_label.len() - expected_label.len())
212         };
213         let mut msg: Vec<_> =
214             vec![(format!("{}{} `", " ".repeat(expected_padding), expected_label), Style::NoStyle)];
215         msg.extend(expected.0.iter().map(|x| match *x {
216             StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
217             StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
218         }));
219         msg.push((format!("`{}\n", expected_extra), Style::NoStyle));
220         msg.push((format!("{}{} `", " ".repeat(found_padding), found_label), Style::NoStyle));
221         msg.extend(found.0.iter().map(|x| match *x {
222             StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
223             StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
224         }));
225         msg.push((format!("`{}", found_extra), Style::NoStyle));
226
227         // For now, just attach these as notes.
228         self.highlighted_note(msg);
229         self
230     }
231
232     pub fn note_trait_signature(&mut self, name: String, signature: String) -> &mut Self {
233         self.highlighted_note(vec![
234             (format!("`{}` from trait: `", name), Style::NoStyle),
235             (signature, Style::Highlight),
236             ("`".to_string(), Style::NoStyle),
237         ]);
238         self
239     }
240
241     pub fn note(&mut self, msg: &str) -> &mut Self {
242         self.sub(Level::Note, msg, MultiSpan::new(), None);
243         self
244     }
245
246     pub fn highlighted_note(&mut self, msg: Vec<(String, Style)>) -> &mut Self {
247         self.sub_with_highlights(Level::Note, msg, MultiSpan::new(), None);
248         self
249     }
250
251     /// Prints the span with a note above it.
252     pub fn span_note<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self {
253         self.sub(Level::Note, msg, sp.into(), None);
254         self
255     }
256
257     pub fn warn(&mut self, msg: &str) -> &mut Self {
258         self.sub(Level::Warning, msg, MultiSpan::new(), None);
259         self
260     }
261
262     /// Prints the span with a warn above it.
263     pub fn span_warn<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self {
264         self.sub(Level::Warning, msg, sp.into(), None);
265         self
266     }
267
268     pub fn help(&mut self, msg: &str) -> &mut Self {
269         self.sub(Level::Help, msg, MultiSpan::new(), None);
270         self
271     }
272
273     /// Prints the span with some help above it.
274     pub fn span_help<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self {
275         self.sub(Level::Help, msg, sp.into(), None);
276         self
277     }
278
279     pub fn multipart_suggestion(
280         &mut self,
281         msg: &str,
282         suggestion: Vec<(Span, String)>,
283         applicability: Applicability,
284     ) -> &mut Self {
285         self.suggestions.push(CodeSuggestion {
286             substitutions: vec![Substitution {
287                 parts: suggestion
288                     .into_iter()
289                     .map(|(span, snippet)| SubstitutionPart { snippet, span })
290                     .collect(),
291             }],
292             msg: msg.to_owned(),
293             style: SuggestionStyle::ShowCode,
294             applicability,
295         });
296         self
297     }
298
299     pub fn multipart_suggestions(
300         &mut self,
301         msg: &str,
302         suggestions: Vec<Vec<(Span, String)>>,
303         applicability: Applicability,
304     ) -> &mut Self {
305         self.suggestions.push(CodeSuggestion {
306             substitutions: suggestions
307                 .into_iter()
308                 .map(|suggestion| Substitution {
309                     parts: suggestion
310                         .into_iter()
311                         .map(|(span, snippet)| SubstitutionPart { snippet, span })
312                         .collect(),
313                 })
314                 .collect(),
315             msg: msg.to_owned(),
316             style: SuggestionStyle::ShowCode,
317             applicability,
318         });
319         self
320     }
321
322     /// Prints out a message with for a multipart suggestion without showing the suggested code.
323     ///
324     /// This is intended to be used for suggestions that are obvious in what the changes need to
325     /// be from the message, showing the span label inline would be visually unpleasant
326     /// (marginally overlapping spans or multiline spans) and showing the snippet window wouldn't
327     /// improve understandability.
328     pub fn tool_only_multipart_suggestion(
329         &mut self,
330         msg: &str,
331         suggestion: Vec<(Span, String)>,
332         applicability: Applicability,
333     ) -> &mut Self {
334         self.suggestions.push(CodeSuggestion {
335             substitutions: vec![Substitution {
336                 parts: suggestion
337                     .into_iter()
338                     .map(|(span, snippet)| SubstitutionPart { snippet, span })
339                     .collect(),
340             }],
341             msg: msg.to_owned(),
342             style: SuggestionStyle::CompletelyHidden,
343             applicability,
344         });
345         self
346     }
347
348     /// Prints out a message with a suggested edit of the code.
349     ///
350     /// In case of short messages and a simple suggestion, rustc displays it as a label:
351     ///
352     /// ```text
353     /// try adding parentheses: `(tup.0).1`
354     /// ```
355     ///
356     /// The message
357     ///
358     /// * should not end in any punctuation (a `:` is added automatically)
359     /// * should not be a question (avoid language like "did you mean")
360     /// * should not contain any phrases like "the following", "as shown", etc.
361     /// * may look like "to do xyz, use" or "to do xyz, use abc"
362     /// * may contain a name of a function, variable, or type, but not whole expressions
363     ///
364     /// See `CodeSuggestion` for more information.
365     pub fn span_suggestion(
366         &mut self,
367         sp: Span,
368         msg: &str,
369         suggestion: String,
370         applicability: Applicability,
371     ) -> &mut Self {
372         self.span_suggestion_with_style(
373             sp,
374             msg,
375             suggestion,
376             applicability,
377             SuggestionStyle::ShowCode,
378         );
379         self
380     }
381
382     pub fn span_suggestion_with_style(
383         &mut self,
384         sp: Span,
385         msg: &str,
386         suggestion: String,
387         applicability: Applicability,
388         style: SuggestionStyle,
389     ) -> &mut Self {
390         self.suggestions.push(CodeSuggestion {
391             substitutions: vec![Substitution {
392                 parts: vec![SubstitutionPart { snippet: suggestion, span: sp }],
393             }],
394             msg: msg.to_owned(),
395             style,
396             applicability,
397         });
398         self
399     }
400
401     pub fn span_suggestion_verbose(
402         &mut self,
403         sp: Span,
404         msg: &str,
405         suggestion: String,
406         applicability: Applicability,
407     ) -> &mut Self {
408         self.span_suggestion_with_style(
409             sp,
410             msg,
411             suggestion,
412             applicability,
413             SuggestionStyle::ShowAlways,
414         );
415         self
416     }
417
418     /// Prints out a message with multiple suggested edits of the code.
419     pub fn span_suggestions(
420         &mut self,
421         sp: Span,
422         msg: &str,
423         suggestions: impl Iterator<Item = String>,
424         applicability: Applicability,
425     ) -> &mut Self {
426         self.suggestions.push(CodeSuggestion {
427             substitutions: suggestions
428                 .map(|snippet| Substitution { parts: vec![SubstitutionPart { snippet, span: sp }] })
429                 .collect(),
430             msg: msg.to_owned(),
431             style: SuggestionStyle::ShowCode,
432             applicability,
433         });
434         self
435     }
436
437     /// Prints out a message with a suggested edit of the code. If the suggestion is presented
438     /// inline, it will only show the message and not the suggestion.
439     ///
440     /// See `CodeSuggestion` for more information.
441     pub fn span_suggestion_short(
442         &mut self,
443         sp: Span,
444         msg: &str,
445         suggestion: String,
446         applicability: Applicability,
447     ) -> &mut Self {
448         self.span_suggestion_with_style(
449             sp,
450             msg,
451             suggestion,
452             applicability,
453             SuggestionStyle::HideCodeInline,
454         );
455         self
456     }
457
458     /// Prints out a message with for a suggestion without showing the suggested code.
459     ///
460     /// This is intended to be used for suggestions that are obvious in what the changes need to
461     /// be from the message, showing the span label inline would be visually unpleasant
462     /// (marginally overlapping spans or multiline spans) and showing the snippet window wouldn't
463     /// improve understandability.
464     pub fn span_suggestion_hidden(
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::HideCodeAlways,
477         );
478         self
479     }
480
481     /// Adds a suggestion to the json output, but otherwise remains silent/undisplayed in the cli.
482     ///
483     /// This is intended to be used for suggestions that are *very* obvious in what the changes
484     /// need to be from the message, but we still want other tools to be able to apply them.
485     pub fn tool_only_span_suggestion(
486         &mut self,
487         sp: Span,
488         msg: &str,
489         suggestion: String,
490         applicability: Applicability,
491     ) -> &mut Self {
492         self.span_suggestion_with_style(
493             sp,
494             msg,
495             suggestion,
496             applicability,
497             SuggestionStyle::CompletelyHidden,
498         );
499         self
500     }
501
502     pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self {
503         self.span = sp.into();
504         if let Some(span) = self.span.primary_span() {
505             self.sort_span = span;
506         }
507         self
508     }
509
510     pub fn code(&mut self, s: DiagnosticId) -> &mut Self {
511         self.code = Some(s);
512         self
513     }
514
515     pub fn clear_code(&mut self) -> &mut Self {
516         self.code = None;
517         self
518     }
519
520     pub fn get_code(&self) -> Option<DiagnosticId> {
521         self.code.clone()
522     }
523
524     pub fn set_primary_message<M: Into<String>>(&mut self, msg: M) -> &mut Self {
525         self.message[0] = (msg.into(), Style::NoStyle);
526         self
527     }
528
529     pub fn message(&self) -> String {
530         self.message.iter().map(|i| i.0.as_str()).collect::<String>()
531     }
532
533     pub fn styled_message(&self) -> &Vec<(String, Style)> {
534         &self.message
535     }
536
537     /// Used by a lint. Copies over all details *but* the "main
538     /// message".
539     pub fn copy_details_not_message(&mut self, from: &Diagnostic) {
540         self.span = from.span.clone();
541         self.code = from.code.clone();
542         self.children.extend(from.children.iter().cloned())
543     }
544
545     /// Convenience function for internal use, clients should use one of the
546     /// public methods above.
547     pub fn sub(
548         &mut self,
549         level: Level,
550         message: &str,
551         span: MultiSpan,
552         render_span: Option<MultiSpan>,
553     ) {
554         let sub = SubDiagnostic {
555             level,
556             message: vec![(message.to_owned(), Style::NoStyle)],
557             span,
558             render_span,
559         };
560         self.children.push(sub);
561     }
562
563     /// Convenience function for internal use, clients should use one of the
564     /// public methods above.
565     fn sub_with_highlights(
566         &mut self,
567         level: Level,
568         message: Vec<(String, Style)>,
569         span: MultiSpan,
570         render_span: Option<MultiSpan>,
571     ) {
572         let sub = SubDiagnostic { level, message, span, render_span };
573         self.children.push(sub);
574     }
575 }
576
577 impl SubDiagnostic {
578     pub fn message(&self) -> String {
579         self.message.iter().map(|i| i.0.as_str()).collect::<String>()
580     }
581
582     pub fn styled_message(&self) -> &Vec<(String, Style)> {
583         &self.message
584     }
585 }