1use alloc::{borrow::Cow, boxed::Box, sync::Arc, vec::Vec};
2use core::{fmt, ops::Range};
3
4pub use miette::{
5 self, Diagnostic, IntoDiagnostic, LabeledSpan, NamedSource, Report, Result, Severity,
6 SourceCode, WrapErr,
7};
8pub use tracing;
9pub use vm_core::debuginfo::*;
10
11#[macro_export]
12macro_rules! report {
13 ($($key:ident = $value:expr,)* $fmt:literal $($arg:tt)*) => {
14 $crate::diagnostics::Report::from(
15 $crate::diagnostic!($($key = $value,)* $fmt $($arg)*)
16 )
17 };
18}
19
20#[macro_export]
21macro_rules! diagnostic {
22 ($fmt:literal $($arg:tt)*) => {{
23 $crate::diagnostics::miette::MietteDiagnostic::new(format!($fmt $($arg)*))
24 }};
25 ($($key:ident = $value:expr,)+ $fmt:literal $($arg:tt)*) => {{
26 let mut diag = $crate::diagnostics::miette::MietteDiagnostic::new(format!($fmt $($arg)*));
27 $(diag.$key = Some($value.into());)*
28 diag
29 }};
30}
31
32#[derive(Debug, Clone, PartialEq, Eq)]
40pub struct Label {
41 span: miette::SourceSpan,
42 label: Option<Cow<'static, str>>,
43}
44
45impl Label {
46 pub fn at<R>(range: R) -> Self
49 where
50 Range<usize>: From<R>,
51 {
52 let range = Range::<usize>::from(range);
53 Self { span: range.into(), label: None }
54 }
55
56 pub fn point<L>(at: usize, label: L) -> Self
58 where
59 Cow<'static, str>: From<L>,
60 {
61 Self {
62 span: miette::SourceSpan::from(at),
63 label: Some(Cow::from(label)),
64 }
65 }
66
67 pub fn new<R, L>(range: R, label: L) -> Self
69 where
70 Range<usize>: From<R>,
71 Cow<'static, str>: From<L>,
72 {
73 let range = Range::<usize>::from(range);
74 Self {
75 span: range.into(),
76 label: Some(Cow::from(label)),
77 }
78 }
79
80 pub fn label(&self) -> Option<&str> {
82 self.label.as_deref()
83 }
84}
85
86impl From<Label> for miette::SourceSpan {
87 #[inline(always)]
88 fn from(label: Label) -> Self {
89 label.span
90 }
91}
92
93impl From<Label> for LabeledSpan {
94 #[inline]
95 fn from(label: Label) -> LabeledSpan {
96 if let Some(message) = label.label {
97 LabeledSpan::at(label.span, message)
98 } else {
99 LabeledSpan::underline(label.span)
100 }
101 }
102}
103
104#[derive(Debug)]
114pub struct RelatedLabel {
115 pub severity: Severity,
117 pub message: Cow<'static, str>,
119 pub labels: Vec<Label>,
121 pub file: Option<Arc<SourceFile>>,
123}
124
125impl fmt::Display for RelatedLabel {
126 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
127 f.write_str(self.message.as_ref())
128 }
129}
130
131#[cfg(feature = "std")]
132impl std::error::Error for RelatedLabel {
133 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
134 None
135 }
136}
137
138#[cfg(not(feature = "std"))]
139impl miette::StdError for RelatedLabel {
140 fn source(&self) -> Option<&(dyn miette::StdError + 'static)> {
141 None
142 }
143}
144
145impl RelatedLabel {
146 pub fn new<S>(severity: Severity, message: S) -> Self
147 where
148 Cow<'static, str>: From<S>,
149 {
150 Self {
151 severity,
152 message: Cow::from(message),
153 labels: vec![],
154 file: None,
155 }
156 }
157
158 pub fn error<S>(message: S) -> Self
159 where
160 Cow<'static, str>: From<S>,
161 {
162 Self::new(Severity::Error, message)
163 }
164
165 #[allow(unused)]
166 pub fn warning<S>(message: S) -> Self
167 where
168 Cow<'static, str>: From<S>,
169 {
170 Self::new(Severity::Warning, message)
171 }
172
173 #[allow(unused)]
174 pub fn advice<S>(message: S) -> Self
175 where
176 Cow<'static, str>: From<S>,
177 {
178 Self::new(Severity::Advice, message)
179 }
180
181 pub fn with_source_file(mut self, file: Option<Arc<SourceFile>>) -> Self {
182 self.file = file;
183 self
184 }
185
186 pub fn with_labeled_span<S>(self, span: SourceSpan, message: S) -> Self
187 where
188 Cow<'static, str>: From<S>,
189 {
190 let range = span.into_range();
191 self.with_label(Label::new((range.start as usize)..(range.end as usize), message))
192 }
193
194 pub fn with_label(mut self, label: Label) -> Self {
195 self.labels.push(label);
196 self
197 }
198
199 #[allow(unused)]
200 pub fn with_labels<I>(mut self, labels: I) -> Self
201 where
202 I: IntoIterator<Item = Label>,
203 {
204 self.labels.extend(labels);
205 self
206 }
207}
208
209impl Diagnostic for RelatedLabel {
210 fn code<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
211 None
212 }
213 fn severity(&self) -> Option<Severity> {
214 Some(self.severity)
215 }
216 fn help<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
217 None
218 }
219 fn url<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
220 None
221 }
222 fn source_code(&self) -> Option<&dyn SourceCode> {
223 self.file.as_ref().map(|f| f as &dyn SourceCode)
224 }
225 fn labels(&self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + '_>> {
226 if self.labels.is_empty() {
227 None
228 } else {
229 Some(Box::new(self.labels.iter().cloned().map(|l| l.into())))
230 }
231 }
232 fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> {
233 None
234 }
235 fn diagnostic_source(&self) -> Option<&dyn Diagnostic> {
236 None
237 }
238}
239
240#[derive(Debug)]
247pub struct RelatedError(Report);
248
249impl RelatedError {
250 pub fn into_report(self) -> Report {
251 self.0
252 }
253
254 #[inline(always)]
255 pub fn as_diagnostic(&self) -> &dyn Diagnostic {
256 self.0.as_ref()
257 }
258}
259
260impl Diagnostic for RelatedError {
261 fn code<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
262 self.as_diagnostic().code()
263 }
264 fn severity(&self) -> Option<Severity> {
265 self.as_diagnostic().severity()
266 }
267 fn help<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
268 self.as_diagnostic().help()
269 }
270 fn url<'a>(&'a self) -> Option<Box<dyn fmt::Display + 'a>> {
271 self.as_diagnostic().url()
272 }
273 fn source_code(&self) -> Option<&dyn SourceCode> {
274 self.as_diagnostic().source_code()
275 }
276 fn labels(&self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + '_>> {
277 self.as_diagnostic().labels()
278 }
279 fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> {
280 self.as_diagnostic().related()
281 }
282 fn diagnostic_source(&self) -> Option<&dyn Diagnostic> {
283 self.as_diagnostic().diagnostic_source()
284 }
285}
286
287impl fmt::Display for RelatedError {
288 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
289 fmt::Display::fmt(&self.0, f)
290 }
291}
292
293#[cfg(feature = "std")]
294impl std::error::Error for RelatedError {
295 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
296 AsRef::<dyn std::error::Error>::as_ref(&self.0).source()
297 }
298}
299
300#[cfg(not(feature = "std"))]
301impl miette::StdError for RelatedError {
302 fn source(&self) -> Option<&(dyn miette::StdError + 'static)> {
303 AsRef::<dyn miette::StdError>::as_ref(&self.0).source()
304 }
305}
306
307impl From<Report> for RelatedError {
308 fn from(report: Report) -> Self {
309 Self(report)
310 }
311}
312
313impl RelatedError {
314 pub const fn new(report: Report) -> Self {
315 Self(report)
316 }
317
318 pub fn wrap<E>(error: E) -> Self
319 where
320 E: Diagnostic + Send + Sync + 'static,
321 {
322 Self(Report::new_boxed(Box::new(error)))
323 }
324}
325
326pub mod reporting {
331 use core::fmt;
332
333 pub use miette::{
334 DebugReportHandler, JSONReportHandler, NarratableReportHandler, ReportHandler, set_hook,
335 };
336 #[cfg(feature = "std")]
337 pub use miette::{GraphicalReportHandler, GraphicalTheme, set_panic_hook};
338
339 pub type ReportHandlerOpts = miette::MietteHandlerOpts;
340
341 #[cfg(feature = "std")]
342 pub type DefaultReportHandler = miette::GraphicalReportHandler;
343
344 #[cfg(not(feature = "std"))]
345 pub type DefaultReportHandler = miette::DebugReportHandler;
346
347 pub struct PrintDiagnostic<D, R = DefaultReportHandler> {
349 handler: R,
350 diag: D,
351 }
352
353 impl<D: AsRef<dyn super::Diagnostic>> PrintDiagnostic<D> {
354 pub fn new(diag: D) -> Self {
355 Self { handler: Default::default(), diag }
356 }
357 #[cfg(feature = "std")]
358 pub fn new_without_color(diag: D) -> Self {
359 Self {
360 handler: DefaultReportHandler::new_themed(GraphicalTheme::none()),
361 diag,
362 }
363 }
364 #[cfg(not(feature = "std"))]
365 pub fn new_without_color(diag: D) -> Self {
366 Self::new(diag)
367 }
368 }
369
370 impl<D: AsRef<dyn super::Diagnostic>> PrintDiagnostic<D, NarratableReportHandler> {
371 pub fn narrated(diag: D) -> Self {
372 Self {
373 handler: NarratableReportHandler::default(),
374 diag,
375 }
376 }
377 }
378
379 impl<D: AsRef<dyn super::Diagnostic>> PrintDiagnostic<D, JSONReportHandler> {
380 pub fn json(diag: D) -> Self {
381 Self { handler: JSONReportHandler, diag }
382 }
383 }
384
385 impl<D: AsRef<dyn super::Diagnostic>> fmt::Display for PrintDiagnostic<D> {
386 fn fmt(&self, f: &mut fmt::Formatter) -> core::fmt::Result {
387 self.handler.render_report(f, self.diag.as_ref())
388 }
389 }
390
391 impl<D: AsRef<dyn super::Diagnostic>> fmt::Display for PrintDiagnostic<D, NarratableReportHandler> {
392 fn fmt(&self, f: &mut fmt::Formatter) -> core::fmt::Result {
393 self.handler.render_report(f, self.diag.as_ref())
394 }
395 }
396
397 impl<D: AsRef<dyn super::Diagnostic>> fmt::Display for PrintDiagnostic<D, JSONReportHandler> {
398 fn fmt(&self, f: &mut fmt::Formatter) -> core::fmt::Result {
399 self.handler.render_report(f, self.diag.as_ref())
400 }
401 }
402}