Skip to main content

wit_parser/resolve/
error.rs

1//! Error types for WIT package resolution.
2
3use alloc::boxed::Box;
4use alloc::format;
5use alloc::string::{String, ToString};
6use alloc::vec::Vec;
7use core::fmt::{self};
8
9use crate::{PackageName, SourceMap, Span, Stability};
10
11/// Convenience alias for a `Result` whose error type is [`ResolveError`].
12pub type ResolveResult<T, E = ResolveError> = Result<T, E>;
13
14/// The category of error that occurred while resolving a WIT package.
15#[non_exhaustive]
16#[derive(Debug, PartialEq, Eq)]
17pub enum ResolveErrorKind {
18    /// A referenced package could not be found among the known packages.
19    PackageNotFound {
20        span: Span,
21        requested: PackageName,
22        known: Vec<PackageName>,
23    },
24    /// An interface has a transitive dependency that creates an incompatible
25    /// import relationship.
26    InvalidTransitiveDependency { span: Span, name: String },
27    /// The same package is defined in two different locations.
28    DuplicatePackage {
29        name: PackageName,
30        span1: Span,
31        span2: Span,
32    },
33    /// Packages form a dependency cycle.
34    PackageCycle { package: PackageName, span: Span },
35    /// A world item shadows a previously-included item of the same kind
36    ItemShadowing {
37        span: Span,
38        item_type: String,
39        name: String,
40    },
41    /// Two stability annotations conflict during merge
42    StabilityMismatch {
43        span: Span,
44        from: Stability,
45        into: Stability,
46    },
47    /// A semantic error during resolution (type mismatch, invalid use, etc.)
48    Semantic { span: Span, message: String },
49}
50
51impl ResolveErrorKind {
52    /// Returns the source span associated with this error.
53    pub fn span(&self) -> Span {
54        match self {
55            ResolveErrorKind::PackageNotFound { span, .. }
56            | ResolveErrorKind::InvalidTransitiveDependency { span, .. }
57            | ResolveErrorKind::PackageCycle { span, .. }
58            | ResolveErrorKind::ItemShadowing { span, .. }
59            | ResolveErrorKind::StabilityMismatch { span, .. }
60            | ResolveErrorKind::Semantic { span, .. } => *span,
61            ResolveErrorKind::DuplicatePackage { span1, .. } => *span1,
62        }
63    }
64}
65
66impl fmt::Display for ResolveErrorKind {
67    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68        match self {
69            ResolveErrorKind::PackageNotFound {
70                requested, known, ..
71            } => {
72                if known.is_empty() {
73                    write!(f, "package '{requested}' not found")
74                } else {
75                    write!(f, "package '{requested}' not found. known packages:")?;
76                    for k in known {
77                        write!(f, "\n    {k}")?;
78                    }
79                    Ok(())
80                }
81            }
82            ResolveErrorKind::InvalidTransitiveDependency { name, .. } => write!(
83                f,
84                "interface `{name}` transitively depends on an interface in incompatible ways",
85            ),
86            ResolveErrorKind::DuplicatePackage { name, .. } => {
87                write!(f, "package `{name}` is defined in two different locations",)
88            }
89            ResolveErrorKind::PackageCycle { package, .. } => {
90                write!(f, "package `{package}` creates a dependency cycle")
91            }
92            ResolveErrorKind::ItemShadowing {
93                item_type, name, ..
94            } => {
95                write!(
96                    f,
97                    "{item_type} of `{name}` shadows previously {item_type}ed items"
98                )
99            }
100            ResolveErrorKind::StabilityMismatch { from, into, .. } => {
101                write!(f, "mismatch in stability from '{from:?}' to '{into:?}'")
102            }
103            ResolveErrorKind::Semantic { message, .. } => message.fmt(f),
104        }
105    }
106}
107
108/// A single structured error from resolving a WIT package.
109#[derive(Debug, PartialEq, Eq)]
110pub struct ResolveError(Box<ResolveErrorKind>);
111
112impl ResolveError {
113    /// Creates a [`ResolveError`] with the [`ResolveErrorKind::Semantic`] variant.
114    pub fn new_semantic(span: Span, message: impl Into<String>) -> Self {
115        ResolveErrorKind::Semantic {
116            span,
117            message: message.into(),
118        }
119        .into()
120    }
121
122    /// Returns the underlying error kind.
123    pub fn kind(&self) -> &ResolveErrorKind {
124        &self.0
125    }
126
127    /// Returns the underlying error kind (mutable).
128    pub fn kind_mut(&mut self) -> &mut ResolveErrorKind {
129        &mut self.0
130    }
131
132    /// Format this error with source context (file:line:col + snippet).
133    pub fn highlight(&self, source_map: &SourceMap) -> String {
134        let e = self.kind();
135        let msg = e.to_string();
136        match e {
137            ResolveErrorKind::DuplicatePackage { name, span1, span2 } => {
138                let loc1 = source_map.render_location(*span1);
139                let loc2 = source_map.render_location(*span2);
140                format!(
141                    "package `{name}` is defined in two different locations:\n  * {loc1}\n  * {loc2}"
142                )
143            }
144            _ => source_map.highlight_span(e.span(), &msg).unwrap_or(msg),
145        }
146    }
147}
148
149impl fmt::Display for ResolveError {
150    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151        fmt::Display::fmt(self.kind(), f)
152    }
153}
154
155impl core::error::Error for ResolveError {}
156
157impl From<ResolveErrorKind> for ResolveError {
158    fn from(kind: ResolveErrorKind) -> Self {
159        ResolveError(Box::new(kind))
160    }
161}