Coverage Report

Created: 2026-08-28 08:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wasm-tools/crates/wit-parser/src/resolve/error.rs
Line
Count
Source
1
//! Error types for WIT package resolution.
2
3
use alloc::boxed::Box;
4
use alloc::format;
5
use alloc::string::{String, ToString};
6
use alloc::vec::Vec;
7
use core::fmt::{self};
8
9
use crate::{PackageName, SourceMap, Span, Stability};
10
11
/// Convenience alias for a `Result` whose error type is [`ResolveError`].
12
pub type ResolveResult<T, E = ResolveError> = Result<T, E>;
13
14
// temporary alias since most errors do not return `Ident`
15
pub(crate) type WorldName = String;
16
pub(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)]
21
pub 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
69
impl ResolveErrorKind {
70
    /// Returns the source span associated with this error.
71
0
    pub fn span(&self) -> Span {
72
0
        match self {
73
0
            ResolveErrorKind::PackageNotFound { span, .. }
74
0
            | ResolveErrorKind::WorldNotFound { span, .. }
75
0
            | ResolveErrorKind::InterfaceNotFound { span, .. }
76
0
            | ResolveErrorKind::InvalidTransitiveDependency { span, .. }
77
0
            | ResolveErrorKind::PackageCycle { span, .. }
78
0
            | ResolveErrorKind::ItemShadowing { span, .. }
79
0
            | ResolveErrorKind::StabilityMismatch { span, .. }
80
0
            | ResolveErrorKind::Semantic { span, .. } => *span,
81
0
            ResolveErrorKind::DuplicatePackage { span1, .. } => *span1,
82
        }
83
0
    }
84
}
85
86
impl fmt::Display for ResolveErrorKind {
87
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88
0
        match self {
89
            ResolveErrorKind::PackageNotFound {
90
0
                requested, known, ..
91
            } => {
92
0
                if known.is_empty() {
93
0
                    write!(f, "package '{requested}' not found")
94
                } else {
95
0
                    write!(f, "package '{requested}' not found. known packages:")?;
96
0
                    for k in known {
97
0
                        write!(f, "\n    {k}")?;
98
                    }
99
0
                    Ok(())
100
                }
101
            }
102
            ResolveErrorKind::WorldNotFound {
103
0
                requested, package, ..
104
0
            } => write!(f, "world '{requested}' not found in package '{package}'"),
105
            ResolveErrorKind::InterfaceNotFound {
106
0
                requested, package, ..
107
0
            } => write!(
108
0
                f,
109
                "interface '{requested}' not found in package '{package}'"
110
            ),
111
0
            ResolveErrorKind::InvalidTransitiveDependency { name, .. } => write!(
112
0
                f,
113
                "interface `{name}` transitively depends on an interface in incompatible ways",
114
            ),
115
0
            ResolveErrorKind::DuplicatePackage { name, .. } => {
116
0
                write!(f, "package `{name}` is defined in two different locations",)
117
            }
118
0
            ResolveErrorKind::PackageCycle { package, .. } => {
119
0
                write!(f, "package `{package}` creates a dependency cycle")
120
            }
121
            ResolveErrorKind::ItemShadowing {
122
0
                item_type, name, ..
123
            } => {
124
0
                write!(
125
0
                    f,
126
                    "{item_type} of `{name}` shadows previously {item_type}ed items"
127
                )
128
            }
129
0
            ResolveErrorKind::StabilityMismatch { from, into, .. } => {
130
0
                write!(f, "mismatch in stability from '{from:?}' to '{into:?}'")
131
            }
132
0
            ResolveErrorKind::Semantic { message, .. } => message.fmt(f),
133
        }
134
0
    }
135
}
136
137
/// A single structured error from resolving a WIT package.
138
#[derive(Debug, PartialEq, Eq)]
139
pub struct ResolveError(Box<ResolveErrorKind>);
140
141
impl ResolveError {
142
    /// Creates a [`ResolveError`] with the [`ResolveErrorKind::Semantic`] variant.
143
0
    pub fn new_semantic(span: Span, message: impl Into<String>) -> Self {
144
0
        ResolveErrorKind::Semantic {
145
0
            span,
146
0
            message: message.into(),
147
0
        }
148
0
        .into()
149
0
    }
Unexecuted instantiation: <wit_parser::resolve::error::ResolveError>::new_semantic::<alloc::string::String>
Unexecuted instantiation: <wit_parser::resolve::error::ResolveError>::new_semantic::<&str>
150
151
    /// Returns the underlying error kind.
152
17
    pub fn kind(&self) -> &ResolveErrorKind {
153
17
        &self.0
154
17
    }
155
156
    /// Returns the underlying error kind (mutable).
157
0
    pub fn kind_mut(&mut self) -> &mut ResolveErrorKind {
158
0
        &mut self.0
159
0
    }
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
0
    pub fn render(&self, source_map: &SourceMap) -> String {
165
0
        let e = self.kind();
166
0
        let msg = e.to_string();
167
0
        match e {
168
0
            ResolveErrorKind::DuplicatePackage { name, span1, span2 } => {
169
0
                let loc1 = source_map.render_location(*span1);
170
0
                let loc2 = source_map.render_location(*span2);
171
0
                format!(
172
                    "package `{name}` is defined in two different locations:\n  * {loc1}\n  * {loc2}"
173
                )
174
            }
175
0
            _ => source_map.highlight_span(e.span(), &msg).unwrap_or(msg),
176
        }
177
0
    }
178
}
179
180
impl fmt::Display for ResolveError {
181
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
182
0
        fmt::Display::fmt(self.kind(), f)
183
0
    }
184
}
185
186
impl core::error::Error for ResolveError {}
187
188
impl From<ResolveErrorKind> for ResolveError {
189
17
    fn from(kind: ResolveErrorKind) -> Self {
190
17
        ResolveError(Box::new(kind))
191
17
    }
192
}