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