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