Repository: bytecodealliance/wasm-tools
Pull request #1761 (2024-09-05 19:45:34)

Fix running tests with `RUST_BACKTRACE=1`
The previous version of test output used the `:?` formatter for `anyhow::Error` which would include the native backtrace when `RUST_BACKTRACE=1` is set. This commit updates formatters to use `:#` which is a bit less readable but doesn't include the backtrace when this env var is set. Additionally a test is added to CI to ensure this doesn't regress in the future.

This is adapted from #1578.

Closes #1578


Patch:
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 3e5bebcd..df414bbc 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -92,6 +92,13 @@ jobs:
           # want this to break.
           - os: ubuntu-latest
             rust: nightly-2024-02-12
+          # test that if `RUST_BACKTRACE=1` is set in the environment that all
+          # tests with blessed error messages still pass.
+          - os: ubuntu-latest
+            rust: default
+            env:
+              RUST_BACKTRACE: 1
+    env: ${{ matrix.env || fromJSON('{}') }}
     steps:
     - uses: actions/checkout@v4
       with:
diff --git a/.github/workflows/release-process.yml b/.github/workflows/release-process.yml
index d6b9b060..a9f9f914 100644
--- a/.github/workflows/release-process.yml
+++ b/.github/workflows/release-process.yml
@@ -16,6 +16,10 @@ on:
         required: false
         default: 'bump'
 
+permissions:
+  contents: write
+  pull-requests: write
+
 jobs:
   release_process:
     name: Run the release process
diff --git a/crates/wasmparser/src/features.rs b/crates/wasmparser/src/features.rs
index 4290ff42..30c8a2f1 100644
--- a/crates/wasmparser/src/features.rs
+++ b/crates/wasmparser/src/features.rs
@@ -229,26 +229,39 @@ define_wasm_features! {
 }
 
 impl WasmFeatures {
-    /// Returns the feature set associated with the 1.0 version of the
+    /// The feature set associated with the 1.0 version of the
     /// WebAssembly specification or the "MVP" feature set.
     #[cfg(feature = "features")]
-    pub fn wasm1() -> WasmFeatures {
-        WasmFeatures::FLOATS | WasmFeatures::GC_TYPES
-    }
+    pub const WASM1: WasmFeatures = WasmFeatures::FLOATS.union(WasmFeatures::GC_TYPES);
 
-    /// Returns the feature set associated with the 2.0 version of the
+    /// The feature set associated with the 2.0 version of the
     /// WebAssembly specification.
     #[cfg(feature = "features")]
-    pub fn wasm2() -> WasmFeatures {
-        WasmFeatures::wasm1()
-            | WasmFeatures::BULK_MEMORY
-            | WasmFeatures::REFERENCE_TYPES
-            | WasmFeatures::SIGN_EXTENSION
-            | WasmFeatures::MUTABLE_GLOBAL
-            | WasmFeatures::SATURATING_FLOAT_TO_INT
-            | WasmFeatures::MULTI_VALUE
-            | WasmFeatures::SIMD
-    }
+    pub const WASM2: WasmFeatures = WasmFeatures::WASM1
+        .union(WasmFeatures::BULK_MEMORY)
+        .union(WasmFeatures::REFERENCE_TYPES)
+        .union(WasmFeatures::SIGN_EXTENSION)
+        .union(WasmFeatures::MUTABLE_GLOBAL)
+        .union(WasmFeatures::SATURATING_FLOAT_TO_INT)
+        .union(WasmFeatures::MULTI_VALUE)
+        .union(WasmFeatures::SIMD);
+
+    /// The feature set associated with the 3.0 version of the
+    /// WebAssembly specification.
+    ///
+    /// Note that as of the time of this writing the 3.0 version of the
+    /// specification is not yet published. The precise set of features set
+    /// here may change as that continues to evolve.
+    #[cfg(feature = "features")]
+    pub const WASM3: WasmFeatures = WasmFeatures::WASM2
+        .union(WasmFeatures::GC)
+        .union(WasmFeatures::TAIL_CALL)
+        .union(WasmFeatures::EXTENDED_CONST)
+        .union(WasmFeatures::FUNCTION_REFERENCES)
+        .union(WasmFeatures::MULTI_MEMORY)
+        .union(WasmFeatures::RELAXED_SIMD)
+        .union(WasmFeatures::THREADS)
+        .union(WasmFeatures::EXCEPTIONS);
 }
 
 #[cfg(feature = "features")]
diff --git a/src/bin/wasm-tools/validate.rs b/src/bin/wasm-tools/validate.rs
index 7283c4da..a065665d 100644
--- a/src/bin/wasm-tools/validate.rs
+++ b/src/bin/wasm-tools/validate.rs
@@ -1,5 +1,5 @@
 use addr2line::LookupResult;
-use anyhow::{anyhow, bail, Context, Result};
+use anyhow::{bail, Context, Result};
 use bitflags::Flags;
 use rayon::prelude::*;
 use std::fmt::Write;
@@ -183,53 +183,76 @@ impl Opts {
 fn parse_features(arg: &str) -> Result<WasmFeatures> {
     let mut ret = WasmFeatures::default();
 
-    fn flag_name(flag: &bitflags::Flag<WasmFeatures>) -> String {
-        flag.name().to_lowercase().replace('_', "-")
+    const GROUPS: &[(&str, WasmFeatures)] = &[
+        ("mvp", WasmFeatures::WASM1),
+        ("wasm1", WasmFeatures::WASM1),
+        ("wasm2", WasmFeatures::WASM2),
+        ("wasm3", WasmFeatures::WASM3),
+    ];
+
+    enum Action {
+        ChangeAll,
+        Group(WasmFeatures),
+        Modify(WasmFeatures),
+    }
+
+    fn actions() -> impl Iterator<Item = (&'static str, Action)> {
+        WasmFeatures::FLAGS
+            .iter()
+            .map(|f| (f.name(), Action::Modify(*f.value())))
+            .chain(
+                GROUPS
+                    .iter()
+                    .map(|(name, features)| (*name, Action::Group(*features))),
+            )
+            .chain([("all", Action::ChangeAll)])
+    }
+
+    fn flag_name(name: &str) -> String {
+        name.to_lowercase().replace('_', "-")
     }
 
-    for part in arg.split(',').map(|s| s.trim()).filter(|s| !s.is_empty()) {
+    'outer: for part in arg.split(',').map(|s| s.trim()).filter(|s| !s.is_empty()) {
         let (enable, part) = if let Some(part) = part.strip_prefix("-") {
             (false, part)
         } else {
             (true, part)
         };
-        match part {
-            "all" => {
-                for flag in WasmFeatures::FLAGS.iter() {
-                    ret.set(*flag.value(), enable);
-                }
+        for (name, action) in actions() {
+            if part != flag_name(name) {
+                continue;
             }
-            "wasm1" | "mvp" => {
-                if !enable {
-                    bail!("cannot disable `{part}`, it can only be enabled");
+            match action {
+                Action::ChangeAll => {
+                    for flag in WasmFeatures::FLAGS.iter() {
+                        ret.set(*flag.value(), enable);
+                    }
                 }
-                ret = WasmFeatures::wasm1();
-            }
-            "wasm2" => {
-                if !enable {
-                    bail!("cannot disable `{part}`, it can only be enabled");
+                Action::Modify(feature) => {
+                    ret.set(feature, enable);
+                }
+                Action::Group(features) => {
+                    if !enable {
+                        bail!("cannot disable `{part}`, it can only be enabled");
+                    }
+                    ret = features;
                 }
-                ret = WasmFeatures::wasm2();
             }
+            continue 'outer;
+        }
 
-            name => {
-                let flag = WasmFeatures::FLAGS
-                    .iter()
-                    .find(|f| flag_name(f) == name)
-                    .ok_or_else(|| {
-                        anyhow!(
-                            "unknown feature `{}`\nValid features: {}",
-                            name,
-                            WasmFeatures::FLAGS
-                                .iter()
-                                .map(flag_name)
-                                .collect::<Vec<_>>()
-                                .join(", "),
-                        )
-                    })?;
-                ret.set(*flag.value(), enable);
+        let mut error = format!("unknown feature `{part}`\n");
+        error.push_str("Valid features: ");
+        let mut first = true;
+        for (name, _) in actions() {
+            if first {
+                first = false;
+            } else {
+                error.push_str(", ");
             }
+            error.push_str(&flag_name(name));
         }
+        bail!("{error}")
     }
 
     Ok(ret)


Test patch:
diff --git a/crates/wasm-smith/tests/common/mod.rs b/crates/wasm-smith/tests/common/mod.rs
index 332fd0ca..688bb87f 100644
--- a/crates/wasm-smith/tests/common/mod.rs
+++ b/crates/wasm-smith/tests/common/mod.rs
@@ -2,7 +2,7 @@ use wasm_smith::Config;
 use wasmparser::{types::Types, Validator, WasmFeatures};
 
 pub fn parser_features_from_config(config: &Config) -> WasmFeatures {
-    let mut features = WasmFeatures::MUTABLE_GLOBAL | WasmFeatures::wasm1();
+    let mut features = WasmFeatures::MUTABLE_GLOBAL | WasmFeatures::WASM1;
     features.set(
         WasmFeatures::SATURATING_FLOAT_TO_INT,
         config.saturating_float_to_int_enabled,
diff --git a/crates/wit-component/tests/components.rs b/crates/wit-component/tests/components.rs
index 8f2951f9..3684b8ec 100644
--- a/crates/wit-component/tests/components.rs
+++ b/crates/wit-component/tests/components.rs
@@ -147,7 +147,7 @@ fn run_test(path: &Path) -> Result<()> {
             if !test_case.starts_with("error-") {
                 return Err(err);
             }
-            assert_output(&format!("{err:?}"), &error_path)?;
+            assert_output(&format!("{err:#}"), &error_path)?;
             return Ok(());
         }
     };
diff --git a/crates/wit-component/tests/components/error-default-export-sig-mismatch/error.txt b/crates/wit-component/tests/components/error-default-export-sig-mismatch/error.txt
index 904d1aef..72264c4a 100644
--- a/crates/wit-component/tests/components/error-default-export-sig-mismatch/error.txt
+++ b/crates/wit-component/tests/components/error-default-export-sig-mismatch/error.txt
@@ -1,5 +1,1 @@
-failed to decode world from module
-
-Caused by:
-    0: module was not valid
-    1: type mismatch for function `a`: expected `[I32, I32] -> [I32]` but found `[] -> []`
\ No newline at end of file
+failed to decode world from module: module was not valid: type mismatch for function `a`: expected `[I32, I32] -> [I32]` but found `[] -> []`
\ No newline at end of file
diff --git a/crates/wit-component/tests/components/error-empty-module-import/error.txt b/crates/wit-component/tests/components/error-empty-module-import/error.txt
index 75f7337b..f68bb1b6 100644
--- a/crates/wit-component/tests/components/error-empty-module-import/error.txt
+++ b/crates/wit-component/tests/components/error-empty-module-import/error.txt
@@ -1,5 +1,1 @@
-failed to decode world from module
-
-Caused by:
-    0: module was not valid
-    1: no top-level imported function `foo` specified
\ No newline at end of file
+failed to decode world from module: module was not valid: no top-level imported function `foo` specified
\ No newline at end of file
diff --git a/crates/wit-component/tests/components/error-export-sig-mismatch/error.txt b/crates/wit-component/tests/components/error-export-sig-mismatch/error.txt
index 0588ebcf..3b1c6afa 100644
--- a/crates/wit-component/tests/components/error-export-sig-mismatch/error.txt
+++ b/crates/wit-component/tests/components/error-export-sig-mismatch/error.txt
@@ -1,6 +1,1 @@
-failed to decode world from module
-
-Caused by:
-    0: module was not valid
-    1: failed to validate exported interface `foo`
-    2: type mismatch for function `a`: expected `[I32, I32] -> [I32]` but found `[] -> []`
\ No newline at end of file
+failed to decode world from module: module was not valid: failed to validate exported interface `foo`: type mismatch for function `a`: expected `[I32, I32] -> [I32]` but found `[] -> []`
\ No newline at end of file
diff --git a/crates/wit-component/tests/components/error-import-resource-rep/error.txt b/crates/wit-component/tests/components/error-import-resource-rep/error.txt
index f9f291d9..7dd176e6 100644
--- a/crates/wit-component/tests/components/error-import-resource-rep/error.txt
+++ b/crates/wit-component/tests/components/error-import-resource-rep/error.txt
@@ -1,5 +1,1 @@
-failed to decode world from module
-
-Caused by:
-    0: module was not valid
-    1: no top-level imported function `[resource-rep]a` specified
\ No newline at end of file
+failed to decode world from module: module was not valid: no top-level imported function `[resource-rep]a` specified
\ No newline at end of file
diff --git a/crates/wit-component/tests/components/error-import-resource-wrong-signature/error.txt b/crates/wit-component/tests/components/error-import-resource-wrong-signature/error.txt
index e76b70fe..d7eaac43 100644
--- a/crates/wit-component/tests/components/error-import-resource-wrong-signature/error.txt
+++ b/crates/wit-component/tests/components/error-import-resource-wrong-signature/error.txt
@@ -1,5 +1,1 @@
-failed to decode world from module
-
-Caused by:
-    0: module was not valid
-    1: type mismatch for function `[resource-drop]a`: expected `[I32] -> []` but found `[I32] -> [I32]`
\ No newline at end of file
+failed to decode world from module: module was not valid: type mismatch for function `[resource-drop]a`: expected `[I32] -> []` but found `[I32] -> [I32]`
\ No newline at end of file
diff --git a/crates/wit-component/tests/components/error-import-sig-mismatch/error.txt b/crates/wit-component/tests/components/error-import-sig-mismatch/error.txt
index 57dae399..41ba5e77 100644
--- a/crates/wit-component/tests/components/error-import-sig-mismatch/error.txt
+++ b/crates/wit-component/tests/components/error-import-sig-mismatch/error.txt
@@ -1,6 +1,1 @@
-failed to decode world from module
-
-Caused by:
-    0: module was not valid
-    1: failed to validate import interface `foo`
-    2: type mismatch for function `bar`: expected `[I32, I32] -> []` but found `[] -> []`
\ No newline at end of file
+failed to decode world from module: module was not valid: failed to validate import interface `foo`: type mismatch for function `bar`: expected `[I32, I32] -> []` but found `[] -> []`
\ No newline at end of file
diff --git a/crates/wit-component/tests/components/error-invalid-module-import/error.txt b/crates/wit-component/tests/components/error-invalid-module-import/error.txt
index 4a54c821..4c5195ec 100644
--- a/crates/wit-component/tests/components/error-invalid-module-import/error.txt
+++ b/crates/wit-component/tests/components/error-invalid-module-import/error.txt
@@ -1,5 +1,1 @@
-failed to decode world from module
-
-Caused by:
-    0: module was not valid
-    1: module is only allowed to import functions
\ No newline at end of file
+failed to decode world from module: module was not valid: module is only allowed to import functions
\ No newline at end of file
diff --git a/crates/wit-component/tests/components/error-missing-default-export/error.txt b/crates/wit-component/tests/components/error-missing-default-export/error.txt
index 1dc1b01f..cd0b78a0 100644
--- a/crates/wit-component/tests/components/error-missing-default-export/error.txt
+++ b/crates/wit-component/tests/components/error-missing-default-export/error.txt
@@ -1,5 +1,1 @@
-failed to decode world from module
-
-Caused by:
-    0: module was not valid
-    1: module does not export required function `a`
\ No newline at end of file
+failed to decode world from module: module was not valid: module does not export required function `a`
\ No newline at end of file
diff --git a/crates/wit-component/tests/components/error-missing-export/error.txt b/crates/wit-component/tests/components/error-missing-export/error.txt
index 6d4148bd..0829150e 100644
--- a/crates/wit-component/tests/components/error-missing-export/error.txt
+++ b/crates/wit-component/tests/components/error-missing-export/error.txt
@@ -1,6 +1,1 @@
-failed to decode world from module
-
-Caused by:
-    0: module was not valid
-    1: failed to validate exported interface `foo`
-    2: module does not export required function `foo#a`
\ No newline at end of file
+failed to decode world from module: module was not valid: failed to validate exported interface `foo`: module does not export required function `foo#a`
\ No newline at end of file
diff --git a/crates/wit-component/tests/components/error-missing-import-func/error.txt b/crates/wit-component/tests/components/error-missing-import-func/error.txt
index 388a6087..b6f4f0dd 100644
--- a/crates/wit-component/tests/components/error-missing-import-func/error.txt
+++ b/crates/wit-component/tests/components/error-missing-import-func/error.txt
@@ -1,5 +1,1 @@
-failed to decode world from module
-
-Caused by:
-    0: module was not valid
-    1: module requires an import interface named `foo`
\ No newline at end of file
+failed to decode world from module: module was not valid: module requires an import interface named `foo`
\ No newline at end of file
diff --git a/crates/wit-component/tests/components/error-missing-import/error.txt b/crates/wit-component/tests/components/error-missing-import/error.txt
index 388a6087..b6f4f0dd 100644
--- a/crates/wit-component/tests/components/error-missing-import/error.txt
+++ b/crates/wit-component/tests/components/error-missing-import/error.txt
@@ -1,5 +1,1 @@
-failed to decode world from module
-
-Caused by:
-    0: module was not valid
-    1: module requires an import interface named `foo`
\ No newline at end of file
+failed to decode world from module: module was not valid: module requires an import interface named `foo`
\ No newline at end of file
diff --git a/crates/wit-component/tests/components/error-missing-module-metadata/error.txt b/crates/wit-component/tests/components/error-missing-module-metadata/error.txt
index 16f77976..a256db38 100644
--- a/crates/wit-component/tests/components/error-missing-module-metadata/error.txt
+++ b/crates/wit-component/tests/components/error-missing-module-metadata/error.txt
@@ -1,4 +1,1 @@
-failed to register indirect shims for main module
-
-Caused by:
-    missing component metadata for import of `new::log`
\ No newline at end of file
+failed to register indirect shims for main module: missing component metadata for import of `new::log`
\ No newline at end of file
diff --git a/crates/wit-component/tests/merge.rs b/crates/wit-component/tests/merge.rs
index f3b3918b..f7f72f2f 100644
--- a/crates/wit-component/tests/merge.rs
+++ b/crates/wit-component/tests/merge.rs
@@ -52,7 +52,7 @@ fn merging() -> Result<()> {
             }
             Err(e) => {
                 assert!(test_case.starts_with("bad-"), "failed to merge with {e:?}");
-                assert_output(&path.join("error.txt"), &format!("{e:?}"))?;
+                assert_output(&path.join("error.txt"), &format!("{e:#}"))?;
             }
         }
     }
diff --git a/crates/wit-component/tests/merge/bad-interface1/error.txt b/crates/wit-component/tests/merge/bad-interface1/error.txt
index 951bb365..0558df13 100644
--- a/crates/wit-component/tests/merge/bad-interface1/error.txt
+++ b/crates/wit-component/tests/merge/bad-interface1/error.txt
@@ -1,5 +1,1 @@
-failed to merge package `foo:foo` into existing copy
-
-Caused by:
-    0: failed to merge interface `a`
-    1: expected type `a` to be present
\ No newline at end of file
+failed to merge package `foo:foo` into existing copy: failed to merge interface `a`: expected type `a` to be present
\ No newline at end of file
diff --git a/crates/wit-component/tests/merge/bad-interface2/error.txt b/crates/wit-component/tests/merge/bad-interface2/error.txt
index bbb5f232..0b3f674b 100644
--- a/crates/wit-component/tests/merge/bad-interface2/error.txt
+++ b/crates/wit-component/tests/merge/bad-interface2/error.txt
@@ -1,5 +1,1 @@
-failed to merge package `foo:foo` into existing copy
-
-Caused by:
-    0: failed to merge interface `a`
-    1: expected function `a` to be present
\ No newline at end of file
+failed to merge package `foo:foo` into existing copy: failed to merge interface `a`: expected function `a` to be present
\ No newline at end of file
diff --git a/crates/wit-component/tests/merge/bad-world1/error.txt b/crates/wit-component/tests/merge/bad-world1/error.txt
index f04e2ed0..059b1e3d 100644
--- a/crates/wit-component/tests/merge/bad-world1/error.txt
+++ b/crates/wit-component/tests/merge/bad-world1/error.txt
@@ -1,5 +1,1 @@
-failed to merge package `foo:foo` into existing copy
-
-Caused by:
-    0: failed to merge world `foo`
-    1: import `b` not found in target world
\ No newline at end of file
+failed to merge package `foo:foo` into existing copy: failed to merge world `foo`: import `b` not found in target world
\ No newline at end of file
diff --git a/crates/wit-component/tests/merge/bad-world2/error.txt b/crates/wit-component/tests/merge/bad-world2/error.txt
index bca74e7b..5f636c13 100644
--- a/crates/wit-component/tests/merge/bad-world2/error.txt
+++ b/crates/wit-component/tests/merge/bad-world2/error.txt
@@ -1,5 +1,1 @@
-failed to merge package `foo:foo` into existing copy
-
-Caused by:
-    0: failed to merge world `foo`
-    1: world contains different number of imports than expected
\ No newline at end of file
+failed to merge package `foo:foo` into existing copy: failed to merge world `foo`: world contains different number of imports than expected
\ No newline at end of file
diff --git a/crates/wit-component/tests/merge/bad-world3/error.txt b/crates/wit-component/tests/merge/bad-world3/error.txt
index daf6860f..1311233f 100644
--- a/crates/wit-component/tests/merge/bad-world3/error.txt
+++ b/crates/wit-component/tests/merge/bad-world3/error.txt
@@ -1,5 +1,1 @@
-failed to merge package `foo:foo` into existing copy
-
-Caused by:
-    0: failed to merge world `foo`
-    1: world contains different number of exports than expected
\ No newline at end of file
+failed to merge package `foo:foo` into existing copy: failed to merge world `foo`: world contains different number of exports than expected
\ No newline at end of file
diff --git a/crates/wit-component/tests/merge/bad-world4/error.txt b/crates/wit-component/tests/merge/bad-world4/error.txt
index 5cf21c92..9b950703 100644
--- a/crates/wit-component/tests/merge/bad-world4/error.txt
+++ b/crates/wit-component/tests/merge/bad-world4/error.txt
@@ -1,5 +1,1 @@
-failed to merge package `foo:foo` into existing copy
-
-Caused by:
-    0: failed to merge world `foo`
-    1: export `b` not found in target world
\ No newline at end of file
+failed to merge package `foo:foo` into existing copy: failed to merge world `foo`: export `b` not found in target world
\ No newline at end of file
diff --git a/crates/wit-component/tests/merge/bad-world5/error.txt b/crates/wit-component/tests/merge/bad-world5/error.txt
index 9eb40e75..38c83bb9 100644
--- a/crates/wit-component/tests/merge/bad-world5/error.txt
+++ b/crates/wit-component/tests/merge/bad-world5/error.txt
@@ -1,5 +1,1 @@
-failed to merge package `foo:foo` into existing copy
-
-Caused by:
-    0: failed to merge world `foo`
-    1: import `a` not found in target world
\ No newline at end of file
+failed to merge package `foo:foo` into existing copy: failed to merge world `foo`: import `a` not found in target world
\ No newline at end of file
diff --git a/crates/wit-component/tests/targets.rs b/crates/wit-component/tests/targets.rs
index 7bbd96ff..ff257285 100644
--- a/crates/wit-component/tests/targets.rs
+++ b/crates/wit-component/tests/targets.rs
@@ -50,7 +50,7 @@ fn targets() -> Result<()> {
             }
             Err(e) => {
                 assert!(test_case.starts_with("error-"), "{e:?}");
-                assert_output(&path.join("error.txt"), &format!("{e:?}"))?;
+                assert_output(&path.join("error.txt"), &format!("{e:#}"))?;
             }
         }
     }
diff --git a/crates/wit-component/tests/targets/error-missing-export/error.txt b/crates/wit-component/tests/targets/error-missing-export/error.txt
index ebfccba1..5751a106 100644
--- a/crates/wit-component/tests/targets/error-missing-export/error.txt
+++ b/crates/wit-component/tests/targets/error-missing-export/error.txt
@@ -1,5 +1,2 @@
-failed to validate encoded bytes
-
-Caused by:
-    type mismatch for import `foobar`
-    missing export named `test:foo/bar` (at offset 0x1d5)
\ No newline at end of file
+failed to validate encoded bytes: type mismatch for import `foobar`
+missing export named `test:foo/bar` (at offset 0x1d5)
\ No newline at end of file
diff --git a/crates/wit-component/tests/targets/error-missing-import/error.txt b/crates/wit-component/tests/targets/error-missing-import/error.txt
index 3a55fb02..d5d32ff9 100644
--- a/crates/wit-component/tests/targets/error-missing-import/error.txt
+++ b/crates/wit-component/tests/targets/error-missing-import/error.txt
@@ -1,5 +1,2 @@
-failed to validate encoded bytes
-
-Caused by:
-    type mismatch for import `foobar`
-    missing import named `test:foo/foo` (at offset 0x175)
\ No newline at end of file
+failed to validate encoded bytes: type mismatch for import `foobar`
+missing import named `test:foo/foo` (at offset 0x175)
\ No newline at end of file
diff --git a/crates/wit-parser/tests/all.rs b/crates/wit-parser/tests/all.rs
index 6c7af1f7..ec9d4be9 100644
--- a/crates/wit-parser/tests/all.rs
+++ b/crates/wit-parser/tests/all.rs
@@ -87,7 +87,7 @@ impl Runner {
                             "some generic platform-agnostic error message",
                         );
                     }
-                    format!("{:?}", e)
+                    format!("{:#}", e)
                 }
             }
         } else {
diff --git a/crates/wit-parser/tests/ui/parse-fail/bad-gate3.wit.result b/crates/wit-parser/tests/ui/parse-fail/bad-gate3.wit.result
index e8e4df1d..181a55f6 100644
--- a/crates/wit-parser/tests/ui/parse-fail/bad-gate3.wit.result
+++ b/crates/wit-parser/tests/ui/parse-fail/bad-gate3.wit.result
@@ -1,8 +1,5 @@
-this type is not gated by a feature but its interface is gated by a feature
-
-Caused by:
-    found a reference to a interface which is excluded due to its feature not being activated
-         --> tests/ui/parse-fail/bad-gate3.wit:5:8
-          |
-        5 |   type a = u32;
-          |        ^
\ No newline at end of file
+this type is not gated by a feature but its interface is gated by a feature: found a reference to a interface which is excluded due to its feature not being activated
+     --> tests/ui/parse-fail/bad-gate3.wit:5:8
+      |
+    5 |   type a = u32;
+      |        ^
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/parse-fail/bad-gate4.wit.result b/crates/wit-parser/tests/ui/parse-fail/bad-gate4.wit.result
index 8b47145a..956ba063 100644
--- a/crates/wit-parser/tests/ui/parse-fail/bad-gate4.wit.result
+++ b/crates/wit-parser/tests/ui/parse-fail/bad-gate4.wit.result
@@ -1,8 +1,5 @@
-failed to update function `[constructor]a`
-
-Caused by:
-    found a reference to a type which is excluded due to its feature not being activated
-         --> tests/ui/parse-fail/bad-gate4.wit:6:5
-          |
-        6 |     constructor();
-          |     ^----------
\ No newline at end of file
+failed to update function `[constructor]a`: found a reference to a type which is excluded due to its feature not being activated
+     --> tests/ui/parse-fail/bad-gate4.wit:6:5
+      |
+    6 |     constructor();
+      |     ^----------
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/parse-fail/bad-gate5.wit.result b/crates/wit-parser/tests/ui/parse-fail/bad-gate5.wit.result
index df063dd3..6c36fb3f 100644
--- a/crates/wit-parser/tests/ui/parse-fail/bad-gate5.wit.result
+++ b/crates/wit-parser/tests/ui/parse-fail/bad-gate5.wit.result
@@ -1,8 +1,5 @@
-failed to update function `[static]a.x`
-
-Caused by:
-    found a reference to a type which is excluded due to its feature not being activated
-         --> tests/ui/parse-fail/bad-gate5.wit:9:5
-          |
-        9 |     x: static func();
-          |     ^
\ No newline at end of file
+failed to update function `[static]a.x`: found a reference to a type which is excluded due to its feature not being activated
+     --> tests/ui/parse-fail/bad-gate5.wit:9:5
+      |
+    9 |     x: static func();
+      |     ^
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/parse-fail/bad-pkg1.wit.result b/crates/wit-parser/tests/ui/parse-fail/bad-pkg1.wit.result
index e366b729..ddc7c7c3 100644
--- a/crates/wit-parser/tests/ui/parse-fail/bad-pkg1.wit.result
+++ b/crates/wit-parser/tests/ui/parse-fail/bad-pkg1.wit.result
@@ -1,9 +1,5 @@
-failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/bad-pkg1]
-
-Caused by:
-    0: failed to parse package: tests/ui/parse-fail/bad-pkg1
-    1: interface or world `nonexistent` not found in package
-            --> tests/ui/parse-fail/bad-pkg1/root.wit:4:7
-             |
-           4 |   use nonexistent.{};
-             |       ^----------
\ No newline at end of file
+failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/bad-pkg1]: failed to parse package: tests/ui/parse-fail/bad-pkg1: interface or world `nonexistent` not found in package
+     --> tests/ui/parse-fail/bad-pkg1/root.wit:4:7
+      |
+    4 |   use nonexistent.{};
+      |       ^----------
\ No newline at end of file
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 3827d5db..235a806d 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,8 +1,5 @@
-failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/bad-pkg2]
-
-Caused by:
-    interface not found in package
-         --> tests/ui/parse-fail/bad-pkg2/root.wit:4:15
-          |
-        4 |   use foo:bar/nonexistent.{};
-          |               ^----------
\ No newline at end of file
+failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/bad-pkg2]: interface not found in package
+     --> tests/ui/parse-fail/bad-pkg2/root.wit:4:15
+      |
+    4 |   use foo:bar/nonexistent.{};
+      |               ^----------
\ No newline at end of file
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 f5d61a28..d3d2102d 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,8 +1,5 @@
-failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/bad-pkg3]
-
-Caused by:
-    interface not found in package
-         --> tests/ui/parse-fail/bad-pkg3/root.wit:4:15
-          |
-        4 |   use foo:bar/baz.{};
-          |               ^--
\ No newline at end of file
+failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/bad-pkg3]: interface not found in package
+     --> tests/ui/parse-fail/bad-pkg3/root.wit:4:15
+      |
+    4 |   use foo:bar/baz.{};
+      |               ^--
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/parse-fail/bad-pkg4.wit.result b/crates/wit-parser/tests/ui/parse-fail/bad-pkg4.wit.result
index dac51efa..e4522856 100644
--- a/crates/wit-parser/tests/ui/parse-fail/bad-pkg4.wit.result
+++ b/crates/wit-parser/tests/ui/parse-fail/bad-pkg4.wit.result
@@ -1,8 +1,5 @@
-failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/bad-pkg4]
-
-Caused by:
-    type `a-name` not defined in interface
-         --> tests/ui/parse-fail/bad-pkg4/root.wit:3:20
-          |
-        3 |   use foo:bar/baz.{a-name};
-          |                    ^-----
\ No newline at end of file
+failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/bad-pkg4]: type `a-name` not defined in interface
+     --> tests/ui/parse-fail/bad-pkg4/root.wit:3:20
+      |
+    3 |   use foo:bar/baz.{a-name};
+      |                    ^-----
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/parse-fail/bad-pkg5.wit.result b/crates/wit-parser/tests/ui/parse-fail/bad-pkg5.wit.result
index 2d37060f..0251000c 100644
--- a/crates/wit-parser/tests/ui/parse-fail/bad-pkg5.wit.result
+++ b/crates/wit-parser/tests/ui/parse-fail/bad-pkg5.wit.result
@@ -1,8 +1,5 @@
-failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/bad-pkg5]
-
-Caused by:
-    type `nonexistent` not defined in interface
-         --> tests/ui/parse-fail/bad-pkg5/root.wit:3:20
-          |
-        3 |   use foo:bar/baz.{nonexistent};
-          |                    ^----------
\ No newline at end of file
+failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/bad-pkg5]: type `nonexistent` not defined in interface
+     --> tests/ui/parse-fail/bad-pkg5/root.wit:3:20
+      |
+    3 |   use foo:bar/baz.{nonexistent};
+      |                    ^----------
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/parse-fail/bad-pkg6.wit.result b/crates/wit-parser/tests/ui/parse-fail/bad-pkg6.wit.result
index c0fdc9d9..56b5c85e 100644
--- a/crates/wit-parser/tests/ui/parse-fail/bad-pkg6.wit.result
+++ b/crates/wit-parser/tests/ui/parse-fail/bad-pkg6.wit.result
@@ -1,8 +1,5 @@
-failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/bad-pkg6]
-
-Caused by:
-    package not found
-         --> tests/ui/parse-fail/bad-pkg6/root.wit:3:7
-          |
-        3 |   use foo:bar/baz.{};
-          |       ^------
\ No newline at end of file
+failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/bad-pkg6]: package not found
+     --> tests/ui/parse-fail/bad-pkg6/root.wit:3:7
+      |
+    3 |   use foo:bar/baz.{};
+      |       ^------
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/parse-fail/bad-resource15.wit.result b/crates/wit-parser/tests/ui/parse-fail/bad-resource15.wit.result
index c26467f8..1ed0a4f5 100644
--- a/crates/wit-parser/tests/ui/parse-fail/bad-resource15.wit.result
+++ b/crates/wit-parser/tests/ui/parse-fail/bad-resource15.wit.result
@@ -1,8 +1,5 @@
-failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/bad-resource15]
-
-Caused by:
-    type used in a handle must be a resource
-         --> tests/ui/parse-fail/bad-resource15/foo.wit:6:16
-          |
-        6 |   type t = own<r>;
-          |                ^
\ No newline at end of file
+failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/bad-resource15]: type used in a handle must be a resource
+     --> tests/ui/parse-fail/bad-resource15/foo.wit:6:16
+      |
+    6 |   type t = own<r>;
+      |                ^
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/parse-fail/conflicting-package.wit.result b/crates/wit-parser/tests/ui/parse-fail/conflicting-package.wit.result
index bffa1fc4..dc9dcee4 100644
--- a/crates/wit-parser/tests/ui/parse-fail/conflicting-package.wit.result
+++ b/crates/wit-parser/tests/ui/parse-fail/conflicting-package.wit.result
@@ -1,10 +1,5 @@
-failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/conflicting-package]
-
-Caused by:
-    0: failed to parse package: tests/ui/parse-fail/conflicting-package
-    1: failed to start resolving path: tests/ui/parse-fail/conflicting-package/b.wit
-    2: package identifier `foo:b` does not match previous package name of `foo:a`
-            --> tests/ui/parse-fail/conflicting-package/b.wit:1:9
-             |
-           1 | package foo:b;
-             |         ^----
\ No newline at end of file
+failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/conflicting-package]: failed to parse package: tests/ui/parse-fail/conflicting-package: failed to start resolving path: tests/ui/parse-fail/conflicting-package/b.wit: package identifier `foo:b` does not match previous package name of `foo:a`
+     --> tests/ui/parse-fail/conflicting-package/b.wit:1:9
+      |
+    1 | package foo:b;
+      |         ^----
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/parse-fail/duplicate-interface2.wit.result b/crates/wit-parser/tests/ui/parse-fail/duplicate-interface2.wit.result
index 81735c76..8eb038bf 100644
--- a/crates/wit-parser/tests/ui/parse-fail/duplicate-interface2.wit.result
+++ b/crates/wit-parser/tests/ui/parse-fail/duplicate-interface2.wit.result
@@ -1,9 +1,5 @@
-failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/duplicate-interface2]
-
-Caused by:
-    0: failed to parse package: tests/ui/parse-fail/duplicate-interface2
-    1: duplicate item named `foo`
-            --> tests/ui/parse-fail/duplicate-interface2/foo2.wit:3:11
-             |
-           3 | interface foo {}
-             |           ^--
\ No newline at end of file
+failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/duplicate-interface2]: failed to parse package: tests/ui/parse-fail/duplicate-interface2: duplicate item named `foo`
+     --> tests/ui/parse-fail/duplicate-interface2/foo2.wit:3:11
+      |
+    3 | interface foo {}
+      |           ^--
\ No newline at end of file
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 00257283..495d3310 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,8 +1,5 @@
-failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/include-foreign]
-
-Caused by:
-    world not found in package
-         --> tests/ui/parse-fail/include-foreign/root.wit:4:19
-          |
-        4 |   include foo:bar/bar;
-          |                   ^--
\ No newline at end of file
+failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/include-foreign]: world not found in package
+     --> tests/ui/parse-fail/include-foreign/root.wit:4:19
+      |
+    4 |   include foo:bar/bar;
+      |                   ^--
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/parse-fail/multi-file-missing-delimiter.wit.result b/crates/wit-parser/tests/ui/parse-fail/multi-file-missing-delimiter.wit.result
index 45400e63..8182a1a4 100644
--- a/crates/wit-parser/tests/ui/parse-fail/multi-file-missing-delimiter.wit.result
+++ b/crates/wit-parser/tests/ui/parse-fail/multi-file-missing-delimiter.wit.result
@@ -1,9 +1,5 @@
-failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/multi-file-missing-delimiter]
-
-Caused by:
-    0: failed to parse package: tests/ui/parse-fail/multi-file-missing-delimiter
-    1: expected `type`, `resource` or `func`, found eof
-            --> tests/ui/parse-fail/multi-file-missing-delimiter/observe.wit:6:1
-             |
-           6 | 
-             | ^
\ No newline at end of file
+failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/multi-file-missing-delimiter]: failed to parse package: tests/ui/parse-fail/multi-file-missing-delimiter: expected `type`, `resource` or `func`, found eof
+     --> tests/ui/parse-fail/multi-file-missing-delimiter/observe.wit:6:1
+      |
+    6 | 
+      | ^
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/parse-fail/multi-package-deps-share-nest.wit.result b/crates/wit-parser/tests/ui/parse-fail/multi-package-deps-share-nest.wit.result
index 31c994e7..053a98b1 100644
--- a/crates/wit-parser/tests/ui/parse-fail/multi-package-deps-share-nest.wit.result
+++ b/crates/wit-parser/tests/ui/parse-fail/multi-package-deps-share-nest.wit.result
@@ -1,6 +1,3 @@
-failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/multi-package-deps-share-nest]
-
-Caused by:
-    package foo:shared is defined in two different locations:
-    * tests/ui/parse-fail/multi-package-deps-share-nest/deps/dep2/types.wit:3:9
-    * tests/ui/parse-fail/multi-package-deps-share-nest/deps/dep1/types.wit:3:9
\ No newline at end of file
+failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/multi-package-deps-share-nest]: package foo:shared is defined in two different locations:
+* tests/ui/parse-fail/multi-package-deps-share-nest/deps/dep2/types.wit:3:9
+* tests/ui/parse-fail/multi-package-deps-share-nest/deps/dep1/types.wit:3:9
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/parse-fail/multiple-package-docs.wit.result b/crates/wit-parser/tests/ui/parse-fail/multiple-package-docs.wit.result
index 4ce44238..fd70371f 100644
--- a/crates/wit-parser/tests/ui/parse-fail/multiple-package-docs.wit.result
+++ b/crates/wit-parser/tests/ui/parse-fail/multiple-package-docs.wit.result
@@ -1,10 +1,5 @@
-failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/multiple-package-docs]
-
-Caused by:
-    0: failed to parse package: tests/ui/parse-fail/multiple-package-docs
-    1: failed to start resolving path: tests/ui/parse-fail/multiple-package-docs/b.wit
-    2: found doc comments on multiple 'package' items
-            --> tests/ui/parse-fail/multiple-package-docs/b.wit:1:1
-             |
-           1 | /// Multiple package docs, B
-             | ^---------------------------
\ No newline at end of file
+failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/multiple-package-docs]: failed to parse package: tests/ui/parse-fail/multiple-package-docs: failed to start resolving path: tests/ui/parse-fail/multiple-package-docs/b.wit: found doc comments on multiple 'package' items
+     --> tests/ui/parse-fail/multiple-package-docs/b.wit:1:1
+      |
+    1 | /// Multiple package docs, B
+      | ^---------------------------
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/parse-fail/no-access-to-sibling-use.wit.result b/crates/wit-parser/tests/ui/parse-fail/no-access-to-sibling-use.wit.result
index 72830f2b..91c39804 100644
--- a/crates/wit-parser/tests/ui/parse-fail/no-access-to-sibling-use.wit.result
+++ b/crates/wit-parser/tests/ui/parse-fail/no-access-to-sibling-use.wit.result
@@ -1,9 +1,5 @@
-failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/no-access-to-sibling-use]
-
-Caused by:
-    0: failed to parse package: tests/ui/parse-fail/no-access-to-sibling-use
-    1: interface or world `bar-renamed` does not exist
-            --> tests/ui/parse-fail/no-access-to-sibling-use/foo.wit:3:5
-             |
-           3 | use bar-renamed;
-             |     ^----------
\ No newline at end of file
+failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/no-access-to-sibling-use]: failed to parse package: tests/ui/parse-fail/no-access-to-sibling-use: interface or world `bar-renamed` does not exist
+     --> tests/ui/parse-fail/no-access-to-sibling-use/foo.wit:3:5
+      |
+    3 | use bar-renamed;
+      |     ^----------
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/parse-fail/non-existance-world-include.wit.result b/crates/wit-parser/tests/ui/parse-fail/non-existance-world-include.wit.result
index 13a11227..f7c853c8 100644
--- a/crates/wit-parser/tests/ui/parse-fail/non-existance-world-include.wit.result
+++ b/crates/wit-parser/tests/ui/parse-fail/non-existance-world-include.wit.result
@@ -1,8 +1,5 @@
-failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/non-existance-world-include]
-
-Caused by:
-    world not found in package
-         --> tests/ui/parse-fail/non-existance-world-include/root.wit:4:19
-          |
-        4 |   include foo:baz/non-existance;
-          |                   ^------------
\ No newline at end of file
+failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/non-existance-world-include]: world not found in package
+     --> tests/ui/parse-fail/non-existance-world-include/root.wit:4:19
+      |
+    4 |   include foo:baz/non-existance;
+      |                   ^------------
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/parse-fail/pkg-cycle.wit.result b/crates/wit-parser/tests/ui/parse-fail/pkg-cycle.wit.result
index f6497093..250e1581 100644
--- a/crates/wit-parser/tests/ui/parse-fail/pkg-cycle.wit.result
+++ b/crates/wit-parser/tests/ui/parse-fail/pkg-cycle.wit.result
@@ -1,8 +1,5 @@
-failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/pkg-cycle]
-
-Caused by:
-    package depends on itself
-         --> tests/ui/parse-fail/pkg-cycle/deps/a1/root.wit:3:7
-          |
-        3 |   use foo:a1/foo.{};
-          |       ^-----
\ No newline at end of file
+failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/pkg-cycle]: package depends on itself
+     --> tests/ui/parse-fail/pkg-cycle/deps/a1/root.wit:3:7
+      |
+    3 |   use foo:a1/foo.{};
+      |       ^-----
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/parse-fail/pkg-cycle2.wit.result b/crates/wit-parser/tests/ui/parse-fail/pkg-cycle2.wit.result
index 53054871..ec10e18c 100644
--- a/crates/wit-parser/tests/ui/parse-fail/pkg-cycle2.wit.result
+++ b/crates/wit-parser/tests/ui/parse-fail/pkg-cycle2.wit.result
@@ -1,8 +1,5 @@
-failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/pkg-cycle2]
-
-Caused by:
-    package depends on itself
-         --> tests/ui/parse-fail/pkg-cycle2/deps/a1/root.wit:3:7
-          |
-        3 |   use foo:a2/foo.{};
-          |       ^-----
\ No newline at end of file
+failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/pkg-cycle2]: package depends on itself
+     --> tests/ui/parse-fail/pkg-cycle2/deps/a1/root.wit:3:7
+      |
+    3 |   use foo:a2/foo.{};
+      |       ^-----
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/parse-fail/resources-multiple-returns-borrow.wit.result b/crates/wit-parser/tests/ui/parse-fail/resources-multiple-returns-borrow.wit.result
index 2094237f..ba5d4f7f 100644
--- a/crates/wit-parser/tests/ui/parse-fail/resources-multiple-returns-borrow.wit.result
+++ b/crates/wit-parser/tests/ui/parse-fail/resources-multiple-returns-borrow.wit.result
@@ -1,8 +1,5 @@
-failed to update function `[method]r1.f1`
-
-Caused by:
-    function returns a type which contains a `borrow<T>` which is not supported
-         --> tests/ui/parse-fail/resources-multiple-returns-borrow.wit:7:5
-          |
-        7 |     f1: func() -> (a: s32, handle: borrow<r1>);
-          |     ^-
\ No newline at end of file
+failed to update function `[method]r1.f1`: function returns a type which contains a `borrow<T>` which is not supported
+     --> tests/ui/parse-fail/resources-multiple-returns-borrow.wit:7:5
+      |
+    7 |     f1: func() -> (a: s32, handle: borrow<r1>);
+      |     ^-
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/parse-fail/resources-return-borrow.wit.result b/crates/wit-parser/tests/ui/parse-fail/resources-return-borrow.wit.result
index 79de3292..b6a7361b 100644
--- a/crates/wit-parser/tests/ui/parse-fail/resources-return-borrow.wit.result
+++ b/crates/wit-parser/tests/ui/parse-fail/resources-return-borrow.wit.result
@@ -1,8 +1,5 @@
-failed to update function `[method]r1.f1`
-
-Caused by:
-    function returns a type which contains a `borrow<T>` which is not supported
-         --> tests/ui/parse-fail/resources-return-borrow.wit:7:5
-          |
-        7 |     f1: func() -> borrow<r1>;
-          |     ^-
\ No newline at end of file
+failed to update function `[method]r1.f1`: function returns a type which contains a `borrow<T>` which is not supported
+     --> tests/ui/parse-fail/resources-return-borrow.wit:7:5
+      |
+    7 |     f1: func() -> borrow<r1>;
+      |     ^-
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/parse-fail/return-borrow1.wit.result b/crates/wit-parser/tests/ui/parse-fail/return-borrow1.wit.result
index 0a0468dd..a4ddb59f 100644
--- a/crates/wit-parser/tests/ui/parse-fail/return-borrow1.wit.result
+++ b/crates/wit-parser/tests/ui/parse-fail/return-borrow1.wit.result
@@ -1,8 +1,5 @@
-failed to update function `x`
-
-Caused by:
-    function returns a type which contains a `borrow<T>` which is not supported
-         --> tests/ui/parse-fail/return-borrow1.wit:6:3
-          |
-        6 |   x: func() -> borrow<y>;
-          |   ^
\ No newline at end of file
+failed to update function `x`: function returns a type which contains a `borrow<T>` which is not supported
+     --> tests/ui/parse-fail/return-borrow1.wit:6:3
+      |
+    6 |   x: func() -> borrow<y>;
+      |   ^
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/parse-fail/return-borrow2.wit.result b/crates/wit-parser/tests/ui/parse-fail/return-borrow2.wit.result
index b46631bb..01c7f6da 100644
--- a/crates/wit-parser/tests/ui/parse-fail/return-borrow2.wit.result
+++ b/crates/wit-parser/tests/ui/parse-fail/return-borrow2.wit.result
@@ -1,8 +1,5 @@
-failed to update function `[method]y.x`
-
-Caused by:
-    function returns a type which contains a `borrow<T>` which is not supported
-         --> tests/ui/parse-fail/return-borrow2.wit:5:5
-          |
-        5 |     x: func() -> borrow<y>;
-          |     ^
\ No newline at end of file
+failed to update function `[method]y.x`: function returns a type which contains a `borrow<T>` which is not supported
+     --> tests/ui/parse-fail/return-borrow2.wit:5:5
+      |
+    5 |     x: func() -> borrow<y>;
+      |     ^
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/parse-fail/return-borrow6.wit.result b/crates/wit-parser/tests/ui/parse-fail/return-borrow6.wit.result
index 13d61fba..e1264b57 100644
--- a/crates/wit-parser/tests/ui/parse-fail/return-borrow6.wit.result
+++ b/crates/wit-parser/tests/ui/parse-fail/return-borrow6.wit.result
@@ -1,8 +1,5 @@
-failed to update function `x`
-
-Caused by:
-    function returns a type which contains a `borrow<T>` which is not supported
-         --> tests/ui/parse-fail/return-borrow6.wit:6:3
-          |
-        6 |   x: func() -> tuple<borrow<y>>;
-          |   ^
\ No newline at end of file
+failed to update function `x`: function returns a type which contains a `borrow<T>` which is not supported
+     --> tests/ui/parse-fail/return-borrow6.wit:6:3
+      |
+    6 |   x: func() -> tuple<borrow<y>>;
+      |   ^
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/parse-fail/return-borrow7.wit.result b/crates/wit-parser/tests/ui/parse-fail/return-borrow7.wit.result
index 3caaf3fe..f2175ae6 100644
--- a/crates/wit-parser/tests/ui/parse-fail/return-borrow7.wit.result
+++ b/crates/wit-parser/tests/ui/parse-fail/return-borrow7.wit.result
@@ -1,8 +1,5 @@
-failed to update function `x`
-
-Caused by:
-    function returns a type which contains a `borrow<T>` which is not supported
-         --> tests/ui/parse-fail/return-borrow7.wit:10:3
-          |
-       10 |   x: func() -> y2;
-          |   ^
\ No newline at end of file
+failed to update function `x`: function returns a type which contains a `borrow<T>` which is not supported
+     --> tests/ui/parse-fail/return-borrow7.wit:10:3
+      |
+   10 |   x: func() -> y2;
+      |   ^
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/parse-fail/return-borrow8.wit.result b/crates/wit-parser/tests/ui/parse-fail/return-borrow8.wit.result
index 9fd1f027..3ace7b8b 100644
--- a/crates/wit-parser/tests/ui/parse-fail/return-borrow8.wit.result
+++ b/crates/wit-parser/tests/ui/parse-fail/return-borrow8.wit.result
@@ -1,9 +1,5 @@
-failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/return-borrow8]
-
-Caused by:
-    0: failed to update function `x`
-    1: function returns a type which contains a `borrow<T>` which is not supported
-            --> tests/ui/parse-fail/return-borrow8/foo.wit:6:3
-             |
-           6 |   x: func() -> r;
-             |   ^
\ No newline at end of file
+failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/return-borrow8]: failed to update function `x`: function returns a type which contains a `borrow<T>` which is not supported
+     --> tests/ui/parse-fail/return-borrow8/foo.wit:6:3
+      |
+    6 |   x: func() -> r;
+      |   ^
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/parse-fail/type-and-resource-same-name.wit.result b/crates/wit-parser/tests/ui/parse-fail/type-and-resource-same-name.wit.result
index d86ebfb3..782921c6 100644
--- a/crates/wit-parser/tests/ui/parse-fail/type-and-resource-same-name.wit.result
+++ b/crates/wit-parser/tests/ui/parse-fail/type-and-resource-same-name.wit.result
@@ -1,8 +1,5 @@
-failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/type-and-resource-same-name]
-
-Caused by:
-    type used in a handle must be a resource
-         --> tests/ui/parse-fail/type-and-resource-same-name/foo.wit:7:20
-          |
-        7 |   type t2 = borrow<a>;
-          |                    ^
\ No newline at end of file
+failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/type-and-resource-same-name]: type used in a handle must be a resource
+     --> tests/ui/parse-fail/type-and-resource-same-name/foo.wit:7:20
+      |
+    7 |   type t2 = borrow<a>;
+      |                    ^
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/parse-fail/unresolved-use10.wit.result b/crates/wit-parser/tests/ui/parse-fail/unresolved-use10.wit.result
index 75343de6..8d36b21d 100644
--- a/crates/wit-parser/tests/ui/parse-fail/unresolved-use10.wit.result
+++ b/crates/wit-parser/tests/ui/parse-fail/unresolved-use10.wit.result
@@ -1,9 +1,5 @@
-failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/unresolved-use10]
-
-Caused by:
-    0: failed to parse package: tests/ui/parse-fail/unresolved-use10
-    1: name `thing` is not defined
-            --> tests/ui/parse-fail/unresolved-use10/bar.wit:4:12
-             |
-           4 |   use foo.{thing};
-             |            ^----
\ No newline at end of file
+failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/unresolved-use10]: failed to parse package: tests/ui/parse-fail/unresolved-use10: name `thing` is not defined
+     --> tests/ui/parse-fail/unresolved-use10/bar.wit:4:12
+      |
+    4 |   use foo.{thing};
+      |            ^----
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/parse-fail/use-and-include-world.wit.result b/crates/wit-parser/tests/ui/parse-fail/use-and-include-world.wit.result
index 0d4fcd91..e61a285a 100644
--- a/crates/wit-parser/tests/ui/parse-fail/use-and-include-world.wit.result
+++ b/crates/wit-parser/tests/ui/parse-fail/use-and-include-world.wit.result
@@ -1,9 +1,5 @@
-failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/use-and-include-world]
-
-Caused by:
-    0: failed to parse package: tests/ui/parse-fail/use-and-include-world
-    1: name `bar` is defined as an interface, not a world
-            --> tests/ui/parse-fail/use-and-include-world/root.wit:6:21
-             |
-           6 |     include foo:baz/bar;
-             |                     ^--
\ No newline at end of file
+failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/use-and-include-world]: failed to parse package: tests/ui/parse-fail/use-and-include-world: name `bar` is defined as an interface, not a world
+     --> tests/ui/parse-fail/use-and-include-world/root.wit:6:21
+      |
+    6 |     include foo:baz/bar;
+      |                     ^--
\ No newline at end of file
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 df56488b..a8597257 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,8 +1,5 @@
-failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/use-world]
-
-Caused by:
-    interface not found in package
-         --> tests/ui/parse-fail/use-world/root.wit:3:13
-          |
-        3 | use foo:baz/bar;
-          |             ^--
\ No newline at end of file
+failed to resolve directory while parsing WIT for path [tests/ui/parse-fail/use-world]: interface not found in package
+     --> tests/ui/parse-fail/use-world/root.wit:3:13
+      |
+    3 | use foo:baz/bar;
+      |             ^--
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/parse-fail/very-nested-packages.wit.result b/crates/wit-parser/tests/ui/parse-fail/very-nested-packages.wit.result
index eaf0e670..3109de95 100644
--- a/crates/wit-parser/tests/ui/parse-fail/very-nested-packages.wit.result
+++ b/crates/wit-parser/tests/ui/parse-fail/very-nested-packages.wit.result
@@ -1,8 +1,5 @@
-failed to handle nested package in: tests/ui/parse-fail/very-nested-packages.wit
-
-Caused by:
-    nested packages must be placed at the top-level
-         --> tests/ui/parse-fail/very-nested-packages.wit:4:11
-          |
-        4 |   package a:c2 {
-          |           ^---
\ No newline at end of file
+failed to handle nested package in: tests/ui/parse-fail/very-nested-packages.wit: nested packages must be placed at the top-level
+     --> tests/ui/parse-fail/very-nested-packages.wit:4:11
+      |
+    4 |   package a:c2 {
+      |           ^---
\ No newline at end of file
diff --git a/tests/cli/validate-unknown-features.wat.stderr b/tests/cli/validate-unknown-features.wat.stderr
index 3e0ccb84..95e79d76 100644
--- a/tests/cli/validate-unknown-features.wat.stderr
+++ b/tests/cli/validate-unknown-features.wat.stderr
@@ -1,4 +1,4 @@
 error: invalid value 'unknown' for '--features <FEATURES>': unknown feature `unknown`
-Valid features: mutable-global, saturating-float-to-int, sign-extension, reference-types, multi-value, bulk-memory, simd, relaxed-simd, threads, shared-everything-threads, tail-call, floats, multi-memory, exceptions, memory64, extended-const, component-model, function-references, memory-control, gc, custom-page-sizes, component-model-values, component-model-nested-names, component-model-more-flags, component-model-multiple-returns, legacy-exceptions, gc-types
+Valid features: mutable-global, saturating-float-to-int, sign-extension, reference-types, multi-value, bulk-memory, simd, relaxed-simd, threads, shared-everything-threads, tail-call, floats, multi-memory, exceptions, memory64, extended-const, component-model, function-references, memory-control, gc, custom-page-sizes, component-model-values, component-model-nested-names, component-model-more-flags, component-model-multiple-returns, legacy-exceptions, gc-types, mvp, wasm1, wasm2, wasm3, all
 
 For more information, try '--help'.
diff --git a/tests/roundtrip.rs b/tests/roundtrip.rs
index a5b15c4c..bd40b897 100644
--- a/tests/roundtrip.rs
+++ b/tests/roundtrip.rs
@@ -598,16 +598,9 @@ impl TestState {
         for part in test.iter().filter_map(|t| t.to_str()) {
             match part {
                 "testsuite" => {
-                    features = WasmFeatures::wasm2();
-                    features |= WasmFeatures::TAIL_CALL;
-                    features |= WasmFeatures::EXTENDED_CONST;
-
-                    // NB: when these proposals are merged upstream in the spec
-                    // repo then this should be removed. Currently this hasn't
-                    // happened so this is required to get tests passing for
-                    // when these proposals are enabled by default.
-                    features.remove(WasmFeatures::MULTI_MEMORY);
-                    features.remove(WasmFeatures::THREADS);
+                    features = WasmFeatures::WASM2
+                        | WasmFeatures::TAIL_CALL
+                        | WasmFeatures::EXTENDED_CONST;
                 }
                 "missing-features" => {
                     features =
@@ -624,14 +617,7 @@ impl TestState {
                 "exception-handling" => features.insert(WasmFeatures::EXCEPTIONS),
                 "legacy-exceptions" => features.insert(WasmFeatures::LEGACY_EXCEPTIONS),
                 "tail-call" => features.insert(WasmFeatures::TAIL_CALL),
-                "memory64" => features.insert(
-                    WasmFeatures::MEMORY64
-                        | WasmFeatures::GC
-                        | WasmFeatures::REFERENCE_TYPES
-                        | WasmFeatures::MULTI_MEMORY
-                        | WasmFeatures::FUNCTION_REFERENCES
-                        | WasmFeatures::EXCEPTIONS,
-                ),
+                "memory64" => features.insert(WasmFeatures::MEMORY64 | WasmFeatures::WASM3),
                 "component-model" => features.insert(WasmFeatures::COMPONENT_MODEL),
                 "shared-everything-threads" => {
                     features.insert(WasmFeatures::COMPONENT_MODEL);