]> git.lizzy.rs Git - rust.git/blob - src/librustc_errors/diagnostic.rs
Add hooks for Miri panic unwinding
[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(Debug, 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(Debug, 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_unsuccessfull_coercion(&mut self,
156                                        expected: DiagnosticStyledString,
157                                        found: DiagnosticStyledString)
158                                        -> &mut Self
159     {
160         let mut msg: Vec<_> =
161             vec![(format!("required when trying to coerce from type `"),
162                   Style::NoStyle)];
163         msg.extend(expected.0.iter()
164                    .map(|x| match *x {
165                        StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
166                        StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
167                    }));
168         msg.push((format!("` to type '"), Style::NoStyle));
169         msg.extend(found.0.iter()
170                    .map(|x| match *x {
171                        StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
172                        StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
173                    }));
174         msg.push((format!("`"), Style::NoStyle));
175
176         // For now, just attach these as notes
177         self.highlighted_note(msg);
178         self
179     }
180
181     pub fn note_expected_found_extra(&mut self,
182                                      label: &dyn fmt::Display,
183                                      expected: DiagnosticStyledString,
184                                      found: DiagnosticStyledString,
185                                      expected_extra: &dyn fmt::Display,
186                                      found_extra: &dyn fmt::Display)
187                                      -> &mut Self
188     {
189         let mut msg: Vec<_> = vec![(format!("expected {} `", label), Style::NoStyle)];
190         msg.extend(expected.0.iter()
191                    .map(|x| match *x {
192                        StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
193                        StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
194                    }));
195         msg.push((format!("`{}\n", expected_extra), Style::NoStyle));
196         msg.push((format!("   found {} `", label), Style::NoStyle));
197         msg.extend(found.0.iter()
198                    .map(|x| match *x {
199                        StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
200                        StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
201                    }));
202         msg.push((format!("`{}", found_extra), Style::NoStyle));
203
204         // For now, just attach these as notes
205         self.highlighted_note(msg);
206         self
207     }
208
209     pub fn note_trait_signature(&mut self, name: String, signature: String) -> &mut Self {
210         self.highlighted_note(vec![
211             (format!("`{}` from trait: `", name), Style::NoStyle),
212             (signature, Style::Highlight),
213             ("`".to_string(), Style::NoStyle)]);
214         self
215     }
216
217     pub fn note(&mut self, msg: &str) -> &mut Self {
218         self.sub(Level::Note, msg, MultiSpan::new(), None);
219         self
220     }
221
222     pub fn highlighted_note(&mut self, msg: Vec<(String, Style)>) -> &mut Self {
223         self.sub_with_highlights(Level::Note, msg, MultiSpan::new(), None);
224         self
225     }
226
227     /// Prints the span with a note above it.
228     pub fn span_note<S: Into<MultiSpan>>(&mut self,
229                                          sp: S,
230                                          msg: &str)
231                                          -> &mut Self {
232         self.sub(Level::Note, msg, sp.into(), None);
233         self
234     }
235
236     pub fn warn(&mut self, msg: &str) -> &mut Self {
237         self.sub(Level::Warning, msg, MultiSpan::new(), None);
238         self
239     }
240
241     /// Prints the span with a warn above it.
242     pub fn span_warn<S: Into<MultiSpan>>(&mut self,
243                                          sp: S,
244                                          msg: &str)
245                                          -> &mut Self {
246         self.sub(Level::Warning, msg, sp.into(), None);
247         self
248     }
249
250     pub fn help(&mut self , msg: &str) -> &mut Self {
251         self.sub(Level::Help, msg, MultiSpan::new(), None);
252         self
253     }
254
255     /// Prints the span with some help above it.
256     pub fn span_help<S: Into<MultiSpan>>(&mut self,
257                                          sp: S,
258                                          msg: &str)
259                                          -> &mut Self {
260         self.sub(Level::Help, msg, sp.into(), None);
261         self
262     }
263
264     pub fn 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::ShowCode,
279             applicability,
280         });
281         self
282     }
283
284     /// Prints out a message with for a multipart suggestion without showing the suggested code.
285     ///
286     /// This is intended to be used for suggestions that are obvious in what the changes need to
287     /// be from the message, showing the span label inline would be visually unpleasant
288     /// (marginally overlapping spans or multiline spans) and showing the snippet window wouldn't
289     /// improve understandability.
290     pub fn tool_only_multipart_suggestion(
291         &mut self,
292         msg: &str,
293         suggestion: Vec<(Span, String)>,
294         applicability: Applicability,
295     ) -> &mut Self {
296         self.suggestions.push(CodeSuggestion {
297             substitutions: vec![Substitution {
298                 parts: suggestion
299                     .into_iter()
300                     .map(|(span, snippet)| SubstitutionPart { snippet, span })
301                     .collect(),
302             }],
303             msg: msg.to_owned(),
304             style: SuggestionStyle::CompletelyHidden,
305             applicability,
306         });
307         self
308     }
309
310     /// Prints out a message with a suggested edit of the code.
311     ///
312     /// In case of short messages and a simple suggestion, rustc displays it as a label:
313     ///
314     /// ```text
315     /// try adding parentheses: `(tup.0).1`
316     /// ```
317     ///
318     /// The message
319     ///
320     /// * should not end in any punctuation (a `:` is added automatically)
321     /// * should not be a question (avoid language like "did you mean")
322     /// * should not contain any phrases like "the following", "as shown", etc.
323     /// * may look like "to do xyz, use" or "to do xyz, use abc"
324     /// * may contain a name of a function, variable, or type, but not whole expressions
325     ///
326     /// See `CodeSuggestion` for more information.
327     pub fn span_suggestion(
328         &mut self,
329         sp: Span,
330         msg: &str,
331         suggestion: String,
332         applicability: Applicability,
333     ) -> &mut Self {
334         self.span_suggestion_with_style(
335             sp,
336             msg,
337             suggestion,
338             applicability,
339             SuggestionStyle::ShowCode,
340         );
341         self
342     }
343
344     pub fn span_suggestion_with_style(
345         &mut self,
346         sp: Span,
347         msg: &str,
348         suggestion: String,
349         applicability: Applicability,
350         style: SuggestionStyle,
351     ) -> &mut Self {
352         self.suggestions.push(CodeSuggestion {
353             substitutions: vec![Substitution {
354                 parts: vec![SubstitutionPart {
355                     snippet: suggestion,
356                     span: sp,
357                 }],
358             }],
359             msg: msg.to_owned(),
360             style,
361             applicability,
362         });
363         self
364     }
365
366     pub fn span_suggestion_verbose(
367         &mut self,
368         sp: Span,
369         msg: &str,
370         suggestion: String,
371         applicability: Applicability,
372     ) -> &mut Self {
373         self.span_suggestion_with_style(
374             sp,
375             msg,
376             suggestion,
377             applicability,
378             SuggestionStyle::ShowAlways,
379         );
380         self
381     }
382
383     /// Prints out a message with multiple suggested edits of the code.
384     pub fn span_suggestions(
385         &mut self,
386         sp: Span,
387         msg: &str,
388         suggestions: impl Iterator<Item = String>,
389         applicability: Applicability,
390     ) -> &mut Self {
391         self.suggestions.push(CodeSuggestion {
392             substitutions: suggestions.map(|snippet| Substitution {
393                 parts: vec![SubstitutionPart {
394                     snippet,
395                     span: sp,
396                 }],
397             }).collect(),
398             msg: msg.to_owned(),
399             style: SuggestionStyle::ShowCode,
400             applicability,
401         });
402         self
403     }
404
405     /// Prints out a message with a suggested edit of the code. If the suggestion is presented
406     /// inline, it will only show the message and not the suggestion.
407     ///
408     /// See `CodeSuggestion` for more information.
409     pub fn span_suggestion_short(
410         &mut self, sp: Span, msg: &str, suggestion: String, applicability: Applicability
411     ) -> &mut Self {
412         self.span_suggestion_with_style(
413             sp,
414             msg,
415             suggestion,
416             applicability,
417             SuggestionStyle::HideCodeInline,
418         );
419         self
420     }
421
422     /// Prints out a message with for a suggestion without showing the suggested code.
423     ///
424     /// This is intended to be used for suggestions that are obvious in what the changes need to
425     /// be from the message, showing the span label inline would be visually unpleasant
426     /// (marginally overlapping spans or multiline spans) and showing the snippet window wouldn't
427     /// improve understandability.
428     pub fn span_suggestion_hidden(
429         &mut self, sp: Span, msg: &str, suggestion: String, applicability: Applicability
430     ) -> &mut Self {
431         self.span_suggestion_with_style(
432             sp,
433             msg,
434             suggestion,
435             applicability,
436             SuggestionStyle::HideCodeAlways,
437         );
438         self
439     }
440
441     /// Adds a suggestion to the json output, but otherwise remains silent/undisplayed in the cli.
442     ///
443     /// This is intended to be used for suggestions that are *very* obvious in what the changes
444     /// need to be from the message, but we still want other tools to be able to apply them.
445     pub fn tool_only_span_suggestion(
446         &mut self, sp: Span, msg: &str, suggestion: String, applicability: Applicability
447     ) -> &mut Self {
448         self.span_suggestion_with_style(
449             sp,
450             msg,
451             suggestion,
452             applicability,
453             SuggestionStyle::CompletelyHidden,
454         );
455         self
456     }
457
458     pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self {
459         self.span = sp.into();
460         self
461     }
462
463     pub fn code(&mut self, s: DiagnosticId) -> &mut Self {
464         self.code = Some(s);
465         self
466     }
467
468     pub fn get_code(&self) -> Option<DiagnosticId> {
469         self.code.clone()
470     }
471
472     pub fn message(&self) -> String {
473         self.message.iter().map(|i| i.0.as_str()).collect::<String>()
474     }
475
476     pub fn styled_message(&self) -> &Vec<(String, Style)> {
477         &self.message
478     }
479
480     /// Used by a lint. Copies over all details *but* the "main
481     /// message".
482     pub fn copy_details_not_message(&mut self, from: &Diagnostic) {
483         self.span = from.span.clone();
484         self.code = from.code.clone();
485         self.children.extend(from.children.iter().cloned())
486     }
487
488     /// Convenience function for internal use, clients should use one of the
489     /// public methods above.
490     pub fn sub(&mut self,
491            level: Level,
492            message: &str,
493            span: MultiSpan,
494            render_span: Option<MultiSpan>) {
495         let sub = SubDiagnostic {
496             level,
497             message: vec![(message.to_owned(), Style::NoStyle)],
498             span,
499             render_span,
500         };
501         self.children.push(sub);
502     }
503
504     /// Convenience function for internal use, clients should use one of the
505     /// public methods above.
506     fn sub_with_highlights(&mut self,
507                            level: Level,
508                            message: Vec<(String, Style)>,
509                            span: MultiSpan,
510                            render_span: Option<MultiSpan>) {
511         let sub = SubDiagnostic {
512             level,
513             message,
514             span,
515             render_span,
516         };
517         self.children.push(sub);
518     }
519 }
520
521 impl SubDiagnostic {
522     pub fn message(&self) -> String {
523         self.message.iter().map(|i| i.0.as_str()).collect::<String>()
524     }
525
526     pub fn styled_message(&self) -> &Vec<(String, Style)> {
527         &self.message
528     }
529 }