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