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}