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