]> git.lizzy.rs Git - rust.git/blob - src/librustc_errors/diagnostic.rs
de73295b499fa3866e1c463a723235c8ba2c6fc9
[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) -> String {
80         match self {
81             &StringPart::Normal(ref s) | & StringPart::Highlighted(ref s) => s.to_owned()
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     /// Cancel the diagnostic (a structured diagnostic must either be emitted or
103     /// canceled or it will panic when dropped).
104     pub fn cancel(&mut self) {
105         self.level = Level::Cancelled;
106     }
107
108     pub fn cancelled(&self) -> bool {
109         self.level == Level::Cancelled
110     }
111
112     /// Add a span/label to be included in the resulting snippet.
113     /// This is pushed onto the `MultiSpan` that was created when the
114     /// diagnostic was first built. If you don't call this function at
115     /// all, and you just supplied a `Span` to create the diagnostic,
116     /// then the snippet will just include that `Span`, which is
117     /// called the primary span.
118     pub fn span_label<T: Into<String>>(&mut self, span: Span, label: T) -> &mut Self {
119         self.span.push_span_label(span, label.into());
120         self
121     }
122
123     pub fn note_expected_found(&mut self,
124                                label: &fmt::Display,
125                                expected: DiagnosticStyledString,
126                                found: DiagnosticStyledString)
127                                -> &mut Self
128     {
129         self.note_expected_found_extra(label, expected, found, &"", &"")
130     }
131
132     pub fn note_expected_found_extra(&mut self,
133                                      label: &fmt::Display,
134                                      expected: DiagnosticStyledString,
135                                      found: DiagnosticStyledString,
136                                      expected_extra: &fmt::Display,
137                                      found_extra: &fmt::Display)
138                                      -> &mut Self
139     {
140         let mut msg: Vec<_> = vec![(format!("expected {} `", label), Style::NoStyle)];
141         msg.extend(expected.0.iter()
142                    .map(|x| match *x {
143                        StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
144                        StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
145                    }));
146         msg.push((format!("`{}\n", expected_extra), Style::NoStyle));
147         msg.push((format!("   found {} `", label), Style::NoStyle));
148         msg.extend(found.0.iter()
149                    .map(|x| match *x {
150                        StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
151                        StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
152                    }));
153         msg.push((format!("`{}", found_extra), Style::NoStyle));
154
155         // For now, just attach these as notes
156         self.highlighted_note(msg);
157         self
158     }
159
160     pub fn note_trait_signature(&mut self, name: String, signature: String) -> &mut Self {
161         self.highlighted_note(vec![
162             (format!("`{}` from trait: `", name), Style::NoStyle),
163             (signature, Style::Highlight),
164             ("`".to_string(), Style::NoStyle)]);
165         self
166     }
167
168     pub fn note(&mut self, msg: &str) -> &mut Self {
169         self.sub(Level::Note, msg, MultiSpan::new(), None);
170         self
171     }
172
173     pub fn highlighted_note(&mut self, msg: Vec<(String, Style)>) -> &mut Self {
174         self.sub_with_highlights(Level::Note, msg, MultiSpan::new(), None);
175         self
176     }
177
178     pub fn span_note<S: Into<MultiSpan>>(&mut self,
179                                          sp: S,
180                                          msg: &str)
181                                          -> &mut Self {
182         self.sub(Level::Note, msg, sp.into(), None);
183         self
184     }
185
186     pub fn warn(&mut self, msg: &str) -> &mut Self {
187         self.sub(Level::Warning, msg, MultiSpan::new(), None);
188         self
189     }
190
191     pub fn span_warn<S: Into<MultiSpan>>(&mut self,
192                                          sp: S,
193                                          msg: &str)
194                                          -> &mut Self {
195         self.sub(Level::Warning, msg, sp.into(), None);
196         self
197     }
198
199     pub fn help(&mut self , msg: &str) -> &mut Self {
200         self.sub(Level::Help, msg, MultiSpan::new(), None);
201         self
202     }
203
204     pub fn span_help<S: Into<MultiSpan>>(&mut self,
205                                          sp: S,
206                                          msg: &str)
207                                          -> &mut Self {
208         self.sub(Level::Help, msg, sp.into(), None);
209         self
210     }
211
212     /// Prints out a message with a suggested edit of the code. If the suggestion is presented
213     /// inline it will only show the text message and not the text.
214     ///
215     /// See `CodeSuggestion` for more information.
216     pub fn span_suggestion_short(&mut self, sp: Span, msg: &str, suggestion: String) -> &mut Self {
217         self.suggestions.push(CodeSuggestion {
218             substitutions: vec![Substitution {
219                 parts: vec![SubstitutionPart {
220                     snippet: suggestion,
221                     span: sp,
222                 }],
223             }],
224             msg: msg.to_owned(),
225             show_code_when_inline: false,
226             applicability: Applicability::Unspecified,
227         });
228         self
229     }
230
231     /// Prints out a message with a suggested edit of the code.
232     ///
233     /// In case of short messages and a simple suggestion,
234     /// rustc displays it as a label like
235     ///
236     /// "try adding parentheses: `(tup.0).1`"
237     ///
238     /// The message
239     ///
240     /// * should not end in any punctuation (a `:` is added automatically)
241     /// * should not be a question
242     /// * should not contain any parts like "the following", "as shown"
243     /// * may look like "to do xyz, use" or "to do xyz, use abc"
244     /// * may contain a name of a function, variable or type, but not whole expressions
245     ///
246     /// See `CodeSuggestion` for more information.
247     pub fn span_suggestion(&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: true,
257             applicability: Applicability::Unspecified,
258         });
259         self
260     }
261
262     pub fn multipart_suggestion(
263         &mut self,
264         msg: &str,
265         suggestion: Vec<(Span, String)>,
266     ) -> &mut Self {
267         self.suggestions.push(CodeSuggestion {
268             substitutions: vec![Substitution {
269                 parts: suggestion
270                     .into_iter()
271                     .map(|(span, snippet)| SubstitutionPart { snippet, span })
272                     .collect(),
273             }],
274             msg: msg.to_owned(),
275             show_code_when_inline: true,
276             applicability: Applicability::Unspecified,
277         });
278         self
279     }
280
281     /// Prints out a message with multiple suggested edits of the code.
282     pub fn span_suggestions(&mut self, sp: Span, msg: &str, suggestions: Vec<String>) -> &mut Self {
283         self.suggestions.push(CodeSuggestion {
284             substitutions: suggestions.into_iter().map(|snippet| Substitution {
285                 parts: vec![SubstitutionPart {
286                     snippet,
287                     span: sp,
288                 }],
289             }).collect(),
290             msg: msg.to_owned(),
291             show_code_when_inline: true,
292             applicability: Applicability::Unspecified,
293         });
294         self
295     }
296
297     /// This is a suggestion that may contain mistakes or fillers and should
298     /// be read and understood by a human.
299     pub fn span_suggestion_with_applicability(&mut self, sp: Span, msg: &str,
300                                        suggestion: String,
301                                        applicability: Applicability) -> &mut Self {
302         self.suggestions.push(CodeSuggestion {
303             substitutions: vec![Substitution {
304                 parts: vec![SubstitutionPart {
305                     snippet: suggestion,
306                     span: sp,
307                 }],
308             }],
309             msg: msg.to_owned(),
310             show_code_when_inline: true,
311             applicability,
312         });
313         self
314     }
315
316     pub fn span_suggestions_with_applicability(&mut self, sp: Span, msg: &str,
317                                         suggestions: Vec<String>,
318                                         applicability: Applicability) -> &mut Self {
319         self.suggestions.push(CodeSuggestion {
320             substitutions: suggestions.into_iter().map(|snippet| Substitution {
321                 parts: vec![SubstitutionPart {
322                     snippet,
323                     span: sp,
324                 }],
325             }).collect(),
326             msg: msg.to_owned(),
327             show_code_when_inline: true,
328             applicability,
329         });
330         self
331     }
332
333     pub fn span_suggestion_short_with_applicability(
334         &mut self, sp: Span, msg: &str, suggestion: String, applicability: Applicability
335     ) -> &mut Self {
336         self.suggestions.push(CodeSuggestion {
337             substitutions: vec![Substitution {
338                 parts: vec![SubstitutionPart {
339                     snippet: suggestion,
340                     span: sp,
341                 }],
342             }],
343             msg: msg.to_owned(),
344             show_code_when_inline: false,
345             applicability: applicability,
346         });
347         self
348     }
349
350     pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self {
351         self.span = sp.into();
352         self
353     }
354
355     pub fn code(&mut self, s: DiagnosticId) -> &mut Self {
356         self.code = Some(s);
357         self
358     }
359
360     pub fn get_code(&self) -> Option<DiagnosticId> {
361         self.code.clone()
362     }
363
364     pub fn message(&self) -> String {
365         self.message.iter().map(|i| i.0.to_owned()).collect::<String>()
366     }
367
368     pub fn styled_message(&self) -> &Vec<(String, Style)> {
369         &self.message
370     }
371
372     /// Used by a lint. Copies over all details *but* the "main
373     /// message".
374     pub fn copy_details_not_message(&mut self, from: &Diagnostic) {
375         self.span = from.span.clone();
376         self.code = from.code.clone();
377         self.children.extend(from.children.iter().cloned())
378     }
379
380     /// Convenience function for internal use, clients should use one of the
381     /// public methods above.
382     pub(crate) fn sub(&mut self,
383            level: Level,
384            message: &str,
385            span: MultiSpan,
386            render_span: Option<MultiSpan>) {
387         let sub = SubDiagnostic {
388             level,
389             message: vec![(message.to_owned(), Style::NoStyle)],
390             span,
391             render_span,
392         };
393         self.children.push(sub);
394     }
395
396     /// Convenience function for internal use, clients should use one of the
397     /// public methods above.
398     fn sub_with_highlights(&mut self,
399                            level: Level,
400                            message: Vec<(String, Style)>,
401                            span: MultiSpan,
402                            render_span: Option<MultiSpan>) {
403         let sub = SubDiagnostic {
404             level,
405             message,
406             span,
407             render_span,
408         };
409         self.children.push(sub);
410     }
411 }
412
413 impl SubDiagnostic {
414     pub fn message(&self) -> String {
415         self.message.iter().map(|i| i.0.to_owned()).collect::<String>()
416     }
417
418     pub fn styled_message(&self) -> &Vec<(String, Style)> {
419         &self.message
420     }
421 }