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