]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_errors/src/diagnostic.rs
870f7b81e21aa734db26b63eaf26c46ad02ef158
[rust.git] / compiler / rustc_errors / src / diagnostic.rs
1 use crate::snippet::Style;
2 use crate::Applicability;
3 use crate::CodeSuggestion;
4 use crate::Level;
5 use crate::Substitution;
6 use crate::SubstitutionPart;
7 use crate::SuggestionStyle;
8 use rustc_span::{MultiSpan, Span, DUMMY_SP};
9 use std::fmt;
10
11 #[must_use]
12 #[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)]
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, Encodable, Decodable)]
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, Encodable, Decodable)]
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 push<S: Into<String>>(&mut self, t: S, highlight: bool) {
56         if highlight {
57             self.push_highlighted(t);
58         } else {
59             self.push_normal(t);
60         }
61     }
62     pub fn normal<S: Into<String>>(t: S) -> DiagnosticStyledString {
63         DiagnosticStyledString(vec![StringPart::Normal(t.into())])
64     }
65
66     pub fn highlighted<S: Into<String>>(t: S) -> DiagnosticStyledString {
67         DiagnosticStyledString(vec![StringPart::Highlighted(t.into())])
68     }
69
70     pub fn content(&self) -> String {
71         self.0.iter().map(|x| x.content()).collect::<String>()
72     }
73 }
74
75 #[derive(Debug, PartialEq, Eq)]
76 pub enum StringPart {
77     Normal(String),
78     Highlighted(String),
79 }
80
81 impl StringPart {
82     pub fn content(&self) -> &str {
83         match self {
84             &StringPart::Normal(ref s) | &StringPart::Highlighted(ref s) => s,
85         }
86     }
87 }
88
89 impl Diagnostic {
90     pub fn new(level: Level, message: &str) -> Self {
91         Diagnostic::new_with_code(level, None, message)
92     }
93
94     pub fn new_with_code(level: Level, code: Option<DiagnosticId>, message: &str) -> Self {
95         Diagnostic {
96             level,
97             message: vec![(message.to_owned(), Style::NoStyle)],
98             code,
99             span: MultiSpan::new(),
100             children: vec![],
101             suggestions: vec![],
102             sort_span: DUMMY_SP,
103         }
104     }
105
106     pub fn is_error(&self) -> bool {
107         match self.level {
108             Level::Bug | Level::Fatal | Level::Error | Level::FailureNote => true,
109
110             Level::Warning | Level::Note | Level::Help | Level::Cancelled => false,
111         }
112     }
113
114     /// Cancel the diagnostic (a structured diagnostic must either be emitted or
115     /// canceled or it will panic when dropped).
116     pub fn cancel(&mut self) {
117         self.level = Level::Cancelled;
118     }
119
120     pub fn cancelled(&self) -> bool {
121         self.level == Level::Cancelled
122     }
123
124     /// Set the sorting span.
125     pub fn set_sort_span(&mut self, sp: Span) {
126         self.sort_span = sp;
127     }
128
129     /// Adds a span/label to be included in the resulting snippet.
130     ///
131     /// This is pushed onto the [`MultiSpan`] that was created when the diagnostic
132     /// was first built. That means it will be shown together with the original
133     /// span/label, *not* a span added by one of the `span_{note,warn,help,suggestions}` methods.
134     ///
135     /// This span is *not* considered a ["primary span"][`MultiSpan`]; only
136     /// the `Span` supplied when creating the diagnostic is primary.
137     ///
138     /// [`MultiSpan`]: ../rustc_span/struct.MultiSpan.html
139     pub fn span_label<T: Into<String>>(&mut self, span: Span, label: T) -> &mut Self {
140         self.span.push_span_label(span, label.into());
141         self
142     }
143
144     pub fn replace_span_with(&mut self, after: Span) -> &mut Self {
145         let before = self.span.clone();
146         self.set_span(after);
147         for span_label in before.span_labels() {
148             if let Some(label) = span_label.label {
149                 self.span_label(after, label);
150             }
151         }
152         self
153     }
154
155     pub fn note_expected_found(
156         &mut self,
157         expected_label: &dyn fmt::Display,
158         expected: DiagnosticStyledString,
159         found_label: &dyn fmt::Display,
160         found: DiagnosticStyledString,
161     ) -> &mut Self {
162         self.note_expected_found_extra(expected_label, expected, found_label, found, &"", &"")
163     }
164
165     pub fn note_unsuccessfull_coercion(
166         &mut self,
167         expected: DiagnosticStyledString,
168         found: DiagnosticStyledString,
169     ) -> &mut Self {
170         let mut msg: Vec<_> =
171             vec![("required when trying to coerce from type `".to_string(), Style::NoStyle)];
172         msg.extend(expected.0.iter().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(("` to type '".to_string(), Style::NoStyle));
177         msg.extend(found.0.iter().map(|x| match *x {
178             StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
179             StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
180         }));
181         msg.push(("`".to_string(), Style::NoStyle));
182
183         // For now, just attach these as notes
184         self.highlighted_note(msg);
185         self
186     }
187
188     pub fn note_expected_found_extra(
189         &mut self,
190         expected_label: &dyn fmt::Display,
191         expected: DiagnosticStyledString,
192         found_label: &dyn fmt::Display,
193         found: DiagnosticStyledString,
194         expected_extra: &dyn fmt::Display,
195         found_extra: &dyn fmt::Display,
196     ) -> &mut Self {
197         let expected_label = expected_label.to_string();
198         let expected_label = if expected_label.is_empty() {
199             "expected".to_string()
200         } else {
201             format!("expected {}", expected_label)
202         };
203         let found_label = found_label.to_string();
204         let found_label = if found_label.is_empty() {
205             "found".to_string()
206         } else {
207             format!("found {}", found_label)
208         };
209         let (found_padding, expected_padding) = if expected_label.len() > found_label.len() {
210             (expected_label.len() - found_label.len(), 0)
211         } else {
212             (0, found_label.len() - expected_label.len())
213         };
214         let mut msg: Vec<_> =
215             vec![(format!("{}{} `", " ".repeat(expected_padding), expected_label), Style::NoStyle)];
216         msg.extend(expected.0.iter().map(|x| match *x {
217             StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
218             StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
219         }));
220         msg.push((format!("`{}\n", expected_extra), Style::NoStyle));
221         msg.push((format!("{}{} `", " ".repeat(found_padding), found_label), Style::NoStyle));
222         msg.extend(found.0.iter().map(|x| match *x {
223             StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
224             StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
225         }));
226         msg.push((format!("`{}", found_extra), Style::NoStyle));
227
228         // For now, just attach these as notes.
229         self.highlighted_note(msg);
230         self
231     }
232
233     pub fn note_trait_signature(&mut self, name: String, signature: String) -> &mut Self {
234         self.highlighted_note(vec![
235             (format!("`{}` from trait: `", name), Style::NoStyle),
236             (signature, Style::Highlight),
237             ("`".to_string(), Style::NoStyle),
238         ]);
239         self
240     }
241
242     pub fn note(&mut self, msg: &str) -> &mut Self {
243         self.sub(Level::Note, msg, MultiSpan::new(), None);
244         self
245     }
246
247     pub fn highlighted_note(&mut self, msg: Vec<(String, Style)>) -> &mut Self {
248         self.sub_with_highlights(Level::Note, msg, MultiSpan::new(), None);
249         self
250     }
251
252     /// Prints the span with a note above it.
253     pub fn span_note<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self {
254         self.sub(Level::Note, msg, sp.into(), None);
255         self
256     }
257
258     pub fn warn(&mut self, msg: &str) -> &mut Self {
259         self.sub(Level::Warning, msg, MultiSpan::new(), None);
260         self
261     }
262
263     /// Prints the span with a warn above it.
264     pub fn span_warn<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self {
265         self.sub(Level::Warning, msg, sp.into(), None);
266         self
267     }
268
269     pub fn help(&mut self, msg: &str) -> &mut Self {
270         self.sub(Level::Help, msg, MultiSpan::new(), None);
271         self
272     }
273
274     /// Prints the span with some help above it.
275     pub fn span_help<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self {
276         self.sub(Level::Help, msg, sp.into(), None);
277         self
278     }
279
280     pub fn multipart_suggestion(
281         &mut self,
282         msg: &str,
283         suggestion: Vec<(Span, String)>,
284         applicability: Applicability,
285     ) -> &mut Self {
286         self.suggestions.push(CodeSuggestion {
287             substitutions: vec![Substitution {
288                 parts: suggestion
289                     .into_iter()
290                     .map(|(span, snippet)| SubstitutionPart { snippet, span })
291                     .collect(),
292             }],
293             msg: msg.to_owned(),
294             style: SuggestionStyle::ShowCode,
295             applicability,
296         });
297         self
298     }
299
300     pub fn multipart_suggestions(
301         &mut self,
302         msg: &str,
303         suggestions: Vec<Vec<(Span, String)>>,
304         applicability: Applicability,
305     ) -> &mut Self {
306         self.suggestions.push(CodeSuggestion {
307             substitutions: suggestions
308                 .into_iter()
309                 .map(|suggestion| Substitution {
310                     parts: suggestion
311                         .into_iter()
312                         .map(|(span, snippet)| SubstitutionPart { snippet, span })
313                         .collect(),
314                 })
315                 .collect(),
316             msg: msg.to_owned(),
317             style: SuggestionStyle::ShowCode,
318             applicability,
319         });
320         self
321     }
322
323     /// Prints out a message with for a multipart suggestion without showing the suggested code.
324     ///
325     /// This is intended to be used for suggestions that are obvious in what the changes need to
326     /// be from the message, showing the span label inline would be visually unpleasant
327     /// (marginally overlapping spans or multiline spans) and showing the snippet window wouldn't
328     /// improve understandability.
329     pub fn tool_only_multipart_suggestion(
330         &mut self,
331         msg: &str,
332         suggestion: Vec<(Span, String)>,
333         applicability: Applicability,
334     ) -> &mut Self {
335         self.suggestions.push(CodeSuggestion {
336             substitutions: vec![Substitution {
337                 parts: suggestion
338                     .into_iter()
339                     .map(|(span, snippet)| SubstitutionPart { snippet, span })
340                     .collect(),
341             }],
342             msg: msg.to_owned(),
343             style: SuggestionStyle::CompletelyHidden,
344             applicability,
345         });
346         self
347     }
348
349     /// Prints out a message with a suggested edit of the code.
350     ///
351     /// In case of short messages and a simple suggestion, rustc displays it as a label:
352     ///
353     /// ```text
354     /// try adding parentheses: `(tup.0).1`
355     /// ```
356     ///
357     /// The message
358     ///
359     /// * should not end in any punctuation (a `:` is added automatically)
360     /// * should not be a question (avoid language like "did you mean")
361     /// * should not contain any phrases like "the following", "as shown", etc.
362     /// * may look like "to do xyz, use" or "to do xyz, use abc"
363     /// * may contain a name of a function, variable, or type, but not whole expressions
364     ///
365     /// See `CodeSuggestion` for more information.
366     pub fn span_suggestion(
367         &mut self,
368         sp: Span,
369         msg: &str,
370         suggestion: String,
371         applicability: Applicability,
372     ) -> &mut Self {
373         self.span_suggestion_with_style(
374             sp,
375             msg,
376             suggestion,
377             applicability,
378             SuggestionStyle::ShowCode,
379         );
380         self
381     }
382
383     pub fn span_suggestion_with_style(
384         &mut self,
385         sp: Span,
386         msg: &str,
387         suggestion: String,
388         applicability: Applicability,
389         style: SuggestionStyle,
390     ) -> &mut Self {
391         self.suggestions.push(CodeSuggestion {
392             substitutions: vec![Substitution {
393                 parts: vec![SubstitutionPart { snippet: suggestion, span: sp }],
394             }],
395             msg: msg.to_owned(),
396             style,
397             applicability,
398         });
399         self
400     }
401
402     pub fn span_suggestion_verbose(
403         &mut self,
404         sp: Span,
405         msg: &str,
406         suggestion: String,
407         applicability: Applicability,
408     ) -> &mut Self {
409         self.span_suggestion_with_style(
410             sp,
411             msg,
412             suggestion,
413             applicability,
414             SuggestionStyle::ShowAlways,
415         );
416         self
417     }
418
419     /// Prints out a message with multiple suggested edits of the code.
420     pub fn span_suggestions(
421         &mut self,
422         sp: Span,
423         msg: &str,
424         suggestions: impl Iterator<Item = String>,
425         applicability: Applicability,
426     ) -> &mut Self {
427         self.suggestions.push(CodeSuggestion {
428             substitutions: suggestions
429                 .map(|snippet| Substitution { parts: vec![SubstitutionPart { snippet, span: sp }] })
430                 .collect(),
431             msg: msg.to_owned(),
432             style: SuggestionStyle::ShowCode,
433             applicability,
434         });
435         self
436     }
437
438     /// Prints out a message with a suggested edit of the code. If the suggestion is presented
439     /// inline, it will only show the message and not the suggestion.
440     ///
441     /// See `CodeSuggestion` for more information.
442     pub fn span_suggestion_short(
443         &mut self,
444         sp: Span,
445         msg: &str,
446         suggestion: String,
447         applicability: Applicability,
448     ) -> &mut Self {
449         self.span_suggestion_with_style(
450             sp,
451             msg,
452             suggestion,
453             applicability,
454             SuggestionStyle::HideCodeInline,
455         );
456         self
457     }
458
459     /// Prints out a message with for a suggestion without showing the suggested code.
460     ///
461     /// This is intended to be used for suggestions that are obvious in what the changes need to
462     /// be from the message, showing the span label inline would be visually unpleasant
463     /// (marginally overlapping spans or multiline spans) and showing the snippet window wouldn't
464     /// improve understandability.
465     pub fn span_suggestion_hidden(
466         &mut self,
467         sp: Span,
468         msg: &str,
469         suggestion: String,
470         applicability: Applicability,
471     ) -> &mut Self {
472         self.span_suggestion_with_style(
473             sp,
474             msg,
475             suggestion,
476             applicability,
477             SuggestionStyle::HideCodeAlways,
478         );
479         self
480     }
481
482     /// Adds a suggestion to the json output, but otherwise remains silent/undisplayed in the cli.
483     ///
484     /// This is intended to be used for suggestions that are *very* obvious in what the changes
485     /// need to be from the message, but we still want other tools to be able to apply them.
486     pub fn tool_only_span_suggestion(
487         &mut self,
488         sp: Span,
489         msg: &str,
490         suggestion: String,
491         applicability: Applicability,
492     ) -> &mut Self {
493         self.span_suggestion_with_style(
494             sp,
495             msg,
496             suggestion,
497             applicability,
498             SuggestionStyle::CompletelyHidden,
499         );
500         self
501     }
502
503     pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self {
504         self.span = sp.into();
505         if let Some(span) = self.span.primary_span() {
506             self.sort_span = span;
507         }
508         self
509     }
510
511     pub fn code(&mut self, s: DiagnosticId) -> &mut Self {
512         self.code = Some(s);
513         self
514     }
515
516     pub fn clear_code(&mut self) -> &mut Self {
517         self.code = None;
518         self
519     }
520
521     pub fn get_code(&self) -> Option<DiagnosticId> {
522         self.code.clone()
523     }
524
525     pub fn set_primary_message<M: Into<String>>(&mut self, msg: M) -> &mut Self {
526         self.message[0] = (msg.into(), Style::NoStyle);
527         self
528     }
529
530     pub fn message(&self) -> String {
531         self.message.iter().map(|i| i.0.as_str()).collect::<String>()
532     }
533
534     pub fn styled_message(&self) -> &Vec<(String, Style)> {
535         &self.message
536     }
537
538     /// Used by a lint. Copies over all details *but* the "main
539     /// message".
540     pub fn copy_details_not_message(&mut self, from: &Diagnostic) {
541         self.span = from.span.clone();
542         self.code = from.code.clone();
543         self.children.extend(from.children.iter().cloned())
544     }
545
546     /// Convenience function for internal use, clients should use one of the
547     /// public methods above.
548     pub fn sub(
549         &mut self,
550         level: Level,
551         message: &str,
552         span: MultiSpan,
553         render_span: Option<MultiSpan>,
554     ) {
555         let sub = SubDiagnostic {
556             level,
557             message: vec![(message.to_owned(), Style::NoStyle)],
558             span,
559             render_span,
560         };
561         self.children.push(sub);
562     }
563
564     /// Convenience function for internal use, clients should use one of the
565     /// public methods above.
566     fn sub_with_highlights(
567         &mut self,
568         level: Level,
569         message: Vec<(String, Style)>,
570         span: MultiSpan,
571         render_span: Option<MultiSpan>,
572     ) {
573         let sub = SubDiagnostic { level, message, span, render_span };
574         self.children.push(sub);
575     }
576 }
577
578 impl SubDiagnostic {
579     pub fn message(&self) -> String {
580         self.message.iter().map(|i| i.0.as_str()).collect::<String>()
581     }
582
583     pub fn styled_message(&self) -> &Vec<(String, Style)> {
584         &self.message
585     }
586 }