rspack_codespan_reporting/
diagnostic.rs

1//! Diagnostic data structures.
2
3#[cfg(feature = "serialization")]
4use serde::{Deserialize, Serialize};
5use std::ops::Range;
6use std::string::ToString;
7
8/// A severity level for diagnostic messages.
9///
10/// These are ordered in the following way:
11///
12/// ```rust
13/// use codespan_reporting::diagnostic::Severity;
14///
15/// assert!(Severity::Bug > Severity::Error);
16/// assert!(Severity::Error > Severity::Warning);
17/// assert!(Severity::Warning > Severity::Note);
18/// assert!(Severity::Note > Severity::Help);
19/// ```
20#[derive(Copy, Clone, Hash, Debug, PartialEq, Eq, PartialOrd, Ord)]
21#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
22pub enum Severity {
23    /// A help message.
24    Help,
25    /// A note.
26    Note,
27    /// A warning.
28    Warning,
29    /// An error.
30    Error,
31    /// An unexpected bug.
32    Bug,
33}
34
35#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd)]
36#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
37pub enum LabelStyle {
38    /// Labels that describe the primary cause of a diagnostic.
39    Primary,
40    /// Labels that provide additional context for a diagnostic.
41    Secondary,
42}
43
44/// A label describing an underlined region of code associated with a diagnostic.
45#[derive(Clone, Debug, PartialEq, Eq)]
46#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
47pub struct Label<FileId> {
48    /// The style of the label.
49    pub style: LabelStyle,
50    /// The file that we are labelling.
51    pub file_id: FileId,
52    /// The range in bytes we are going to include in the final snippet.
53    pub range: Range<usize>,
54    /// An optional message to provide some additional information for the
55    /// underlined code. These should not include line breaks.
56    pub message: String,
57}
58
59impl<FileId> Label<FileId> {
60    /// Create a new label.
61    pub fn new(
62        style: LabelStyle,
63        file_id: FileId,
64        range: impl Into<Range<usize>>,
65    ) -> Label<FileId> {
66        Label {
67            style,
68            file_id,
69            range: range.into(),
70            message: String::new(),
71        }
72    }
73
74    /// Create a new label with a style of [`LabelStyle::Primary`].
75    ///
76    /// [`LabelStyle::Primary`]: LabelStyle::Primary
77    pub fn primary(file_id: FileId, range: impl Into<Range<usize>>) -> Label<FileId> {
78        Label::new(LabelStyle::Primary, file_id, range)
79    }
80
81    /// Create a new label with a style of [`LabelStyle::Secondary`].
82    ///
83    /// [`LabelStyle::Secondary`]: LabelStyle::Secondary
84    pub fn secondary(file_id: FileId, range: impl Into<Range<usize>>) -> Label<FileId> {
85        Label::new(LabelStyle::Secondary, file_id, range)
86    }
87
88    /// Add a message to the diagnostic.
89    pub fn with_message(mut self, message: impl ToString) -> Label<FileId> {
90        self.message = message.to_string();
91        self
92    }
93}
94
95/// Represents a diagnostic message that can provide information like errors and
96/// warnings to the user.
97///
98/// The position of a Diagnostic is considered to be the position of the [`Label`] that has the earliest starting position and has the highest style which appears in all the labels of the diagnostic.
99#[derive(Clone, Debug, PartialEq, Eq)]
100#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
101pub struct Diagnostic<FileId> {
102    /// The overall severity of the diagnostic
103    pub severity: Severity,
104    /// An optional code that identifies this diagnostic.
105    pub code: Option<String>,
106    /// The main message associated with this diagnostic.
107    ///
108    /// These should not include line breaks, and in order support the 'short'
109    /// diagnostic display mod, the message should be specific enough to make
110    /// sense on its own, without additional context provided by labels and notes.
111    pub message: String,
112    /// Source labels that describe the cause of the diagnostic.
113    /// The order of the labels inside the vector does not have any meaning.
114    /// The labels are always arranged in the order they appear in the source code.
115    pub labels: Vec<Label<FileId>>,
116    /// Notes that are associated with the primary cause of the diagnostic.
117    /// These can include line breaks for improved formatting.
118    pub notes: Vec<String>,
119}
120
121impl<FileId> Diagnostic<FileId> {
122    /// Create a new diagnostic.
123    pub fn new(severity: Severity) -> Diagnostic<FileId> {
124        Diagnostic {
125            severity,
126            code: None,
127            message: String::new(),
128            labels: Vec::new(),
129            notes: Vec::new(),
130        }
131    }
132
133    /// Create a new diagnostic with a severity of [`Severity::Bug`].
134    ///
135    /// [`Severity::Bug`]: Severity::Bug
136    pub fn bug() -> Diagnostic<FileId> {
137        Diagnostic::new(Severity::Bug)
138    }
139
140    /// Create a new diagnostic with a severity of [`Severity::Error`].
141    ///
142    /// [`Severity::Error`]: Severity::Error
143    pub fn error() -> Diagnostic<FileId> {
144        Diagnostic::new(Severity::Error)
145    }
146
147    /// Create a new diagnostic with a severity of [`Severity::Warning`].
148    ///
149    /// [`Severity::Warning`]: Severity::Warning
150    pub fn warning() -> Diagnostic<FileId> {
151        Diagnostic::new(Severity::Warning)
152    }
153
154    /// Create a new diagnostic with a severity of [`Severity::Note`].
155    ///
156    /// [`Severity::Note`]: Severity::Note
157    pub fn note() -> Diagnostic<FileId> {
158        Diagnostic::new(Severity::Note)
159    }
160
161    /// Create a new diagnostic with a severity of [`Severity::Help`].
162    ///
163    /// [`Severity::Help`]: Severity::Help
164    pub fn help() -> Diagnostic<FileId> {
165        Diagnostic::new(Severity::Help)
166    }
167
168    /// Set the error code of the diagnostic.
169    pub fn with_code(mut self, code: impl ToString) -> Diagnostic<FileId> {
170        self.code = Some(code.to_string());
171        self
172    }
173
174    /// Set the message of the diagnostic.
175    pub fn with_message(mut self, message: impl ToString) -> Diagnostic<FileId> {
176        self.message = message.to_string();
177        self
178    }
179
180    /// Add some labels to the diagnostic.
181    pub fn with_labels(mut self, mut labels: Vec<Label<FileId>>) -> Diagnostic<FileId> {
182        self.labels.append(&mut labels);
183        self
184    }
185
186    /// Add some notes to the diagnostic.
187    pub fn with_notes(mut self, mut notes: Vec<String>) -> Diagnostic<FileId> {
188        self.notes.append(&mut notes);
189        self
190    }
191}