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