]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_errors/src/diagnostic.rs
Detect match statement intended to be tail expression
[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         self.suggestions.push(CodeSuggestion {
299             substitutions: vec![Substitution {
300                 parts: suggestion
301                     .into_iter()
302                     .map(|(span, snippet)| SubstitutionPart { snippet, span })
303                     .collect(),
304             }],
305             msg: msg.to_owned(),
306             style: SuggestionStyle::ShowCode,
307             applicability,
308             tool_metadata: Default::default(),
309         });
310         self
311     }
312
313     /// Show multiple suggestions that have multiple parts.
314     /// See also [`Diagnostic::multipart_suggestion()`].
315     pub fn multipart_suggestions(
316         &mut self,
317         msg: &str,
318         suggestions: Vec<Vec<(Span, String)>>,
319         applicability: Applicability,
320     ) -> &mut Self {
321         self.suggestions.push(CodeSuggestion {
322             substitutions: suggestions
323                 .into_iter()
324                 .map(|suggestion| Substitution {
325                     parts: suggestion
326                         .into_iter()
327                         .map(|(span, snippet)| SubstitutionPart { snippet, span })
328                         .collect(),
329                 })
330                 .collect(),
331             msg: msg.to_owned(),
332             style: SuggestionStyle::ShowCode,
333             applicability,
334             tool_metadata: Default::default(),
335         });
336         self
337     }
338
339     /// Prints out a message with for a multipart suggestion without showing the suggested code.
340     ///
341     /// This is intended to be used for suggestions that are obvious in what the changes need to
342     /// be from the message, showing the span label inline would be visually unpleasant
343     /// (marginally overlapping spans or multiline spans) and showing the snippet window wouldn't
344     /// improve understandability.
345     pub fn tool_only_multipart_suggestion(
346         &mut self,
347         msg: &str,
348         suggestion: Vec<(Span, String)>,
349         applicability: Applicability,
350     ) -> &mut Self {
351         self.suggestions.push(CodeSuggestion {
352             substitutions: vec![Substitution {
353                 parts: suggestion
354                     .into_iter()
355                     .map(|(span, snippet)| SubstitutionPart { snippet, span })
356                     .collect(),
357             }],
358             msg: msg.to_owned(),
359             style: SuggestionStyle::CompletelyHidden,
360             applicability,
361             tool_metadata: Default::default(),
362         });
363         self
364     }
365
366     /// Prints out a message with a suggested edit of the code.
367     ///
368     /// In case of short messages and a simple suggestion, rustc displays it as a label:
369     ///
370     /// ```text
371     /// try adding parentheses: `(tup.0).1`
372     /// ```
373     ///
374     /// The message
375     ///
376     /// * should not end in any punctuation (a `:` is added automatically)
377     /// * should not be a question (avoid language like "did you mean")
378     /// * should not contain any phrases like "the following", "as shown", etc.
379     /// * may look like "to do xyz, use" or "to do xyz, use abc"
380     /// * may contain a name of a function, variable, or type, but not whole expressions
381     ///
382     /// See `CodeSuggestion` for more information.
383     pub fn span_suggestion(
384         &mut self,
385         sp: Span,
386         msg: &str,
387         suggestion: String,
388         applicability: Applicability,
389     ) -> &mut Self {
390         self.span_suggestion_with_style(
391             sp,
392             msg,
393             suggestion,
394             applicability,
395             SuggestionStyle::ShowCode,
396         );
397         self
398     }
399
400     /// [`Diagnostic::span_suggestion()`] but you can set the [`SuggestionStyle`].
401     pub fn span_suggestion_with_style(
402         &mut self,
403         sp: Span,
404         msg: &str,
405         suggestion: String,
406         applicability: Applicability,
407         style: SuggestionStyle,
408     ) -> &mut Self {
409         self.suggestions.push(CodeSuggestion {
410             substitutions: vec![Substitution {
411                 parts: vec![SubstitutionPart { snippet: suggestion, span: sp }],
412             }],
413             msg: msg.to_owned(),
414             style,
415             applicability,
416             tool_metadata: Default::default(),
417         });
418         self
419     }
420
421     /// Always show the suggested change.
422     pub fn span_suggestion_verbose(
423         &mut self,
424         sp: Span,
425         msg: &str,
426         suggestion: String,
427         applicability: Applicability,
428     ) -> &mut Self {
429         self.span_suggestion_with_style(
430             sp,
431             msg,
432             suggestion,
433             applicability,
434             SuggestionStyle::ShowAlways,
435         );
436         self
437     }
438
439     /// Prints out a message with multiple suggested edits of the code.
440     /// See also [`Diagnostic::span_suggestion()`].
441     pub fn span_suggestions(
442         &mut self,
443         sp: Span,
444         msg: &str,
445         suggestions: impl Iterator<Item = String>,
446         applicability: Applicability,
447     ) -> &mut Self {
448         self.suggestions.push(CodeSuggestion {
449             substitutions: suggestions
450                 .map(|snippet| Substitution { parts: vec![SubstitutionPart { snippet, span: sp }] })
451                 .collect(),
452             msg: msg.to_owned(),
453             style: SuggestionStyle::ShowCode,
454             applicability,
455             tool_metadata: Default::default(),
456         });
457         self
458     }
459
460     /// Prints out a message with a suggested edit of the code. If the suggestion is presented
461     /// inline, it will only show the message and not the suggestion.
462     ///
463     /// See `CodeSuggestion` for more information.
464     pub fn span_suggestion_short(
465         &mut self,
466         sp: Span,
467         msg: &str,
468         suggestion: String,
469         applicability: Applicability,
470     ) -> &mut Self {
471         self.span_suggestion_with_style(
472             sp,
473             msg,
474             suggestion,
475             applicability,
476             SuggestionStyle::HideCodeInline,
477         );
478         self
479     }
480
481     /// Prints out a message for a suggestion without showing the suggested code.
482     ///
483     /// This is intended to be used for suggestions that are obvious in what the changes need to
484     /// be from the message, showing the span label inline would be visually unpleasant
485     /// (marginally overlapping spans or multiline spans) and showing the snippet window wouldn't
486     /// improve understandability.
487     pub fn span_suggestion_hidden(
488         &mut self,
489         sp: Span,
490         msg: &str,
491         suggestion: String,
492         applicability: Applicability,
493     ) -> &mut Self {
494         self.span_suggestion_with_style(
495             sp,
496             msg,
497             suggestion,
498             applicability,
499             SuggestionStyle::HideCodeAlways,
500         );
501         self
502     }
503
504     /// Adds a suggestion to the JSON output that will not be shown in the CLI.
505     ///
506     /// This is intended to be used for suggestions that are *very* obvious in what the changes
507     /// need to be from the message, but we still want other tools to be able to apply them.
508     pub fn tool_only_span_suggestion(
509         &mut self,
510         sp: Span,
511         msg: &str,
512         suggestion: String,
513         applicability: Applicability,
514     ) -> &mut Self {
515         self.span_suggestion_with_style(
516             sp,
517             msg,
518             suggestion,
519             applicability,
520             SuggestionStyle::CompletelyHidden,
521         );
522         self
523     }
524
525     /// Adds a suggestion intended only for a tool. The intent is that the metadata encodes
526     /// the suggestion in a tool-specific way, as it may not even directly involve Rust code.
527     pub fn tool_only_suggestion_with_metadata(
528         &mut self,
529         msg: &str,
530         applicability: Applicability,
531         tool_metadata: Json,
532     ) {
533         self.suggestions.push(CodeSuggestion {
534             substitutions: vec![],
535             msg: msg.to_owned(),
536             style: SuggestionStyle::CompletelyHidden,
537             applicability,
538             tool_metadata: ToolMetadata::new(tool_metadata),
539         })
540     }
541
542     pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self {
543         self.span = sp.into();
544         if let Some(span) = self.span.primary_span() {
545             self.sort_span = span;
546         }
547         self
548     }
549
550     pub fn code(&mut self, s: DiagnosticId) -> &mut Self {
551         self.code = Some(s);
552         self
553     }
554
555     pub fn clear_code(&mut self) -> &mut Self {
556         self.code = None;
557         self
558     }
559
560     pub fn get_code(&self) -> Option<DiagnosticId> {
561         self.code.clone()
562     }
563
564     pub fn set_primary_message<M: Into<String>>(&mut self, msg: M) -> &mut Self {
565         self.message[0] = (msg.into(), Style::NoStyle);
566         self
567     }
568
569     pub fn message(&self) -> String {
570         self.message.iter().map(|i| i.0.as_str()).collect::<String>()
571     }
572
573     pub fn styled_message(&self) -> &Vec<(String, Style)> {
574         &self.message
575     }
576
577     /// Convenience function for internal use, clients should use one of the
578     /// public methods above.
579     pub fn sub(
580         &mut self,
581         level: Level,
582         message: &str,
583         span: MultiSpan,
584         render_span: Option<MultiSpan>,
585     ) {
586         let sub = SubDiagnostic {
587             level,
588             message: vec![(message.to_owned(), Style::NoStyle)],
589             span,
590             render_span,
591         };
592         self.children.push(sub);
593     }
594
595     /// Convenience function for internal use, clients should use one of the
596     /// public methods above.
597     fn sub_with_highlights(
598         &mut self,
599         level: Level,
600         message: Vec<(String, Style)>,
601         span: MultiSpan,
602         render_span: Option<MultiSpan>,
603     ) {
604         let sub = SubDiagnostic { level, message, span, render_span };
605         self.children.push(sub);
606     }
607 }
608
609 impl SubDiagnostic {
610     pub fn message(&self) -> String {
611         self.message.iter().map(|i| i.0.as_str()).collect::<String>()
612     }
613
614     pub fn styled_message(&self) -> &Vec<(String, Style)> {
615         &self.message
616     }
617 }