1use std::{fmt::Display, path::PathBuf};
4
5use thiserror::Error;
6use wgsl_parse::{
7 span::Span,
8 syntax::{Expression, Ident, ModulePath, Visibility},
9};
10
11#[cfg(feature = "eval")]
12use crate::eval::EvalError;
13use crate::{Mangler, sourcemap::SourceMap};
14
15#[derive(Debug, Error)]
17pub enum CondCompError {
18 #[error("invalid feature flag: `{0}`")]
19 InvalidFeatureFlag(String),
20 #[error("unexpected feature flag: `{0}`")]
21 UnexpectedFeatureFlag(String),
22 #[error("invalid if attribute expression: `{0}`")]
23 InvalidExpression(Expression),
24 #[error("an @elif or @else attribute must be preceded by a @if or @elif on the previous node")]
25 NoPrecedingIf,
26 #[error("cannot have multiple @if/@elif/@else attributes on the same node")]
27 DuplicateIf,
28}
29
30#[derive(Debug, thiserror::Error)]
32pub enum ResolveError {
33 #[error("I/O Error: {0}")]
34 Io(std::io::Error),
35 #[error("file not found: `{0}` ({1})")]
36 FileNotFound(PathBuf, String),
37 #[error("module not found: `{0}` ({1})")]
38 ModuleNotFound(ModulePath, String),
39 #[error("the resolver does not support file system paths")]
40 FilesystemNotSupported,
41 #[error("attempt to access path `{0}`, which escapes the package root path `{1}`")]
42 FileEscapesRoot(PathBuf, PathBuf),
43 #[error("{0}")]
44 Error(#[from] Diagnostic<Error>),
45}
46
47#[derive(Clone, Debug, thiserror::Error)]
49pub enum ValidateError {
50 #[error("cannot find declaration of `{0}`")]
51 UndefinedSymbol(String),
52 #[error("incorrect number of arguments to `{0}`, expected `{1}`, got `{2}`")]
53 ParamCount(String, usize, usize),
54 #[error("`{0}` is not callable")]
55 NotCallable(String),
56 #[error("duplicate declaration of `{0}`")]
57 Duplicate(String),
58 #[error("declaration of `{0}` is cyclic via `{1}`")]
59 Cycle(String, String),
60}
61
62#[derive(Debug, thiserror::Error)]
64pub enum ImportError {
65 #[error("duplicate declaration of `{0}`")]
66 DuplicateSymbol(String),
67 #[error("{0}")]
68 ResolveError(#[from] ResolveError),
69}
70
71#[derive(Debug, thiserror::Error)]
72pub enum UsageError {
73 #[error("module `{0}` has no declaration `{1}`")]
74 NotFound(ModulePath, String),
75 #[error(
76 "`{decl_path}::{decl_name}` is declared with `{decl_vis}` visibility, but {orig} imports it with `{min_vis}` visibility",
77 decl_path = .decl.0,
78 decl_name = .decl.1,
79 orig = .orig
80 .as_ref()
81 .map(|(path, name)| format!("`{path}::{name}`"))
82 .unwrap_or("another declaration".to_string())
83 )]
84 Visibility {
85 orig: Option<(ModulePath, Ident)>,
86 decl: (ModulePath, Ident),
87 min_vis: Visibility,
88 decl_vis: Visibility,
89 },
90}
91
92#[derive(Debug, thiserror::Error)]
93pub enum TomlError {
94 #[error("wesl.toml not found at `{0}`")]
95 TomlNotFound(PathBuf),
96 #[error("Failed to parse wesl.toml: {0}")]
97 TomlParse(#[from] toml::de::Error),
98 #[error("expected dependencies = \"auto\"")]
99 ExpectedAuto,
100 #[error("Invalid glob pattern `{0}`: {1}")]
101 InvalidGlob(String, glob::PatternError),
102 #[error("File `{0}` is outside root `{1}`")]
103 FileOutsideRoot(PathBuf, PathBuf),
104 #[error("I/O error: {0}")]
105 Io(#[from] std::io::Error),
106 #[error("No source files matched the include patterns")]
107 NoFilesMatched,
108 #[error("Multiple files map to module `{0}`: {1:?}")]
109 ConflictingFiles(String, Vec<PathBuf>),
110}
111
112#[derive(Debug, thiserror::Error)]
114pub enum Error {
115 #[error("{0}")]
116 ParseError(#[from] wgsl_parse::Error),
117 #[error("{0}")]
118 ValidateError(#[from] ValidateError),
119 #[error("{0}")]
120 ResolveError(#[from] ResolveError),
121 #[error("{0}")]
122 ImportError(#[from] ImportError),
123 #[error("{0}")]
124 UsageError(#[from] UsageError),
125 #[error("{0}")]
126 CondCompError(#[from] CondCompError),
127 #[error("{0}")]
128 TomlError(#[from] TomlError),
129 #[cfg(feature = "eval")]
130 #[error("{0}")]
131 EvalError(#[from] EvalError),
132 #[error("{0}")]
133 Error(#[from] Diagnostic<Error>),
134 #[error("{0}")]
135 Custom(String),
136}
137
138#[derive(Debug)]
143pub struct Diagnostic<E = Error> {
144 pub error: Box<E>,
145 pub detail: Box<Detail>,
146}
147
148#[derive(Clone, Debug)]
149pub struct Detail {
150 pub source: Option<String>,
151 pub output: Option<String>,
152 pub module_path: Option<ModulePath>,
153 pub display_name: Option<String>,
154 pub declaration: Option<String>,
155 pub span: Option<Span>,
156}
157
158impl From<wgsl_parse::Error> for Diagnostic<Error> {
159 fn from(error: wgsl_parse::Error) -> Self {
160 let span = error.span;
161 let mut res = Self::new(Error::ParseError(error));
162 res.detail.span = Some(span);
163 res
164 }
165}
166
167impl From<ValidateError> for Diagnostic<Error> {
168 fn from(error: ValidateError) -> Self {
169 Self::new(error.into())
170 }
171}
172
173impl From<ResolveError> for Diagnostic<Error> {
174 fn from(error: ResolveError) -> Self {
175 match error {
176 ResolveError::Io(_)
177 | ResolveError::FileNotFound(_, _)
178 | ResolveError::ModuleNotFound(_, _)
179 | ResolveError::FilesystemNotSupported
180 | ResolveError::FileEscapesRoot(_, _) => Self::new(error.into()),
181 ResolveError::Error(e) => e,
182 }
183 }
184}
185
186impl From<ImportError> for Diagnostic<Error> {
187 fn from(error: ImportError) -> Self {
188 match error {
189 ImportError::ResolveError(e) => Self::from(e),
190 _ => Self::new(error.into()),
191 }
192 }
193}
194
195impl From<UsageError> for Diagnostic<Error> {
196 fn from(error: UsageError) -> Self {
197 Self::new(error.into())
198 }
199}
200
201impl From<CondCompError> for Diagnostic<Error> {
202 fn from(error: CondCompError) -> Self {
203 Self::new(error.into())
204 }
205}
206
207impl From<TomlError> for Diagnostic<Error> {
208 fn from(error: TomlError) -> Self {
209 Self::new(error.into())
210 }
211}
212
213#[cfg(feature = "eval")]
214impl From<EvalError> for Diagnostic<Error> {
215 fn from(error: EvalError) -> Self {
216 Self::new(error.into())
217 }
218}
219
220impl From<Error> for Diagnostic<Error> {
221 fn from(error: Error) -> Self {
222 match error {
223 Error::ParseError(e) => e.into(),
224 Error::ResolveError(e) => e.into(),
225 Error::ImportError(e) => e.into(),
226 Error::UsageError(e) => e.into(),
227 Error::Error(e) => e,
228 Error::ValidateError(e) => e.into(),
229 Error::CondCompError(e) => e.into(),
230 Error::TomlError(e) => e.into(),
231 #[cfg(feature = "eval")]
232 Error::EvalError(e) => e.into(),
233 Error::Custom(_) => Self::new(error),
234 }
235 }
236}
237
238impl<E> Diagnostic<E> {
239 fn new(error: E) -> Diagnostic<E> {
241 Self {
242 error: Box::new(error),
243 detail: Box::new(Detail {
244 source: None,
245 output: None,
246 module_path: None,
247 display_name: None,
248 declaration: None,
249 span: None,
250 }),
251 }
252 }
253 pub fn with_source(mut self, source: String) -> Self {
256 if self.detail.source.is_none() {
257 self.detail.source = Some(source);
258 }
259 self
260 }
261 pub fn with_span(mut self, span: Span) -> Self {
265 if self.detail.span.is_none() {
266 self.detail.span = Some(span);
267 }
268 self
269 }
270 pub fn with_declaration(mut self, decl: String) -> Self {
272 if self.detail.declaration.is_none() {
273 self.detail.declaration = Some(decl);
274 }
275 self
276 }
277 pub fn with_output(mut self, out: String) -> Self {
279 if self.detail.output.is_none() {
280 self.detail.output = Some(out);
281 }
282 self
283 }
284 pub fn with_module_path(mut self, path: ModulePath, disp_name: Option<String>) -> Self {
287 if self.detail.module_path.is_none() {
288 self.detail.module_path = Some(path);
289 self.detail.display_name = disp_name;
290 }
291 self
292 }
293 #[cfg(feature = "eval")]
295 pub fn with_ctx(mut self, ctx: &crate::eval::Context) -> Self {
296 let (decl, span) = ctx.err_ctx();
297 self.detail.declaration = decl.map(|id| id.to_string());
298 self.detail.span = span;
299 self
300 }
301
302 pub fn with_sourcemap(mut self, sourcemap: &impl SourceMap) -> Self {
305 if let Some(decl) = &self.detail.declaration
306 && let Some(entry) = sourcemap.item(decl)
307 {
308 self.detail.module_path = Some(entry.path.clone());
309 self.detail.declaration = Some(entry.name.to_string());
310 self.detail.display_name = sourcemap
311 .display_name(&entry.path)
312 .map(|name| name.to_string());
313 self.detail.source = sourcemap
314 .source(&entry.path)
315 .map(|s| s.to_string())
316 .or(self.detail.source);
317 }
318
319 if self.detail.source.is_none() {
320 if let Some(path) = &self.detail.module_path {
321 self.detail.source = sourcemap.source(path).map(|s| s.to_string());
322 } else {
323 self.detail.source = sourcemap.default_source().map(|s| s.to_string());
324 }
325 }
326
327 self
328 }
329
330 pub(crate) fn display_origin(&self) -> String {
331 match (&self.detail.module_path, &self.detail.display_name) {
332 (Some(res), Some(name)) => {
333 format!("{res} ({name})")
334 }
335 (Some(res), None) => res.to_string(),
336 (None, Some(name)) => name.to_string(),
337 (None, None) => "unknown module".to_string(),
338 }
339 }
340
341 pub(crate) fn display_short_origin(&self) -> Option<String> {
342 self.detail
343 .display_name
344 .clone()
345 .or_else(|| self.detail.module_path.as_ref().map(|res| res.to_string()))
346 }
347}
348
349impl Diagnostic<Error> {
350 #[allow(dead_code, reason = "TODO this should be re-added")]
357 fn unmangle(
358 mut self,
359 sourcemap: Option<&impl SourceMap>,
360 mangler: Option<&impl Mangler>,
361 ) -> Self {
362 fn unmangle_id(
363 id: &mut Ident,
364 sourcemap: Option<&impl SourceMap>,
365 mangler: Option<&impl Mangler>,
366 ) {
367 let path_name = if let Some(sourcemap) = sourcemap {
368 sourcemap
369 .item(&id.name())
370 .map(|entry| (entry.path.clone(), entry.name.to_string()))
371 } else if let Some(mangler) = mangler {
372 mangler.unmangle(&id.name())
373 } else {
374 None
375 };
376 if let Some((path, name)) = path_name {
377 *id = Ident::new(format!("{path}::{name}"));
378 }
379 }
380
381 fn unmangle_name(
382 mangled: &mut String,
383 sourcemap: Option<&impl SourceMap>,
384 mangler: Option<&impl Mangler>,
385 ) {
386 let path_name = if let Some(sourcemap) = sourcemap {
387 sourcemap
388 .item(mangled)
389 .map(|entry| (entry.path.clone(), entry.name.to_string()))
390 } else if let Some(mangler) = mangler {
391 mangler.unmangle(mangled)
392 } else {
393 None
394 };
395 if let Some((path, name)) = path_name {
396 *mangled = format!("{path}::{name}");
397 }
398 }
399
400 fn unmangle_expr(
401 expr: &mut Expression,
402 sourcemap: Option<&impl SourceMap>,
403 mangler: Option<&impl Mangler>,
404 ) {
405 match expr {
406 Expression::Literal(_) => {}
407 Expression::Parenthesized(e) => {
408 unmangle_expr(&mut e.expression, sourcemap, mangler)
409 }
410 Expression::NamedComponent(e) => unmangle_expr(&mut e.base, sourcemap, mangler),
411 Expression::Indexing(e) => unmangle_expr(&mut e.base, sourcemap, mangler),
412 Expression::Unary(e) => unmangle_expr(&mut e.operand, sourcemap, mangler),
413 Expression::Binary(e) => {
414 unmangle_expr(&mut e.left, sourcemap, mangler);
415 unmangle_expr(&mut e.right, sourcemap, mangler);
416 }
417 Expression::FunctionCall(e) => {
418 unmangle_id(&mut e.ty.ident, sourcemap, mangler);
419 for arg in &mut e.arguments {
420 unmangle_expr(arg, sourcemap, mangler);
421 }
422 }
423 Expression::TypeOrIdentifier(ty) => unmangle_id(&mut ty.ident, sourcemap, mangler),
424 }
425 }
426
427 #[cfg(feature = "eval")]
428 fn unmangle_ty(
429 mangled: &mut wgsl_types::ty::Type,
430 sourcemap: Option<&impl SourceMap>,
431 mangler: Option<&impl Mangler>,
432 ) {
433 use wgsl_types::ty::Type;
434 match mangled {
435 Type::Struct(s) => {
437 unmangle_name(&mut s.name, sourcemap, mangler);
438 for m in s.members.iter_mut() {
439 unmangle_ty(&mut m.ty, sourcemap, mangler);
440 }
441 }
442 Type::Array(ty, _) => unmangle_ty(&mut *ty, sourcemap, mangler),
443 Type::Atomic(ty) => unmangle_ty(&mut *ty, sourcemap, mangler),
444 Type::Ptr(_, ty, _) => unmangle_ty(&mut *ty, sourcemap, mangler),
445 Type::Ref(_, ty, _) => unmangle_ty(&mut *ty, sourcemap, mangler),
446 _ => (),
447 }
448 }
449
450 #[cfg(feature = "eval")]
451 fn unmangle_inst(
452 mangled: &mut wgsl_types::inst::Instance,
453 sourcemap: Option<&impl SourceMap>,
454 mangler: Option<&impl Mangler>,
455 ) {
456 use wgsl_types::inst::Instance;
457 match mangled {
458 Instance::Struct(inst) => {
459 unmangle_name(&mut inst.ty.name, sourcemap, mangler);
460 for inst in inst.members.iter_mut() {
461 unmangle_inst(inst, sourcemap, mangler);
462 }
463 }
464 Instance::Array(inst) => {
465 for c in inst.iter_mut() {
466 unmangle_inst(c, sourcemap, mangler);
467 }
468 }
469 Instance::Ptr(inst) => {
470 unmangle_ty(&mut inst.ptr.ty, sourcemap, mangler);
471 }
472 Instance::Ref(inst) => {
473 unmangle_ty(&mut inst.ty, sourcemap, mangler);
474 }
475 Instance::Atomic(inst) => {
476 unmangle_inst(inst.inner_mut(), sourcemap, mangler);
477 }
478 Instance::Opaque(ty) => unmangle_ty(ty, sourcemap, mangler),
479 Instance::Literal(_) | Instance::Vec(_) | Instance::Mat(_) => {}
480 }
481 }
482
483 if let Some(decl) = &mut self.detail.declaration {
484 unmangle_name(decl, sourcemap, mangler);
485 }
486
487 match &mut *self.error {
488 Error::ParseError(_) => {}
489 Error::ValidateError(e) => match e {
490 ValidateError::UndefinedSymbol(name)
491 | ValidateError::ParamCount(name, _, _)
492 | ValidateError::NotCallable(name)
493 | ValidateError::Duplicate(name) => unmangle_name(name, sourcemap, mangler),
494 ValidateError::Cycle(name1, name2) => {
495 unmangle_name(name1, sourcemap, mangler);
496 unmangle_name(name2, sourcemap, mangler);
497 }
498 },
499 Error::ResolveError(_) => {}
500 Error::ImportError(_) => {}
501 Error::UsageError(e) => match e {
502 UsageError::NotFound(_, _) => todo!(),
503 UsageError::Visibility { orig, decl, .. } => {
504 if let Some((_, id)) = orig {
505 unmangle_id(id, sourcemap, mangler);
506 }
507 unmangle_id(&mut decl.1, sourcemap, mangler);
508 }
509 },
510 Error::CondCompError(e) => match e {
511 CondCompError::InvalidExpression(expr) => unmangle_expr(expr, sourcemap, mangler),
512 CondCompError::InvalidFeatureFlag(_)
513 | CondCompError::UnexpectedFeatureFlag(_)
514 | CondCompError::NoPrecedingIf
515 | CondCompError::DuplicateIf => {}
516 },
517 Error::TomlError(_) => {}
518 #[cfg(feature = "eval")]
521 Error::EvalError(e) => match e {
522 EvalError::NotScalar(ty) => unmangle_ty(ty, sourcemap, mangler),
523 EvalError::NotConstructible(ty) => unmangle_ty(ty, sourcemap, mangler),
524 EvalError::Type(ty1, ty2) => {
525 unmangle_ty(ty1, sourcemap, mangler);
526 unmangle_ty(ty2, sourcemap, mangler);
527 }
528 EvalError::SampledType(ty) => {
529 unmangle_ty(ty, sourcemap, mangler);
530 }
531 EvalError::NotType(name) => unmangle_name(name, sourcemap, mangler),
532 EvalError::UnknownType(name) => unmangle_name(name, sourcemap, mangler),
533 EvalError::UnknownStruct(name) => unmangle_name(name, sourcemap, mangler),
534 EvalError::NotAccessible(name, _) => unmangle_name(name, sourcemap, mangler),
535 EvalError::UnexpectedTemplate(name) => unmangle_name(name, sourcemap, mangler),
536 EvalError::View(ty, _) => unmangle_ty(ty, sourcemap, mangler),
537 EvalError::RefType(ty1, ty2) => {
538 unmangle_ty(ty1, sourcemap, mangler);
539 unmangle_ty(ty2, sourcemap, mangler);
540 }
541 EvalError::WriteRefType(ty1, ty2) => {
542 unmangle_ty(ty1, sourcemap, mangler);
543 unmangle_ty(ty2, sourcemap, mangler);
544 }
545 EvalError::Conversion(ty1, ty2) => {
546 unmangle_ty(ty1, sourcemap, mangler);
547 unmangle_ty(ty2, sourcemap, mangler);
548 }
549 EvalError::ConvOverflow(_, ty) => unmangle_ty(ty, sourcemap, mangler),
550 EvalError::Component(ty, _) => unmangle_ty(ty, sourcemap, mangler),
551 EvalError::Index(ty) => unmangle_ty(ty, sourcemap, mangler),
552 EvalError::NotIndexable(ty) => unmangle_ty(ty, sourcemap, mangler),
553 EvalError::OutOfBounds(_, ty, _) => unmangle_ty(ty, sourcemap, mangler),
554 EvalError::Unary(_, ty) => unmangle_ty(ty, sourcemap, mangler),
555 EvalError::Binary(_, ty1, ty2) => {
556 unmangle_ty(ty1, sourcemap, mangler);
557 unmangle_ty(ty2, sourcemap, mangler);
558 }
559 EvalError::CompwiseBinary(ty1, ty2) => {
560 unmangle_ty(ty1, sourcemap, mangler);
561 unmangle_ty(ty2, sourcemap, mangler);
562 }
563 EvalError::UnknownFunction(name) => unmangle_name(name, sourcemap, mangler),
564 EvalError::NotCallable(name) => unmangle_name(name, sourcemap, mangler),
565 EvalError::Signature(sig) => {
566 unmangle_name(&mut sig.name, sourcemap, mangler);
567 for tplt in sig.tplt.iter_mut().flatten() {
568 match tplt {
569 wgsl_types::tplt::TpltParam::Type(ty) => {
570 unmangle_ty(ty, sourcemap, mangler)
571 }
572 wgsl_types::tplt::TpltParam::Instance(inst) => {
573 unmangle_inst(inst, sourcemap, mangler)
574 }
575 wgsl_types::tplt::TpltParam::Enumerant(_) => {}
576 }
577 }
578 for arg in &mut sig.args {
579 unmangle_ty(arg, sourcemap, mangler);
580 }
581 }
582 EvalError::ParamCount(name, _, _) => unmangle_name(name, sourcemap, mangler),
583 EvalError::ParamType(ty1, ty2) => {
584 unmangle_ty(ty1, sourcemap, mangler);
585 unmangle_ty(ty2, sourcemap, mangler);
586 }
587 EvalError::ReturnType(ty1, name, ty2) => {
588 unmangle_ty(ty1, sourcemap, mangler);
589 unmangle_name(name, sourcemap, mangler);
590 unmangle_ty(ty2, sourcemap, mangler);
591 }
592 EvalError::NoReturn(name, ty) => {
593 unmangle_name(name, sourcemap, mangler);
594 unmangle_ty(ty, sourcemap, mangler);
595 }
596 EvalError::UnexpectedReturn(name, ty) => {
597 unmangle_name(name, sourcemap, mangler);
598 unmangle_ty(ty, sourcemap, mangler);
599 }
600 EvalError::NotConst(name) => unmangle_name(name, sourcemap, mangler),
601 EvalError::Void(name) => unmangle_name(name, sourcemap, mangler),
602 EvalError::MustUse(name) => unmangle_name(name, sourcemap, mangler),
603 EvalError::NotEntrypoint(name) => unmangle_name(name, sourcemap, mangler),
604 EvalError::UnknownDecl(name) => unmangle_name(name, sourcemap, mangler),
605 EvalError::UninitConst(name) => unmangle_name(name, sourcemap, mangler),
606 EvalError::UninitLet(name) => unmangle_name(name, sourcemap, mangler),
607 EvalError::UninitOverride(name) => unmangle_name(name, sourcemap, mangler),
608 EvalError::DuplicateDecl(name) => unmangle_name(name, sourcemap, mangler),
609 EvalError::AssignType(ty1, ty2) => {
610 unmangle_ty(ty1, sourcemap, mangler);
611 unmangle_ty(ty2, sourcemap, mangler);
612 }
613 EvalError::IncrType(ty) => unmangle_ty(ty, sourcemap, mangler),
614 EvalError::DecrType(ty) => unmangle_ty(ty, sourcemap, mangler),
615 EvalError::ConstAssertFailure(expr) => unmangle_expr(expr, sourcemap, mangler),
616 EvalError::Todo(_)
617 | EvalError::Unreachable
618 | EvalError::MissingTemplate(_)
619 | EvalError::NotWrite
620 | EvalError::NotRead
621 | EvalError::NotReadWrite
622 | EvalError::PtrHandle
623 | EvalError::PtrVecComp
624 | EvalError::Swizzle(_)
625 | EvalError::NegOverflow
626 | EvalError::AddOverflow
627 | EvalError::SubOverflow
628 | EvalError::MulOverflow
629 | EvalError::DivByZero
630 | EvalError::RemZeroDiv
631 | EvalError::ShlOverflow(_, _)
632 | EvalError::ShrOverflow(_, _)
633 | EvalError::Builtin(_)
634 | EvalError::TemplateArgs(_)
635 | EvalError::InvalidEntrypointParam(_)
636 | EvalError::MissingBuiltinInput(_, _)
637 | EvalError::OutputBuiltin(_)
638 | EvalError::InputBuiltin(_)
639 | EvalError::MissingUserInput(_, _)
640 | EvalError::OverrideInConst
641 | EvalError::OverrideInFn
642 | EvalError::LetInMod
643 | EvalError::ForbiddenInitializer(_)
644 | EvalError::UntypedDecl
645 | EvalError::ForbiddenDecl(_, _)
646 | EvalError::MissingResource(_, _)
647 | EvalError::AddressSpace(_, _)
648 | EvalError::AccessMode(_, _)
649 | EvalError::MissingBindAttr
650 | EvalError::MissingWorkgroupSize
651 | EvalError::NegativeAttr(_)
652 | EvalError::InvalidBlendSrc(_)
653 | EvalError::NotRef(_)
654 | EvalError::IncrOverflow
655 | EvalError::DecrOverflow
656 | EvalError::FlowInContinuing(_)
657 | EvalError::DiscardInConst
658 | EvalError::FlowInFunction(_)
659 | EvalError::FlowInModule(_) => {}
660 },
661 Error::Error(_) => {}
662 Error::Custom(_) => {}
663 };
664
665 self
666 }
667}
668
669impl<E: std::error::Error> Diagnostic<E> {
670 fn render_snippet(&self, renderer: &annotate_snippets::Renderer) -> String {
671 use annotate_snippets::*;
672 let msg = format!("{}", self.error);
673 let title = Level::ERROR.primary_title(&msg);
674 let mut group = Group::with_title(title);
675
676 let orig = self.display_origin();
677 let short_orig = self.display_short_origin();
678
679 if let Some(span) = &self.detail.span {
680 let source = self.detail.source.as_deref();
681
682 if let Some(source) = source {
683 if span.range().end <= source.len() {
684 let annot = AnnotationKind::Primary.span(span.range()).label(&msg);
685 let mut snip = Snippet::source(source).fold(true).annotation(annot);
686
687 if let Some(orig) = &short_orig {
688 snip = snip.path(orig);
689 }
690
691 group = group.element(snip);
692 } else {
693 group = group.element(
694 Level::NOTE.message("cannot display snippet: invalid source location"),
695 )
696 }
697 } else {
698 group = group
699 .element(Level::NOTE.message("cannot display snippet: missing source file"))
700 }
701 }
702
703 let note;
704 if let Some(decl) = &self.detail.declaration {
705 note = format!("in declaration of `{decl}` in {orig}");
706 } else {
707 note = format!("in {orig}");
708 }
709 let group = group.element(Level::NOTE.message(¬e));
710
711 renderer.render(&[group])
712 }
713
714 pub fn render_plain(&self) -> String {
715 let renderer = annotate_snippets::Renderer::plain();
716 self.render_snippet(&renderer)
717 }
718
719 pub fn render_colored(&self) -> String {
720 let renderer = annotate_snippets::Renderer::styled();
721 self.render_snippet(&renderer)
722 }
723}
724
725impl<E: std::error::Error> std::error::Error for Diagnostic<E> {}
726
727impl<E: std::error::Error> Display for Diagnostic<E> {
728 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
729 let choice = anstream::AutoStream::choice(&std::io::stdout());
734 if choice == anstream::ColorChoice::Always || choice == anstream::ColorChoice::AlwaysAnsi {
735 write!(f, "{}", self.render_colored())
736 } else {
737 write!(f, "{}", self.render_plain())
738 }
739 }
740}
741
742#[derive(Debug)]
744pub struct ColoredDiagnostic<'a, E>(&'a Diagnostic<E>, bool);
745
746impl<E> Diagnostic<E> {
747 pub fn colored(&self, colored: bool) -> ColoredDiagnostic<'_, E> {
752 ColoredDiagnostic(self, colored)
753 }
754}
755
756impl Error {
757 pub fn diagnostic(self) -> Diagnostic<Error> {
762 Diagnostic::from(self)
763 }
764}
765
766impl<E: std::error::Error> std::error::Error for ColoredDiagnostic<'_, E> {}
767
768impl<E: std::error::Error> std::fmt::Display for ColoredDiagnostic<'_, E> {
769 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
770 if self.1 {
771 write!(f, "{}", self.0.render_colored())
772 } else {
773 write!(f, "{}", self.0.render_plain())
774 }
775 }
776}