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