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