]> git.lizzy.rs Git - rust.git/blob - src/librustc_errors/diagnostic.rs
Rollup merge of #58203 - euclio:rustdoc-async, r=GuillaumeGomez
[rust.git] / src / librustc_errors / diagnostic.rs
1 use crate::CodeSuggestion;
2 use crate::SubstitutionPart;
3 use crate::Substitution;
4 use crate::Applicability;
5 use crate::Level;
6 use crate::snippet::Style;
7 use std::fmt;
8 use syntax_pos::{MultiSpan, Span};
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     pub fn multipart_suggestion(
233         &mut self,
234         msg: &str,
235         suggestion: Vec<(Span, String)>,
236         applicability: Applicability,
237     ) -> &mut Self {
238         self.suggestions.push(CodeSuggestion {
239             substitutions: vec![Substitution {
240                 parts: suggestion
241                     .into_iter()
242                     .map(|(span, snippet)| SubstitutionPart { snippet, span })
243                     .collect(),
244             }],
245             msg: msg.to_owned(),
246             show_code_when_inline: true,
247             applicability,
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, rustc displays it as a label:
255     ///
256     /// ```text
257     /// try adding parentheses: `(tup.0).1`
258     /// ```
259     ///
260     /// The message
261     ///
262     /// * should not end in any punctuation (a `:` is added automatically)
263     /// * should not be a question (avoid language like "did you mean")
264     /// * should not contain any phrases like "the following", "as shown", etc.
265     /// * may look like "to do xyz, use" or "to do xyz, use abc"
266     /// * may contain a name of a function, variable, or type, but not whole expressions
267     ///
268     /// See `CodeSuggestion` for more information.
269     pub fn span_suggestion(&mut self, sp: Span, msg: &str,
270                                        suggestion: String,
271                                        applicability: Applicability) -> &mut Self {
272         self.suggestions.push(CodeSuggestion {
273             substitutions: vec![Substitution {
274                 parts: vec![SubstitutionPart {
275                     snippet: suggestion,
276                     span: sp,
277                 }],
278             }],
279             msg: msg.to_owned(),
280             show_code_when_inline: true,
281             applicability,
282         });
283         self
284     }
285
286     /// Prints out a message with multiple suggested edits of the code.
287     pub fn span_suggestions(&mut self, sp: Span, msg: &str,
288         suggestions: impl Iterator<Item = String>, applicability: Applicability) -> &mut Self
289     {
290         self.suggestions.push(CodeSuggestion {
291             substitutions: suggestions.map(|snippet| Substitution {
292                 parts: vec![SubstitutionPart {
293                     snippet,
294                     span: sp,
295                 }],
296             }).collect(),
297             msg: msg.to_owned(),
298             show_code_when_inline: true,
299             applicability,
300         });
301         self
302     }
303
304     /// Prints out a message with a suggested edit of the code. If the suggestion is presented
305     /// inline, it will only show the message and not the suggestion.
306     ///
307     /// See `CodeSuggestion` for more information.
308     pub fn span_suggestion_short(
309         &mut self, sp: Span, msg: &str, suggestion: String, applicability: Applicability
310     ) -> &mut Self {
311         self.suggestions.push(CodeSuggestion {
312             substitutions: vec![Substitution {
313                 parts: vec![SubstitutionPart {
314                     snippet: suggestion,
315                     span: sp,
316                 }],
317             }],
318             msg: msg.to_owned(),
319             show_code_when_inline: false,
320             applicability: applicability,
321         });
322         self
323     }
324
325     pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self {
326         self.span = sp.into();
327         self
328     }
329
330     pub fn code(&mut self, s: DiagnosticId) -> &mut Self {
331         self.code = Some(s);
332         self
333     }
334
335     pub fn get_code(&self) -> Option<DiagnosticId> {
336         self.code.clone()
337     }
338
339     pub fn message(&self) -> String {
340         self.message.iter().map(|i| i.0.as_str()).collect::<String>()
341     }
342
343     pub fn styled_message(&self) -> &Vec<(String, Style)> {
344         &self.message
345     }
346
347     /// Used by a lint. Copies over all details *but* the "main
348     /// message".
349     pub fn copy_details_not_message(&mut self, from: &Diagnostic) {
350         self.span = from.span.clone();
351         self.code = from.code.clone();
352         self.children.extend(from.children.iter().cloned())
353     }
354
355     /// Convenience function for internal use, clients should use one of the
356     /// public methods above.
357     pub fn sub(&mut self,
358            level: Level,
359            message: &str,
360            span: MultiSpan,
361            render_span: Option<MultiSpan>) {
362         let sub = SubDiagnostic {
363             level,
364             message: vec![(message.to_owned(), Style::NoStyle)],
365             span,
366             render_span,
367         };
368         self.children.push(sub);
369     }
370
371     /// Convenience function for internal use, clients should use one of the
372     /// public methods above.
373     fn sub_with_highlights(&mut self,
374                            level: Level,
375                            message: Vec<(String, Style)>,
376                            span: MultiSpan,
377                            render_span: Option<MultiSpan>) {
378         let sub = SubDiagnostic {
379             level,
380             message,
381             span,
382             render_span,
383         };
384         self.children.push(sub);
385     }
386 }
387
388 impl SubDiagnostic {
389     pub fn message(&self) -> String {
390         self.message.iter().map(|i| i.0.as_str()).collect::<String>()
391     }
392
393     pub fn styled_message(&self) -> &Vec<(String, Style)> {
394         &self.message
395     }
396 }