swc_common/errors/
diagnostic.rs

1// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
2// file at the top-level directory of this distribution and at
3// http://rust-lang.org/COPYRIGHT.
4//
5// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8// option. This file may not be copied, modified, or distributed
9// except according to those terms.
10
11use std::fmt;
12
13use super::{snippet::Style, Applicability, CodeSuggestion, Level, Substitution, SubstitutionPart};
14use crate::syntax_pos::{MultiSpan, Span};
15
16#[derive(Clone, Debug, PartialEq, Eq, Hash)]
17#[cfg_attr(
18    feature = "diagnostic-serde",
19    derive(serde::Serialize, serde::Deserialize)
20)]
21#[cfg_attr(
22    any(feature = "rkyv-impl"),
23    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
24)]
25#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
26#[cfg_attr(feature = "rkyv-impl", repr(C))]
27pub struct Message(pub String, pub Style);
28
29#[must_use]
30#[derive(Clone, Debug, PartialEq, Eq, Hash)]
31#[cfg_attr(
32    feature = "diagnostic-serde",
33    derive(serde::Serialize, serde::Deserialize)
34)]
35#[cfg_attr(
36    any(feature = "rkyv-impl"),
37    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
38)]
39#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
40#[cfg_attr(feature = "rkyv-impl", repr(C))]
41pub struct Diagnostic {
42    pub level: Level,
43    pub message: Vec<Message>,
44    pub code: Option<DiagnosticId>,
45    pub span: MultiSpan,
46    pub children: Vec<SubDiagnostic>,
47    pub suggestions: Vec<CodeSuggestion>,
48}
49
50#[derive(Clone, Debug, PartialEq, Eq, Hash)]
51#[cfg_attr(
52    feature = "diagnostic-serde",
53    derive(serde::Serialize, serde::Deserialize)
54)]
55#[cfg_attr(
56    any(feature = "rkyv-impl"),
57    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
58)]
59#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
60#[cfg_attr(feature = "rkyv-impl", repr(u32))]
61pub enum DiagnosticId {
62    Error(String),
63    Lint(String),
64}
65
66/// For example a note attached to an error.
67#[derive(Clone, Debug, PartialEq, Eq, Hash)]
68#[cfg_attr(
69    feature = "diagnostic-serde",
70    derive(serde::Serialize, serde::Deserialize)
71)]
72#[cfg_attr(
73    any(feature = "rkyv-impl"),
74    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
75)]
76#[cfg_attr(feature = "rkyv-impl", derive(bytecheck::CheckBytes))]
77#[cfg_attr(feature = "rkyv-impl", repr(C))]
78pub struct SubDiagnostic {
79    pub level: Level,
80    pub message: Vec<Message>,
81    pub span: MultiSpan,
82    pub render_span: Option<MultiSpan>,
83}
84
85#[derive(PartialEq, Eq, Default)]
86pub struct DiagnosticStyledString(pub Vec<StringPart>);
87
88impl DiagnosticStyledString {
89    pub fn new() -> DiagnosticStyledString {
90        Default::default()
91    }
92
93    pub fn push_normal<S: Into<String>>(&mut self, t: S) {
94        self.0.push(StringPart::Normal(t.into()));
95    }
96
97    pub fn push_highlighted<S: Into<String>>(&mut self, t: S) {
98        self.0.push(StringPart::Highlighted(t.into()));
99    }
100
101    pub fn normal<S: Into<String>>(t: S) -> DiagnosticStyledString {
102        DiagnosticStyledString(vec![StringPart::Normal(t.into())])
103    }
104
105    pub fn highlighted<S: Into<String>>(t: S) -> DiagnosticStyledString {
106        DiagnosticStyledString(vec![StringPart::Highlighted(t.into())])
107    }
108
109    pub fn content(&self) -> String {
110        self.0.iter().map(|x| x.content()).collect::<String>()
111    }
112}
113
114#[derive(PartialEq, Eq)]
115pub enum StringPart {
116    Normal(String),
117    Highlighted(String),
118}
119
120impl StringPart {
121    pub fn content(&self) -> &str {
122        match self {
123            &StringPart::Normal(ref s) | &StringPart::Highlighted(ref s) => s,
124        }
125    }
126}
127
128impl Diagnostic {
129    pub fn new(level: Level, message: &str) -> Self {
130        Diagnostic::new_with_code(level, None, message)
131    }
132
133    pub fn new_with_code(level: Level, code: Option<DiagnosticId>, message: &str) -> Self {
134        Diagnostic {
135            level,
136            message: vec![Message(message.to_owned(), Style::NoStyle)],
137            code,
138            span: MultiSpan::new(),
139            children: Vec::new(),
140            suggestions: Vec::new(),
141        }
142    }
143
144    pub fn is_error(&self) -> bool {
145        match self.level {
146            Level::Bug | Level::Fatal | Level::PhaseFatal | Level::Error | Level::FailureNote => {
147                true
148            }
149
150            Level::Warning | Level::Note | Level::Help | Level::Cancelled => false,
151        }
152    }
153
154    /// Cancel the diagnostic (a structured diagnostic must either be emitted or
155    /// canceled or it will panic when dropped).
156    pub fn cancel(&mut self) {
157        self.level = Level::Cancelled;
158    }
159
160    pub fn cancelled(&self) -> bool {
161        self.level == Level::Cancelled
162    }
163
164    /// Add a span/label to be included in the resulting snippet.
165    /// This is pushed onto the `MultiSpan` that was created when the
166    /// diagnostic was first built. If you don't call this function at
167    /// all, and you just supplied a `Span` to create the diagnostic,
168    /// then the snippet will just include that `Span`, which is
169    /// called the primary span.
170    pub fn span_label<T: Into<String>>(&mut self, span: Span, label: T) -> &mut Self {
171        self.span.push_span_label(span, label.into());
172        self
173    }
174
175    pub fn replace_span_with(&mut self, after: Span) -> &mut Self {
176        let before = self.span.clone();
177        self.set_span(after);
178        for span_label in before.span_labels() {
179            if let Some(label) = span_label.label {
180                self.span_label(after, label);
181            }
182        }
183        self
184    }
185
186    pub fn note_expected_found(
187        &mut self,
188        label: &dyn fmt::Display,
189        expected: DiagnosticStyledString,
190        found: DiagnosticStyledString,
191    ) -> &mut Self {
192        self.note_expected_found_extra(label, expected, found, &"", &"")
193    }
194
195    pub fn note_expected_found_extra(
196        &mut self,
197        label: &dyn fmt::Display,
198        expected: DiagnosticStyledString,
199        found: DiagnosticStyledString,
200        expected_extra: &dyn fmt::Display,
201        found_extra: &dyn fmt::Display,
202    ) -> &mut Self {
203        let mut msg: Vec<_> = vec![Message(format!("expected {} `", label), Style::NoStyle)];
204        msg.extend(expected.0.iter().map(|x| match *x {
205            StringPart::Normal(ref s) => Message(s.to_owned(), Style::NoStyle),
206            StringPart::Highlighted(ref s) => Message(s.to_owned(), Style::Highlight),
207        }));
208        msg.push(Message(format!("`{}\n", expected_extra), Style::NoStyle));
209        msg.push(Message(format!("   found {} `", label), Style::NoStyle));
210        msg.extend(found.0.iter().map(|x| match *x {
211            StringPart::Normal(ref s) => Message(s.to_owned(), Style::NoStyle),
212            StringPart::Highlighted(ref s) => Message(s.to_owned(), Style::Highlight),
213        }));
214        msg.push(Message(format!("`{}", found_extra), Style::NoStyle));
215
216        // For now, just attach these as notes
217        self.highlighted_note(msg);
218        self
219    }
220
221    pub fn note_trait_signature(&mut self, name: String, signature: String) -> &mut Self {
222        self.highlighted_note(vec![
223            Message(format!("`{}` from trait: `", name), Style::NoStyle),
224            Message(signature, Style::Highlight),
225            Message("`".to_string(), Style::NoStyle),
226        ]);
227        self
228    }
229
230    pub fn note(&mut self, msg: &str) -> &mut Self {
231        self.sub(Level::Note, msg, MultiSpan::new(), None);
232        self
233    }
234
235    pub fn highlighted_note(&mut self, msg: Vec<Message>) -> &mut Self {
236        self.sub_with_highlights(Level::Note, msg, MultiSpan::new(), None);
237        self
238    }
239
240    pub fn span_note<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self {
241        self.sub(Level::Note, msg, sp.into(), None);
242        self
243    }
244
245    pub fn warn(&mut self, msg: &str) -> &mut Self {
246        self.sub(Level::Warning, msg, MultiSpan::new(), None);
247        self
248    }
249
250    pub fn span_warn<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self {
251        self.sub(Level::Warning, msg, sp.into(), None);
252        self
253    }
254
255    pub fn help(&mut self, msg: &str) -> &mut Self {
256        self.sub(Level::Help, msg, MultiSpan::new(), None);
257        self
258    }
259
260    pub fn span_help<S: Into<MultiSpan>>(&mut self, sp: S, msg: &str) -> &mut Self {
261        self.sub(Level::Help, msg, sp.into(), None);
262        self
263    }
264
265    /// Prints out a message with a suggested edit of the code. If the
266    /// suggestion is presented inline it will only show the text message
267    /// and not the text.
268    ///
269    /// See `CodeSuggestion` for more information.
270    #[deprecated(note = "Use `span_suggestion_short_with_applicability`")]
271    pub fn span_suggestion_short(&mut self, sp: Span, msg: &str, suggestion: String) -> &mut Self {
272        self.suggestions.push(CodeSuggestion {
273            substitutions: vec![Substitution {
274                parts: vec![SubstitutionPart {
275                    snippet: suggestion,
276                    span: sp,
277                }],
278            }],
279            msg: msg.to_owned(),
280            show_code_when_inline: false,
281            applicability: Applicability::Unspecified,
282        });
283        self
284    }
285
286    /// Prints out a message with a suggested edit of the code.
287    ///
288    /// In case of short messages and a simple suggestion,
289    /// rustc displays it as a label like
290    ///
291    /// "try adding parentheses: `(tup.0).1`"
292    ///
293    /// The message
294    ///
295    /// * should not end in any punctuation (a `:` is added automatically)
296    /// * should not be a question
297    /// * should not contain any parts like "the following", "as shown"
298    /// * may look like "to do xyz, use" or "to do xyz, use abc"
299    /// * may contain a name of a function, variable or type, but not whole
300    ///   expressions
301    ///
302    /// See `CodeSuggestion` for more information.
303    #[deprecated(note = "Use `span_suggestion_with_applicability`")]
304    pub fn span_suggestion(&mut self, sp: Span, msg: &str, suggestion: String) -> &mut Self {
305        self.suggestions.push(CodeSuggestion {
306            substitutions: vec![Substitution {
307                parts: vec![SubstitutionPart {
308                    snippet: suggestion,
309                    span: sp,
310                }],
311            }],
312            msg: msg.to_owned(),
313            show_code_when_inline: true,
314            applicability: Applicability::Unspecified,
315        });
316        self
317    }
318
319    pub fn multipart_suggestion_with_applicability(
320        &mut self,
321        msg: &str,
322        suggestion: Vec<(Span, String)>,
323        applicability: Applicability,
324    ) -> &mut Self {
325        self.suggestions.push(CodeSuggestion {
326            substitutions: vec![Substitution {
327                parts: suggestion
328                    .into_iter()
329                    .map(|(span, snippet)| SubstitutionPart { snippet, span })
330                    .collect(),
331            }],
332            msg: msg.to_owned(),
333            show_code_when_inline: true,
334            applicability,
335        });
336        self
337    }
338
339    #[deprecated(note = "Use `multipart_suggestion_with_applicability`")]
340    pub fn multipart_suggestion(
341        &mut self,
342        msg: &str,
343        suggestion: Vec<(Span, String)>,
344    ) -> &mut Self {
345        self.multipart_suggestion_with_applicability(msg, suggestion, Applicability::Unspecified)
346    }
347
348    /// Prints out a message with multiple suggested edits of the code.
349    #[deprecated(note = "Use `span_suggestions_with_applicability`")]
350    pub fn span_suggestions(&mut self, sp: Span, msg: &str, suggestions: Vec<String>) -> &mut Self {
351        self.suggestions.push(CodeSuggestion {
352            substitutions: suggestions
353                .into_iter()
354                .map(|snippet| Substitution {
355                    parts: vec![SubstitutionPart { snippet, span: sp }],
356                })
357                .collect(),
358            msg: msg.to_owned(),
359            show_code_when_inline: true,
360            applicability: Applicability::Unspecified,
361        });
362        self
363    }
364
365    /// This is a suggestion that may contain mistakes or fillers and should
366    /// be read and understood by a human.
367    pub fn span_suggestion_with_applicability(
368        &mut self,
369        sp: Span,
370        msg: &str,
371        suggestion: String,
372        applicability: Applicability,
373    ) -> &mut Self {
374        self.suggestions.push(CodeSuggestion {
375            substitutions: vec![Substitution {
376                parts: vec![SubstitutionPart {
377                    snippet: suggestion,
378                    span: sp,
379                }],
380            }],
381            msg: msg.to_owned(),
382            show_code_when_inline: true,
383            applicability,
384        });
385        self
386    }
387
388    pub fn span_suggestions_with_applicability(
389        &mut self,
390        sp: Span,
391        msg: &str,
392        suggestions: impl Iterator<Item = String>,
393        applicability: Applicability,
394    ) -> &mut Self {
395        self.suggestions.push(CodeSuggestion {
396            substitutions: suggestions
397                .map(|snippet| Substitution {
398                    parts: vec![SubstitutionPart { snippet, span: sp }],
399                })
400                .collect(),
401            msg: msg.to_owned(),
402            show_code_when_inline: true,
403            applicability,
404        });
405        self
406    }
407
408    pub fn span_suggestion_short_with_applicability(
409        &mut self,
410        sp: Span,
411        msg: &str,
412        suggestion: String,
413        applicability: Applicability,
414    ) -> &mut Self {
415        self.suggestions.push(CodeSuggestion {
416            substitutions: vec![Substitution {
417                parts: vec![SubstitutionPart {
418                    snippet: suggestion,
419                    span: sp,
420                }],
421            }],
422            msg: msg.to_owned(),
423            show_code_when_inline: false,
424            applicability,
425        });
426        self
427    }
428
429    pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self {
430        self.span = sp.into();
431        self
432    }
433
434    pub fn code(&mut self, s: DiagnosticId) -> &mut Self {
435        self.code = Some(s);
436        self
437    }
438
439    pub fn get_code(&self) -> Option<DiagnosticId> {
440        self.code.clone()
441    }
442
443    pub fn message(&self) -> String {
444        self.message
445            .iter()
446            .map(|i| i.0.as_str())
447            .collect::<String>()
448    }
449
450    pub fn styled_message(&self) -> &Vec<Message> {
451        &self.message
452    }
453
454    /// Used by a lint. Copies over all details *but* the "main
455    /// message".
456    pub fn copy_details_not_message(&mut self, from: &Diagnostic) {
457        self.span = from.span.clone();
458        self.code.clone_from(&from.code);
459        self.children.extend(from.children.iter().cloned())
460    }
461
462    /// Convenience function for internal use, clients should use one of the
463    /// public methods above.
464    pub fn sub(
465        &mut self,
466        level: Level,
467        message: &str,
468        span: MultiSpan,
469        render_span: Option<MultiSpan>,
470    ) {
471        let sub = SubDiagnostic {
472            level,
473            message: vec![Message(message.to_owned(), Style::NoStyle)],
474            span,
475            render_span,
476        };
477        self.children.push(sub);
478    }
479
480    /// Convenience function for internal use, clients should use one of the
481    /// public methods above.
482    fn sub_with_highlights(
483        &mut self,
484        level: Level,
485        message: Vec<Message>,
486        span: MultiSpan,
487        render_span: Option<MultiSpan>,
488    ) {
489        let sub = SubDiagnostic {
490            level,
491            message,
492            span,
493            render_span,
494        };
495        self.children.push(sub);
496    }
497}
498
499impl SubDiagnostic {
500    pub fn message(&self) -> String {
501        self.message
502            .iter()
503            .map(|i| i.0.as_str())
504            .collect::<String>()
505    }
506
507    pub fn styled_message(&self) -> &Vec<Message> {
508        &self.message
509    }
510}