]> git.lizzy.rs Git - rust.git/blob - src/librustc_errors/diagnostic.rs
Rollup merge of #66587 - matthewjasper:handle-static-as-const, r=oli-obk
[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(
158         &mut self,
159         expected_label: &dyn fmt::Display,
160         expected: DiagnosticStyledString,
161         found_label: &dyn fmt::Display,
162         found: DiagnosticStyledString,
163     ) -> &mut Self {
164         self.note_expected_found_extra(expected_label, expected, found_label, found, &"", &"")
165     }
166
167     pub fn note_unsuccessfull_coercion(
168         &mut self,
169         expected: DiagnosticStyledString,
170         found: DiagnosticStyledString,
171     ) -> &mut Self {
172         let mut msg: Vec<_> =
173             vec![(format!("required when trying to coerce from type `"),
174                   Style::NoStyle)];
175         msg.extend(expected.0.iter()
176                    .map(|x| match *x {
177                        StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
178                        StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
179                    }));
180         msg.push((format!("` to type '"), Style::NoStyle));
181         msg.extend(found.0.iter()
182                    .map(|x| match *x {
183                        StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
184                        StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
185                    }));
186         msg.push((format!("`"), Style::NoStyle));
187
188         // For now, just attach these as notes
189         self.highlighted_note(msg);
190         self
191     }
192
193     pub fn note_expected_found_extra(
194         &mut self,
195         expected_label: &dyn fmt::Display,
196         expected: DiagnosticStyledString,
197         found_label: &dyn fmt::Display,
198         found: DiagnosticStyledString,
199         expected_extra: &dyn fmt::Display,
200         found_extra: &dyn fmt::Display,
201     ) -> &mut Self {
202         let expected_label = format!("expected {}", expected_label);
203         let found_label = format!("found {}", found_label);
204         let (found_padding, expected_padding) = if expected_label.len() > found_label.len() {
205             (expected_label.len() - found_label.len(), 0)
206         } else {
207             (0, found_label.len() - expected_label.len())
208         };
209         let mut msg: Vec<_> = vec![(
210             format!("{}{} `", " ".repeat(expected_padding), expected_label),
211             Style::NoStyle,
212         )];
213         msg.extend(expected.0.iter()
214             .map(|x| match *x {
215                 StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
216                 StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
217             }));
218         msg.push((format!("`{}\n", expected_extra), Style::NoStyle));
219         msg.push((format!("{}{} `", " ".repeat(found_padding), found_label), Style::NoStyle));
220         msg.extend(found.0.iter()
221             .map(|x| match *x {
222                 StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
223                 StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
224             }));
225         msg.push((format!("`{}", found_extra), Style::NoStyle));
226
227         // For now, just attach these as notes.
228         self.highlighted_note(msg);
229         self
230     }
231
232     pub fn note_trait_signature(&mut self, name: String, signature: String) -> &mut Self {
233         self.highlighted_note(vec![
234             (format!("`{}` from trait: `", name), Style::NoStyle),
235             (signature, Style::Highlight),
236             ("`".to_string(), Style::NoStyle)]);
237         self
238     }
239
240     pub fn note(&mut self, msg: &str) -> &mut Self {
241         self.sub(Level::Note, msg, MultiSpan::new(), None);
242         self
243     }
244
245     pub fn highlighted_note(&mut self, msg: Vec<(String, Style)>) -> &mut Self {
246         self.sub_with_highlights(Level::Note, msg, MultiSpan::new(), None);
247         self
248     }
249
250     /// Prints the span with a note above it.
251     pub fn span_note<S: Into<MultiSpan>>(&mut self,
252                                          sp: S,
253                                          msg: &str)
254                                          -> &mut Self {
255         self.sub(Level::Note, msg, sp.into(), None);
256         self
257     }
258
259     pub fn warn(&mut self, msg: &str) -> &mut Self {
260         self.sub(Level::Warning, msg, MultiSpan::new(), None);
261         self
262     }
263
264     /// Prints the span with a warn above it.
265     pub fn span_warn<S: Into<MultiSpan>>(&mut self,
266                                          sp: S,
267                                          msg: &str)
268                                          -> &mut Self {
269         self.sub(Level::Warning, msg, sp.into(), None);
270         self
271     }
272
273     pub fn help(&mut self , msg: &str) -> &mut Self {
274         self.sub(Level::Help, msg, MultiSpan::new(), None);
275         self
276     }
277
278     /// Prints the span with some help above it.
279     pub fn span_help<S: Into<MultiSpan>>(&mut self,
280                                          sp: S,
281                                          msg: &str)
282                                          -> &mut Self {
283         self.sub(Level::Help, msg, sp.into(), None);
284         self
285     }
286
287     pub fn multipart_suggestion(
288         &mut self,
289         msg: &str,
290         suggestion: Vec<(Span, String)>,
291         applicability: Applicability,
292     ) -> &mut Self {
293         self.suggestions.push(CodeSuggestion {
294             substitutions: vec![Substitution {
295                 parts: suggestion
296                     .into_iter()
297                     .map(|(span, snippet)| SubstitutionPart { snippet, span })
298                     .collect(),
299             }],
300             msg: msg.to_owned(),
301             style: SuggestionStyle::ShowCode,
302             applicability,
303         });
304         self
305     }
306
307     /// Prints out a message with for a multipart suggestion without showing the suggested code.
308     ///
309     /// This is intended to be used for suggestions that are obvious in what the changes need to
310     /// be from the message, showing the span label inline would be visually unpleasant
311     /// (marginally overlapping spans or multiline spans) and showing the snippet window wouldn't
312     /// improve understandability.
313     pub fn tool_only_multipart_suggestion(
314         &mut self,
315         msg: &str,
316         suggestion: Vec<(Span, String)>,
317         applicability: Applicability,
318     ) -> &mut Self {
319         self.suggestions.push(CodeSuggestion {
320             substitutions: vec![Substitution {
321                 parts: suggestion
322                     .into_iter()
323                     .map(|(span, snippet)| SubstitutionPart { snippet, span })
324                     .collect(),
325             }],
326             msg: msg.to_owned(),
327             style: SuggestionStyle::CompletelyHidden,
328             applicability,
329         });
330         self
331     }
332
333     /// Prints out a message with a suggested edit of the code.
334     ///
335     /// In case of short messages and a simple suggestion, rustc displays it as a label:
336     ///
337     /// ```text
338     /// try adding parentheses: `(tup.0).1`
339     /// ```
340     ///
341     /// The message
342     ///
343     /// * should not end in any punctuation (a `:` is added automatically)
344     /// * should not be a question (avoid language like "did you mean")
345     /// * should not contain any phrases like "the following", "as shown", etc.
346     /// * may look like "to do xyz, use" or "to do xyz, use abc"
347     /// * may contain a name of a function, variable, or type, but not whole expressions
348     ///
349     /// See `CodeSuggestion` for more information.
350     pub fn span_suggestion(
351         &mut self,
352         sp: Span,
353         msg: &str,
354         suggestion: String,
355         applicability: Applicability,
356     ) -> &mut Self {
357         self.span_suggestion_with_style(
358             sp,
359             msg,
360             suggestion,
361             applicability,
362             SuggestionStyle::ShowCode,
363         );
364         self
365     }
366
367     pub fn span_suggestion_with_style(
368         &mut self,
369         sp: Span,
370         msg: &str,
371         suggestion: String,
372         applicability: Applicability,
373         style: SuggestionStyle,
374     ) -> &mut Self {
375         self.suggestions.push(CodeSuggestion {
376             substitutions: vec![Substitution {
377                 parts: vec![SubstitutionPart {
378                     snippet: suggestion,
379                     span: sp,
380                 }],
381             }],
382             msg: msg.to_owned(),
383             style,
384             applicability,
385         });
386         self
387     }
388
389     pub fn span_suggestion_verbose(
390         &mut self,
391         sp: Span,
392         msg: &str,
393         suggestion: String,
394         applicability: Applicability,
395     ) -> &mut Self {
396         self.span_suggestion_with_style(
397             sp,
398             msg,
399             suggestion,
400             applicability,
401             SuggestionStyle::ShowAlways,
402         );
403         self
404     }
405
406     /// Prints out a message with multiple suggested edits of the code.
407     pub fn span_suggestions(
408         &mut self,
409         sp: Span,
410         msg: &str,
411         suggestions: impl Iterator<Item = String>,
412         applicability: Applicability,
413     ) -> &mut Self {
414         self.suggestions.push(CodeSuggestion {
415             substitutions: suggestions.map(|snippet| Substitution {
416                 parts: vec![SubstitutionPart {
417                     snippet,
418                     span: sp,
419                 }],
420             }).collect(),
421             msg: msg.to_owned(),
422             style: SuggestionStyle::ShowCode,
423             applicability,
424         });
425         self
426     }
427
428     /// Prints out a message with a suggested edit of the code. If the suggestion is presented
429     /// inline, it will only show the message and not the suggestion.
430     ///
431     /// See `CodeSuggestion` for more information.
432     pub fn span_suggestion_short(
433         &mut self, sp: Span, msg: &str, suggestion: String, applicability: Applicability
434     ) -> &mut Self {
435         self.span_suggestion_with_style(
436             sp,
437             msg,
438             suggestion,
439             applicability,
440             SuggestionStyle::HideCodeInline,
441         );
442         self
443     }
444
445     /// Prints out a message with for a suggestion without showing the suggested code.
446     ///
447     /// This is intended to be used for suggestions that are obvious in what the changes need to
448     /// be from the message, showing the span label inline would be visually unpleasant
449     /// (marginally overlapping spans or multiline spans) and showing the snippet window wouldn't
450     /// improve understandability.
451     pub fn span_suggestion_hidden(
452         &mut self, sp: Span, msg: &str, suggestion: String, applicability: Applicability
453     ) -> &mut Self {
454         self.span_suggestion_with_style(
455             sp,
456             msg,
457             suggestion,
458             applicability,
459             SuggestionStyle::HideCodeAlways,
460         );
461         self
462     }
463
464     /// Adds a suggestion to the json output, but otherwise remains silent/undisplayed in the cli.
465     ///
466     /// This is intended to be used for suggestions that are *very* obvious in what the changes
467     /// need to be from the message, but we still want other tools to be able to apply them.
468     pub fn tool_only_span_suggestion(
469         &mut self, sp: Span, msg: &str, suggestion: String, applicability: Applicability
470     ) -> &mut Self {
471         self.span_suggestion_with_style(
472             sp,
473             msg,
474             suggestion,
475             applicability,
476             SuggestionStyle::CompletelyHidden,
477         );
478         self
479     }
480
481     pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self {
482         self.span = sp.into();
483         if let Some(span) = self.span.primary_span() {
484             self.sort_span = span;
485         }
486         self
487     }
488
489     pub fn code(&mut self, s: DiagnosticId) -> &mut Self {
490         self.code = Some(s);
491         self
492     }
493
494     pub fn get_code(&self) -> Option<DiagnosticId> {
495         self.code.clone()
496     }
497
498     pub fn message(&self) -> String {
499         self.message.iter().map(|i| i.0.as_str()).collect::<String>()
500     }
501
502     pub fn styled_message(&self) -> &Vec<(String, Style)> {
503         &self.message
504     }
505
506     /// Used by a lint. Copies over all details *but* the "main
507     /// message".
508     pub fn copy_details_not_message(&mut self, from: &Diagnostic) {
509         self.span = from.span.clone();
510         self.code = from.code.clone();
511         self.children.extend(from.children.iter().cloned())
512     }
513
514     /// Convenience function for internal use, clients should use one of the
515     /// public methods above.
516     pub fn sub(&mut self,
517            level: Level,
518            message: &str,
519            span: MultiSpan,
520            render_span: Option<MultiSpan>) {
521         let sub = SubDiagnostic {
522             level,
523             message: vec![(message.to_owned(), Style::NoStyle)],
524             span,
525             render_span,
526         };
527         self.children.push(sub);
528     }
529
530     /// Convenience function for internal use, clients should use one of the
531     /// public methods above.
532     fn sub_with_highlights(&mut self,
533                            level: Level,
534                            message: Vec<(String, Style)>,
535                            span: MultiSpan,
536                            render_span: Option<MultiSpan>) {
537         let sub = SubDiagnostic {
538             level,
539             message,
540             span,
541             render_span,
542         };
543         self.children.push(sub);
544     }
545 }
546
547 impl SubDiagnostic {
548     pub fn message(&self) -> String {
549         self.message.iter().map(|i| i.0.as_str()).collect::<String>()
550     }
551
552     pub fn styled_message(&self) -> &Vec<(String, Style)> {
553         &self.message
554     }
555 }