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