-
Notifications
You must be signed in to change notification settings - Fork 333
Structured resolver errors #2482
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
alexcrichton
merged 29 commits into
bytecodealliance:main
from
PhoebeSzmucer:structured-resolver-errors
May 5, 2026
Merged
Changes from 19 commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
b1bdd14
[wit-parser] Migrate to structured errors in the resolver
PhoebeSzmucer 2aeff69
Rename structs
PhoebeSzmucer 2bb93f0
Remove unneeded methods
PhoebeSzmucer 9177bad
Remove dangling doc
PhoebeSzmucer 8bce150
Merge branch 'main' into structured-resolver-errors
PhoebeSzmucer f924443
Doc improvements
PhoebeSzmucer 0641505
Update snapshot
PhoebeSzmucer 2043c6c
Don't reference a non-existent method in docs
PhoebeSzmucer 89df4c4
Fix bad merge
PhoebeSzmucer 0692157
sm_idx -> *source_maps_index
PhoebeSzmucer 6496897
Revert "sm_idx -> *source_maps_index"
PhoebeSzmucer d90a9f2
Remove push_groups, push_group returns a structured error
PhoebeSzmucer 151cda0
Remove stale reference
PhoebeSzmucer 9ab9544
Use fewer abbreviations
PhoebeSzmucer 69fe727
Introduce ResolveError::new_semantic
PhoebeSzmucer c44b8ba
Bring back push_groups because it makes it easier to test things
PhoebeSzmucer d489aea
Rename for consistency
PhoebeSzmucer 231996e
Fix typo
PhoebeSzmucer 1a01c31
Merge branch 'main' into structured-resolver-errors
PhoebeSzmucer a8d40f7
Preserve downcasting
PhoebeSzmucer 317f704
Add new variants to ResolveError
PhoebeSzmucer eaa481b
Pass a more specific span
PhoebeSzmucer 5641adf
Merge branch 'main' into structured-resolver-errors
PhoebeSzmucer 8a28ad7
Bless the snapshots
PhoebeSzmucer 8bd2e4c
Mention function name
PhoebeSzmucer 1050e4a
fmt
PhoebeSzmucer 0f2787b
bin fmt
PhoebeSzmucer bc4e225
Trim down comment
PhoebeSzmucer a027750
Tighter downcasting tests
PhoebeSzmucer File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,136 @@ | ||
| //! Error types for WIT package resolution. | ||
|
|
||
| use alloc::boxed::Box; | ||
| use alloc::format; | ||
| use alloc::string::{String, ToString}; | ||
| use alloc::vec::Vec; | ||
| use core::fmt; | ||
|
|
||
| use crate::{PackageName, SourceMap, Span}; | ||
|
|
||
| /// Convenience alias for a `Result` whose error type is [`ResolveError`]. | ||
| pub type ResolveResult<T, E = ResolveError> = Result<T, E>; | ||
|
|
||
| /// The category of error that occurred while resolving a WIT package. | ||
| #[non_exhaustive] | ||
| #[derive(Debug, PartialEq, Eq)] | ||
| pub enum ResolveErrorKind { | ||
| /// A referenced package could not be found among the known packages. | ||
| PackageNotFound { | ||
| span: Span, | ||
| requested: PackageName, | ||
| known: Vec<PackageName>, | ||
| }, | ||
| /// An interface has a transitive dependency that creates an incompatible | ||
| /// import relationship. | ||
| InvalidTransitiveDependency { span: Span, name: String }, | ||
| /// The same package is defined in two different locations. | ||
| DuplicatePackage { | ||
| name: PackageName, | ||
| span1: Span, | ||
| span2: Span, | ||
| }, | ||
| /// Packages form a dependency cycle. | ||
| PackageCycle { package: PackageName, span: Span }, | ||
| /// A semantic error during resolution (type mismatch, invalid use, etc.) | ||
| Semantic { span: Span, message: String }, | ||
| } | ||
|
|
||
| impl ResolveErrorKind { | ||
| /// Returns the source span associated with this error. | ||
| pub fn span(&self) -> Span { | ||
| match self { | ||
| ResolveErrorKind::PackageNotFound { span, .. } | ||
| | ResolveErrorKind::InvalidTransitiveDependency { span, .. } | ||
| | ResolveErrorKind::PackageCycle { span, .. } | ||
| | ResolveErrorKind::Semantic { span, .. } => *span, | ||
| ResolveErrorKind::DuplicatePackage { span1, .. } => *span1, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl fmt::Display for ResolveErrorKind { | ||
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| match self { | ||
| ResolveErrorKind::PackageNotFound { | ||
| requested, known, .. | ||
| } => { | ||
| if known.is_empty() { | ||
| write!(f, "package '{requested}' not found") | ||
| } else { | ||
| write!(f, "package '{requested}' not found. known packages:")?; | ||
| for k in known { | ||
| write!(f, "\n {k}")?; | ||
| } | ||
| Ok(()) | ||
| } | ||
| } | ||
| ResolveErrorKind::InvalidTransitiveDependency { name, .. } => write!( | ||
| f, | ||
| "interface `{name}` transitively depends on an interface in incompatible ways", | ||
| ), | ||
| ResolveErrorKind::DuplicatePackage { name, .. } => { | ||
| write!(f, "package `{name}` is defined in two different locations",) | ||
| } | ||
| ResolveErrorKind::PackageCycle { package, .. } => { | ||
| write!(f, "package `{package}` creates a dependency cycle") | ||
| } | ||
| ResolveErrorKind::Semantic { message, .. } => message.fmt(f), | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// A single structured error from resolving a WIT package. | ||
| #[derive(Debug, PartialEq, Eq)] | ||
| pub struct ResolveError(Box<ResolveErrorKind>); | ||
|
|
||
| impl ResolveError { | ||
| /// Creates a [`ResolveError`] with the [`ResolveErrorKind::Semantic`] variant. | ||
| pub fn new_semantic(span: Span, message: impl Into<String>) -> Self { | ||
| ResolveErrorKind::Semantic { | ||
| span, | ||
| message: message.into(), | ||
| } | ||
| .into() | ||
| } | ||
|
|
||
| /// Returns the underlying error kind. | ||
| pub fn kind(&self) -> &ResolveErrorKind { | ||
| &self.0 | ||
| } | ||
|
|
||
| /// Returns the underlying error kind (mutable). | ||
| pub fn kind_mut(&mut self) -> &mut ResolveErrorKind { | ||
| &mut self.0 | ||
| } | ||
|
|
||
| /// Format this error with source context (file:line:col + snippet). | ||
| pub fn highlight(&self, source_map: &SourceMap) -> String { | ||
| let e = self.kind(); | ||
| let msg = e.to_string(); | ||
| match e { | ||
| ResolveErrorKind::DuplicatePackage { name, span1, span2 } => { | ||
| let loc1 = source_map.render_location(*span1); | ||
| let loc2 = source_map.render_location(*span2); | ||
| format!( | ||
| "package `{name}` is defined in two different locations:\n * {loc1}\n * {loc2}" | ||
| ) | ||
| } | ||
| _ => source_map.highlight_span(e.span(), &msg).unwrap_or(msg), | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl fmt::Display for ResolveError { | ||
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
| fmt::Display::fmt(self.kind(), f) | ||
| } | ||
| } | ||
|
|
||
| impl core::error::Error for ResolveError {} | ||
|
|
||
| impl From<ResolveErrorKind> for ResolveError { | ||
| fn from(kind: ResolveErrorKind) -> Self { | ||
| ResolveError(Box::new(kind)) | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.