From 4ce2e044af0cf401ec4b481e7426d291eede1c5e Mon Sep 17 00:00:00 2001 From: Mikhail Katychev Date: Fri, 31 Jul 2026 14:23:49 -0500 Subject: [PATCH 1/4] feat(wit-parser): add more -not-found variants to `ResolveErrorKind` --- crates/wit-parser/src/resolve/error.rs | 26 +++++ crates/wit-parser/src/resolve/mod.rs | 106 +++++++++++------- .../tests/ui/parse-fail/bad-pkg2.wit.result | 2 +- .../tests/ui/parse-fail/bad-pkg3.wit.result | 2 +- .../ui/parse-fail/include-foreign.wit.result | 2 +- .../non-existence-world-include.wit.result | 2 +- .../tests/ui/parse-fail/use-world.wit.result | 2 +- 7 files changed, 96 insertions(+), 46 deletions(-) diff --git a/crates/wit-parser/src/resolve/error.rs b/crates/wit-parser/src/resolve/error.rs index ff7407c454..6cbf42a797 100644 --- a/crates/wit-parser/src/resolve/error.rs +++ b/crates/wit-parser/src/resolve/error.rs @@ -11,6 +11,10 @@ use crate::{PackageName, SourceMap, Span, Stability}; /// Convenience alias for a `Result` whose error type is [`ResolveError`]. pub type ResolveResult = Result; +// temporary alias since most errors do not return `Ident` +pub(crate) type WorldName = String; +pub(crate) type InterfaceName = String; + /// The category of error that occurred while resolving a WIT package. #[non_exhaustive] #[derive(Debug, PartialEq, Eq)] @@ -21,6 +25,20 @@ pub enum ResolveErrorKind { requested: PackageName, known: Vec, }, + + /// A referenced world could not be found for the provided package + WorldNotFound { + span: Span, + requested: WorldName, + package: PackageName, + }, + + /// A referenced interface could not be found for the provided package + InterfaceNotFound { + span: Span, + requested: InterfaceName, + package: PackageName, + }, /// An interface has a transitive dependency that creates an incompatible /// import relationship. InvalidTransitiveDependency { span: Span, name: String }, @@ -53,6 +71,8 @@ impl ResolveErrorKind { pub fn span(&self) -> Span { match self { ResolveErrorKind::PackageNotFound { span, .. } + | ResolveErrorKind::WorldNotFound { span, .. } + | ResolveErrorKind::InterfaceNotFound { span, .. } | ResolveErrorKind::InvalidTransitiveDependency { span, .. } | ResolveErrorKind::PackageCycle { span, .. } | ResolveErrorKind::ItemShadowing { span, .. } @@ -79,6 +99,12 @@ impl fmt::Display for ResolveErrorKind { Ok(()) } } + ResolveErrorKind::WorldNotFound { + requested, package, .. + } => write!(f, "world '{requested}' not found in package '{package}'"), + ResolveErrorKind::InterfaceNotFound { + requested, package, .. + } => write!(f, "interface '{requested}' not found in package '{package}'"), ResolveErrorKind::InvalidTransitiveDependency { name, .. } => write!( f, "interface `{name}` transitively depends on an interface in incompatible ways", diff --git a/crates/wit-parser/src/resolve/mod.rs b/crates/wit-parser/src/resolve/mod.rs index 8297d3f7c2..fe499bbdb6 100644 --- a/crates/wit-parser/src/resolve/mod.rs +++ b/crates/wit-parser/src/resolve/mod.rs @@ -3782,7 +3782,11 @@ impl Remap { } let iface_id = pkg.interfaces.get(interface).copied().ok_or_else(|| { - ResolveError::new_semantic(iface_span, "interface not found in package") + ResolveError::from(ResolveErrorKind::InterfaceNotFound { + span: iface_span, + requested: interface.to_string(), + package: pkg.name.clone(), + }) })?; assert_eq!(self.interfaces.len(), unresolved_iface_id.index()); self.interfaces.push(Some(iface_id)); @@ -3804,46 +3808,19 @@ impl Remap { parent_pkg_id: &PackageId, ) -> ResolveResult<()> { for (unresolved_world_id, unresolved_world) in unresolved.worlds.iter() { - let (pkg_name, world, span, stabilities) = - match world_to_package.get(&unresolved_world_id) { - Some(items) => *items, - // Same as above, all worlds are foreign until we find a - // non-foreign one. - None => break, - }; - - let pkgid = resolve - .package_names - .get(pkg_name) - .copied() - .ok_or_else(|| { - ResolveError::from(ResolveErrorKind::PackageNotFound { - span, - requested: pkg_name.clone(), - known: resolve.package_names.keys().cloned().collect(), - }) - })?; - let pkg = &resolve.packages[pkgid]; - let world_span = unresolved_world.span; - - let mut enabled = false; - for stability in stabilities { - if resolve.include_stability(stability, parent_pkg_id, world_span)? { - enabled = true; - break; - } - } - - if !enabled { - self.worlds.push(None); - continue; - } - - let world_id = pkg.worlds.get(world).copied().ok_or_else(|| { - ResolveError::new_semantic(world_span, "world not found in package") - })?; - assert_eq!(self.worlds.len(), unresolved_world_id.index()); - self.worlds.push(Some(world_id)); + let pkg = match world_to_package.get(&unresolved_world_id) { + Some(items) => *items, + // Same as above, all worlds are foreign until we find a + // non-foreign one. + None => break, + }; + self.process_foreign_world( + pkg, + unresolved_world_id, + unresolved_world, + resolve, + parent_pkg_id, + )?; } for (id, _) in unresolved.worlds.iter().skip(self.worlds.len()) { assert!( @@ -3854,6 +3831,53 @@ impl Remap { Ok(()) } + fn process_foreign_world( + &mut self, + (pkg_name, world, span, stabilities): (&PackageName, &String, Span, &Vec), + unresolved_world_id: Id, + unresolved_world: &World, + resolve: &mut Resolve, + parent_pkg_id: &PackageId, + ) -> ResolveResult<()> { + let pkgid = resolve + .package_names + .get(pkg_name) + .copied() + .ok_or_else(|| { + ResolveError::from(ResolveErrorKind::PackageNotFound { + span, + requested: pkg_name.clone(), + known: resolve.package_names.keys().cloned().collect(), + }) + })?; + let pkg = &resolve.packages[pkgid]; + let world_span = unresolved_world.span; + + let mut enabled = false; + for stability in stabilities { + if resolve.include_stability(stability, parent_pkg_id, world_span)? { + enabled = true; + break; + } + } + + if !enabled { + self.worlds.push(None); + return Ok(()); + } + + let world_id = pkg.worlds.get(world).copied().ok_or_else(|| { + ResolveError::from(ResolveErrorKind::WorldNotFound { + span: world_span, + requested: world.to_string(), + package: pkg.name.clone(), + }) + })?; + assert_eq!(self.worlds.len(), unresolved_world_id.index()); + self.worlds.push(Some(world_id)); + Ok(()) + } + fn process_foreign_types( &mut self, unresolved: &UnresolvedPackage, diff --git a/crates/wit-parser/tests/ui/parse-fail/bad-pkg2.wit.result b/crates/wit-parser/tests/ui/parse-fail/bad-pkg2.wit.result index 235a806d21..ca12daae49 100644 --- a/crates/wit-parser/tests/ui/parse-fail/bad-pkg2.wit.result +++ b/crates/wit-parser/tests/ui/parse-fail/bad-pkg2.wit.result @@ -1,4 +1,4 @@ -failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/bad-pkg2]: interface not found in package +failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/bad-pkg2]: interface 'nonexistent' not found in package 'foo:bar' --> tests/ui/parse-fail/bad-pkg2/root.wit:4:15 | 4 | use foo:bar/nonexistent.{}; diff --git a/crates/wit-parser/tests/ui/parse-fail/bad-pkg3.wit.result b/crates/wit-parser/tests/ui/parse-fail/bad-pkg3.wit.result index d3d2102d7b..59d9e2e14e 100644 --- a/crates/wit-parser/tests/ui/parse-fail/bad-pkg3.wit.result +++ b/crates/wit-parser/tests/ui/parse-fail/bad-pkg3.wit.result @@ -1,4 +1,4 @@ -failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/bad-pkg3]: interface not found in package +failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/bad-pkg3]: interface 'baz' not found in package 'foo:bar' --> tests/ui/parse-fail/bad-pkg3/root.wit:4:15 | 4 | use foo:bar/baz.{}; diff --git a/crates/wit-parser/tests/ui/parse-fail/include-foreign.wit.result b/crates/wit-parser/tests/ui/parse-fail/include-foreign.wit.result index 495d331006..6bd7b22433 100644 --- a/crates/wit-parser/tests/ui/parse-fail/include-foreign.wit.result +++ b/crates/wit-parser/tests/ui/parse-fail/include-foreign.wit.result @@ -1,4 +1,4 @@ -failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/include-foreign]: world not found in package +failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/include-foreign]: world 'bar' not found in package 'foo:bar' --> tests/ui/parse-fail/include-foreign/root.wit:4:19 | 4 | include foo:bar/bar; diff --git a/crates/wit-parser/tests/ui/parse-fail/non-existence-world-include.wit.result b/crates/wit-parser/tests/ui/parse-fail/non-existence-world-include.wit.result index 545cb1aca7..6d4d362305 100644 --- a/crates/wit-parser/tests/ui/parse-fail/non-existence-world-include.wit.result +++ b/crates/wit-parser/tests/ui/parse-fail/non-existence-world-include.wit.result @@ -1,4 +1,4 @@ -failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/non-existence-world-include]: world not found in package +failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/non-existence-world-include]: world 'non-existence' not found in package 'foo:baz' --> tests/ui/parse-fail/non-existence-world-include/root.wit:4:19 | 4 | include foo:baz/non-existence; diff --git a/crates/wit-parser/tests/ui/parse-fail/use-world.wit.result b/crates/wit-parser/tests/ui/parse-fail/use-world.wit.result index a85972579b..975def98c5 100644 --- a/crates/wit-parser/tests/ui/parse-fail/use-world.wit.result +++ b/crates/wit-parser/tests/ui/parse-fail/use-world.wit.result @@ -1,4 +1,4 @@ -failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/use-world]: interface not found in package +failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/use-world]: interface 'bar' not found in package 'foo:baz' --> tests/ui/parse-fail/use-world/root.wit:3:13 | 3 | use foo:baz/bar; From 2c8c2a866b2929c78a9600392b7c1cc19161e243 Mon Sep 17 00:00:00 2001 From: Mikhail Katychev Date: Fri, 31 Jul 2026 14:26:54 -0500 Subject: [PATCH 2/4] chore: typos --- crates/wit-parser/src/resolve/mod.rs | 2 +- tests/cli/component-model/custom-page-sizes.wast | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/wit-parser/src/resolve/mod.rs b/crates/wit-parser/src/resolve/mod.rs index fe499bbdb6..f66863681d 100644 --- a/crates/wit-parser/src/resolve/mod.rs +++ b/crates/wit-parser/src/resolve/mod.rs @@ -3095,7 +3095,7 @@ impl Resolve { // never being importable-from it means that mutations to `CloneMaps` // are discarded when named interfaces are cloned. The trick here // happens where this unconditionally preserves all modifications to - // `CloneMaps` for `WorldKey::Interface` clones. Behaivor then "falls + // `CloneMaps` for `WorldKey::Interface` clones. Behavior then "falls // out" where references to cloned interfaces are naturally rewritten. // // This all falls down, however, if an import is cloned and recorded. diff --git a/tests/cli/component-model/custom-page-sizes.wast b/tests/cli/component-model/custom-page-sizes.wast index 28c56f4d87..d56cb84858 100644 --- a/tests/cli/component-model/custom-page-sizes.wast +++ b/tests/cli/component-model/custom-page-sizes.wast @@ -20,7 +20,7 @@ (core func (canon lower (func $f) (memory (core memory $i "m")))) ) -;; subtyping works with explict page sizes +;; subtyping works with explicit page sizes (component (core module $a (memory (export "m") 1 (pagesize 65536))) (core module $b (import "a" "m" (memory 1 (pagesize 65536)))) From 1024004bf551160836bacc3c576b780b5b0b0341 Mon Sep 17 00:00:00 2001 From: Mikhail Katychev Date: Fri, 31 Jul 2026 14:30:39 -0500 Subject: [PATCH 3/4] revert: remove process_foreign_world function --- crates/wit-parser/src/resolve/mod.rs | 104 ++++++++++++--------------- 1 file changed, 44 insertions(+), 60 deletions(-) diff --git a/crates/wit-parser/src/resolve/mod.rs b/crates/wit-parser/src/resolve/mod.rs index f66863681d..388e2767b7 100644 --- a/crates/wit-parser/src/resolve/mod.rs +++ b/crates/wit-parser/src/resolve/mod.rs @@ -3808,19 +3808,50 @@ impl Remap { parent_pkg_id: &PackageId, ) -> ResolveResult<()> { for (unresolved_world_id, unresolved_world) in unresolved.worlds.iter() { - let pkg = match world_to_package.get(&unresolved_world_id) { - Some(items) => *items, - // Same as above, all worlds are foreign until we find a - // non-foreign one. - None => break, - }; - self.process_foreign_world( - pkg, - unresolved_world_id, - unresolved_world, - resolve, - parent_pkg_id, - )?; + let (pkg_name, world, span, stabilities) = + match world_to_package.get(&unresolved_world_id) { + Some(items) => *items, + // Same as above, all worlds are foreign until we find a + // non-foreign one. + None => break, + }; + + let pkgid = resolve + .package_names + .get(pkg_name) + .copied() + .ok_or_else(|| { + ResolveError::from(ResolveErrorKind::PackageNotFound { + span, + requested: pkg_name.clone(), + known: resolve.package_names.keys().cloned().collect(), + }) + })?; + let pkg = &resolve.packages[pkgid]; + let world_span = unresolved_world.span; + + let mut enabled = false; + for stability in stabilities { + if resolve.include_stability(stability, parent_pkg_id, world_span)? { + enabled = true; + break; + } + } + + if !enabled { + self.worlds.push(None); + continue; + } + + let world_id = pkg.worlds.get(world).copied().ok_or_else(|| { + ResolveError::from(ResolveErrorKind::WorldNotFound { + span: world_span, + requested: world.to_string(), + package: pkg.name.clone(), + }) + })?; + assert_eq!(self.worlds.len(), unresolved_world_id.index()); + self.worlds.push(Some(world_id)); } for (id, _) in unresolved.worlds.iter().skip(self.worlds.len()) { assert!( @@ -3831,53 +3862,6 @@ impl Remap { Ok(()) } - fn process_foreign_world( - &mut self, - (pkg_name, world, span, stabilities): (&PackageName, &String, Span, &Vec), - unresolved_world_id: Id, - unresolved_world: &World, - resolve: &mut Resolve, - parent_pkg_id: &PackageId, - ) -> ResolveResult<()> { - let pkgid = resolve - .package_names - .get(pkg_name) - .copied() - .ok_or_else(|| { - ResolveError::from(ResolveErrorKind::PackageNotFound { - span, - requested: pkg_name.clone(), - known: resolve.package_names.keys().cloned().collect(), - }) - })?; - let pkg = &resolve.packages[pkgid]; - let world_span = unresolved_world.span; - - let mut enabled = false; - for stability in stabilities { - if resolve.include_stability(stability, parent_pkg_id, world_span)? { - enabled = true; - break; - } - } - - if !enabled { - self.worlds.push(None); - return Ok(()); - } - - let world_id = pkg.worlds.get(world).copied().ok_or_else(|| { - ResolveError::from(ResolveErrorKind::WorldNotFound { - span: world_span, - requested: world.to_string(), - package: pkg.name.clone(), - }) - })?; - assert_eq!(self.worlds.len(), unresolved_world_id.index()); - self.worlds.push(Some(world_id)); - Ok(()) - } - fn process_foreign_types( &mut self, unresolved: &UnresolvedPackage, From ff0d1ff48253330002eee6689fe3a73a48739bca Mon Sep 17 00:00:00 2001 From: Mikhail Katychev Date: Fri, 31 Jul 2026 14:32:51 -0500 Subject: [PATCH 4/4] fix: cargo fmt --- crates/wit-parser/src/resolve/error.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/wit-parser/src/resolve/error.rs b/crates/wit-parser/src/resolve/error.rs index 6cbf42a797..526aa9887e 100644 --- a/crates/wit-parser/src/resolve/error.rs +++ b/crates/wit-parser/src/resolve/error.rs @@ -104,7 +104,10 @@ impl fmt::Display for ResolveErrorKind { } => write!(f, "world '{requested}' not found in package '{package}'"), ResolveErrorKind::InterfaceNotFound { requested, package, .. - } => write!(f, "interface '{requested}' not found in package '{package}'"), + } => write!( + f, + "interface '{requested}' not found in package '{package}'" + ), ResolveErrorKind::InvalidTransitiveDependency { name, .. } => write!( f, "interface `{name}` transitively depends on an interface in incompatible ways",