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