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