Repository: bytecodealliance/wasm-tools
Pull request #1313 (2023-12-05 22:53:06)

Parse and print `//`-style comments as docs
This updates `wit-parser` to account for WebAssembly/component-model#281. Doc comments are now considered to be all forms of comments and are collected in the `Docs` structure. The structure already stripped all leading `/` characters so it works for either `///` or `//`. Additionally docs are now printed with `//` instead of `///`.


Patch:
diff --git a/crates/wasm-compose/src/encoding.rs b/crates/wasm-compose/src/encoding.rs
index 7a64bb8e..88531a60 100644
--- a/crates/wasm-compose/src/encoding.rs
+++ b/crates/wasm-compose/src/encoding.rs
@@ -5,11 +5,8 @@ use anyhow::{anyhow, bail, Result};
 use indexmap::{IndexMap, IndexSet};
 use petgraph::EdgeDirection;
 use smallvec::SmallVec;
+use std::collections::{hash_map::Entry, HashMap};
 use std::mem;
-use std::{
-    borrow::Cow,
-    collections::{hash_map::Entry, HashMap},
-};
 use wasm_encoder::*;
 use wasmparser::{
     names::KebabString,
diff --git a/crates/wit-component/src/printing.rs b/crates/wit-component/src/printing.rs
index 1ae09295..93cd99c7 100644
--- a/crates/wit-component/src/printing.rs
+++ b/crates/wit-component/src/printing.rs
@@ -843,7 +843,7 @@ impl WitPrinter {
         if self.emit_docs {
             if let Some(contents) = &docs.contents {
                 for line in contents.lines() {
-                    self.output.push_str("/// ");
+                    self.output.push_str("// ");
                     self.output.push_str(line);
                     self.output.push_str("\n");
                 }
diff --git a/crates/wit-parser/src/ast.rs b/crates/wit-parser/src/ast.rs
index 4f386c26..3df0a7c5 100644
--- a/crates/wit-parser/src/ast.rs
+++ b/crates/wit-parser/src/ast.rs
@@ -941,19 +941,17 @@ fn parse_docs<'a>(tokens: &mut Tokenizer<'a>) -> Result<Docs<'a>> {
             Token::Whitespace => {}
             Token::Comment => {
                 let comment = tokens.get_span(span);
-                if comment.starts_with("///") || (comment.starts_with("/**") && comment != "/**/") {
-                    if !started {
-                        docs.span.start = span.start;
-                        started = true;
-                    }
-                    let trailing_ws = comment
-                        .bytes()
-                        .rev()
-                        .take_while(|ch| ch.is_ascii_whitespace())
-                        .count();
-                    docs.span.end = span.end - (trailing_ws as u32);
-                    docs.docs.push(comment.into());
+                if !started {
+                    docs.span.start = span.start;
+                    started = true;
                 }
+                let trailing_ws = comment
+                    .bytes()
+                    .rev()
+                    .take_while(|ch| ch.is_ascii_whitespace())
+                    .count();
+                docs.span.end = span.end - (trailing_ws as u32);
+                docs.docs.push(comment.into());
             }
             _ => break,
         };


Test patch:
diff --git a/crates/wasm-compose/tests/compositions/locked-dep-invalid-semver/error.txt b/crates/wasm-compose/tests/compositions/locked-dep-invalid-semver/error.txt
index 3cfba13a..a11ece76 100644
--- a/crates/wasm-compose/tests/compositions/locked-dep-invalid-semver/error.txt
+++ b/crates/wasm-compose/tests/compositions/locked-dep-invalid-semver/error.txt
@@ -2,4 +2,4 @@ failed to parse component `tests/compositions/locked-dep-invalid-semver/root.wat
 
 Caused by:
     import name `locked-dep=<foo:add@1.0.>` is not a valid extern name
-`1.0.` is not a valid semver: unexpected end of input while parsing patch version number (at offset 0x1b)
\ No newline at end of file
+`1.0.` is not a valid semver: unexpected end of input while parsing patch version number (at offset 0x1b)
diff --git a/crates/wasm-compose/tests/compositions/locked-dep-simple/composed.wat b/crates/wasm-compose/tests/compositions/locked-dep-simple/composed.wat
index 6f738756..74121324 100644
--- a/crates/wasm-compose/tests/compositions/locked-dep-simple/composed.wat
+++ b/crates/wasm-compose/tests/compositions/locked-dep-simple/composed.wat
@@ -34,4 +34,4 @@
       (with "locked-dep=<foo:add@1.0.0>" (instance 1))
     )
   )
-)
\ No newline at end of file
+)
diff --git a/crates/wasm-compose/tests/compositions/unlocked-dep-invalid-semver/error.txt b/crates/wasm-compose/tests/compositions/unlocked-dep-invalid-semver/error.txt
index e43e6938..027adb1a 100644
--- a/crates/wasm-compose/tests/compositions/unlocked-dep-invalid-semver/error.txt
+++ b/crates/wasm-compose/tests/compositions/unlocked-dep-invalid-semver/error.txt
@@ -2,4 +2,4 @@ failed to parse component `tests/compositions/unlocked-dep-invalid-semver/root.w
 
 Caused by:
     import name `unlocked-dep=<foo:add@{>=1.0.}>` is not a valid extern name
-`1.0.` is not a valid semver: unexpected end of input while parsing patch version number (at offset 0x1b)
\ No newline at end of file
+`1.0.` is not a valid semver: unexpected end of input while parsing patch version number (at offset 0x1b)
diff --git a/crates/wasm-compose/tests/compositions/unlocked-dep-simple/composed.wat b/crates/wasm-compose/tests/compositions/unlocked-dep-simple/composed.wat
index 286c359b..c7a0e96e 100644
--- a/crates/wasm-compose/tests/compositions/unlocked-dep-simple/composed.wat
+++ b/crates/wasm-compose/tests/compositions/unlocked-dep-simple/composed.wat
@@ -34,4 +34,4 @@
       (with "unlocked-dep=<foo:add@{>=1.0.0}>" (instance 1))
     )
   )
-)
\ No newline at end of file
+)
diff --git a/crates/wit-component/tests/interfaces/doc-comments/foo.wit.print b/crates/wit-component/tests/interfaces/doc-comments/foo.wit.print
index 91e3bebb..b9d95f58 100644
--- a/crates/wit-component/tests/interfaces/doc-comments/foo.wit.print
+++ b/crates/wit-component/tests/interfaces/doc-comments/foo.wit.print
@@ -1,78 +1,79 @@
-/// package docs;
+// package docs;
 package foo:foo;
 
-/// interface docs
+// interface docs
 interface coverage-iface {
-  /// basic typedef docs
+  // basic typedef docs
   type t = u32;
 
-  /// record typedef docs
+  // record typedef docs
   record r {
-    /// record field docs
+    // record field docs
     f1: u8,
     f2: u8,
   }
 
   flags fl {
-    /// flag docs
+    // flag docs
     f1,
     f2,
   }
 
   variant v {
-    /// variant case docs
+    // variant case docs
     c1(u8),
     c2,
   }
 
   enum e {
-    /// enum case docs
+    // enum case docs
     c1,
     c2,
   }
 
   resource res {
-    /// constructor docs
+    // constructor docs
     constructor();
-    /// method docs
+    // method docs
     m: func();
-    /// static func docs
+    // static func docs
     s: static func();
   }
 
-  /// interface func docs
+  // interface func docs
   f: func();
 }
 
-/// other comment forms
-///     multi-line block
+// other comment forms
+//     multi-line block
 interface other-comment-forms {
-  /// one doc line
-  /// another doc line
+  // one doc line
+  // non-doc in the middle
+  // another doc line
   multiple-lines-split: func();
 
-  /// mixed forms; line doc
-  /// plus block doc
-  ///       multi-line
+  // mixed forms; line doc
+  // plus block doc
+  //       multi-line
   mixed-forms: func();
 }
 
-/// world docs
+// world docs
 world coverage-world {
-  /// world func import docs
+  // world func import docs
   import imp: func();
 
-  /// world typedef docs
+  // world typedef docs
   type t = u32;
 
-  /// world func export docs
+  // world func export docs
   export exp: func();
-  /// world inline interface docs
+  // world inline interface docs
   export i: interface {
-    /// inline interface typedef docs
+    // inline interface typedef docs
     type t = u32;
 
-    /// inline interface func docs
+    // inline interface func docs
     f: func();
   }
 }
diff --git a/crates/wit-component/tests/interfaces/resources.wit.print b/crates/wit-component/tests/interfaces/resources.wit.print
index b2a22dcd..ffe015c8 100644
--- a/crates/wit-component/tests/interfaces/resources.wit.print
+++ b/crates/wit-component/tests/interfaces/resources.wit.print
@@ -31,14 +31,19 @@ interface implicit-own-handles {
 }
 
 interface implicit-own-handles2 {
+  // the `own` return and list param should be the same `own`
   resource a {
     constructor(a: list<a>);
   }
 
+  // same as above, even when the `list<b>` implicitly-defined `own` comes
+  // before an explicitly defined `own`
   resource b {
     constructor(a: list<b>, b: b);
   }
 
+  // same as the above, the `own` argument should have the same type as the
+  // return value
   resource c {
     a: static func(a: c) -> c;
   }
@@ -64,6 +69,8 @@ world some-world {
   export y: func() -> bar;
 }
 world implicit-own-handles3 {
+  // there should only be one `list` type despite there looking like two
+  // list types here
   resource a {
     constructor(a: list<a>, b: list<a>);
   }
diff --git a/crates/wit-component/tests/interfaces/wasi-http/http.wit.print b/crates/wit-component/tests/interfaces/wasi-http/http.wit.print
index a2af2451..84939e2a 100644
--- a/crates/wit-component/tests/interfaces/wasi-http/http.wit.print
+++ b/crates/wit-component/tests/interfaces/wasi-http/http.wit.print
@@ -1,9 +1,13 @@
 package wasi:http;
 
+// The `wasi:http/types` interface is meant to be imported by components to
+// define the HTTP resource types and operations used by the component's
+// imported and exported interfaces.
 interface types {
   use wasi:io/streams.{input-stream, output-stream};
   use wasi:poll/poll.{pollable};
 
+  // This type corresponds to HTTP standard Methods.
   variant method {
     get,
     head,
@@ -17,12 +21,16 @@ interface types {
     other(string),
   }
 
+  // This type corresponds to HTTP standard Related Schemes.
   variant scheme {
     HTTP,
     HTTPS,
     other(string),
   }
 
+  // TODO: perhaps better align with HTTP semantics?
+  // This type enumerates the different kinds of errors that may occur when
+  // initially returning a response.
   variant error {
     invalid-url(string),
     timeout-error(string),
@@ -30,34 +38,81 @@ interface types {
     unexpected-error(string),
   }
 
+  // This following block defines the `fields` resource which corresponds to
+  // HTTP standard Fields. Soon, when resource types are added, the `type
+  // fields = u32` type alias can be replaced by a proper `resource fields`
+  // definition containing all the functions using the method syntactic sugar.
   type fields = u32;
 
   type headers = fields;
 
   type trailers = fields;
 
+  // The following block defines stream types which corresponds to the HTTP
+  // standard Contents and Trailers. With Preview3, all of these fields can be
+  // replaced by a stream<u8, option<trailers>>. In the interim, we need to
+  // build on separate resource types defined by `wasi:io/streams`. The
+  // `finish-` functions emulate the stream's result value and MUST be called
+  // exactly once after the final read/write from/to the stream before dropping
+  // the stream.
   type incoming-stream = input-stream;
 
   type outgoing-stream = output-stream;
 
+  // The following block defines the `incoming-request` and `outgoing-request`
+  // resource types that correspond to HTTP standard Requests. Soon, when
+  // resource types are added, the `u32` type aliases can be replaced by
+  // proper `resource` type definitions containing all the functions as
+  // methods. Later, Preview2 will allow both types to be merged together into
+  // a single `request` type (that uses the single `stream` type mentioned
+  // above). The `consume` and `write` methods may only be called once (and
+  // return failure thereafter).
   type incoming-request = u32;
 
   type outgoing-request = u32;
 
+  // Additional optional parameters that can be set when making a request.
   record request-options {
+    // The following timeouts are specific to the HTTP protocol and work
+    // independently of the overall timeouts passed to `io.poll.poll-oneoff`.
+    // The timeout for the initial connect.
     connect-timeout-ms: option<u32>,
+    // The timeout for receiving the first byte of the response body.
     first-byte-timeout-ms: option<u32>,
+    // The timeout for receiving the next chunk of bytes in the response body
+    // stream.
     between-bytes-timeout-ms: option<u32>,
   }
 
+  // The following block defines a special resource type used by the
+  // `wasi:http/incoming-handler` interface. When resource types are added, this
+  // block can be replaced by a proper `resource response-outparam { ... }`
+  // definition. Later, with Preview3, the need for an outparam goes away entirely
+  // (the `wasi:http/handler` interface used for both incoming and outgoing can
+  // simply return a `stream`).
   type response-outparam = u32;
 
+  // This type corresponds to the HTTP standard Status Code.
   type status-code = u16;
 
+  // The following block defines the `incoming-response` and `outgoing-response`
+  // resource types that correspond to HTTP standard Responses. Soon, when
+  // resource types are added, the `u32` type aliases can be replaced by proper
+  // `resource` type definitions containing all the functions as methods. Later,
+  // Preview2 will allow both types to be merged together into a single `response`
+  // type (that uses the single `stream` type mentioned above). The `consume` and
+  // `write` methods may only be called once (and return failure thereafter).
   type incoming-response = u32;
 
   type outgoing-response = u32;
 
+  // The following block defines a special resource type used by the
+  // `wasi:http/outgoing-handler` interface to emulate
+  // `future<result<response, error>>` in advance of Preview3. Given a
+  // `future-incoming-response`, the client can call the non-blocking `get`
+  // method to get the result if it is available. If the result is not available,
+  // the client can call `listen` to get a `pollable` that can be passed to
+  // `io.poll.poll-oneoff`.
   type future-incoming-response = u32;
 
   drop-fields: func(fields: fields);
@@ -127,18 +182,48 @@ interface types {
   listen-to-future-incoming-response: func(f: future-incoming-response) -> pollable;
 }
 
+// The `wasi:http/incoming-handler` interface is meant to be exported by
+// components and called by the host in response to a new incoming HTTP
+// response.
+//
+// NOTE: in Preview3, this interface will be merged with
+// `wasi:http/outgoing-handler` into a single `wasi:http/handler` interface
+// that takes a `request` parameter and returns a `response` result.
 interface incoming-handler {
   use types.{incoming-request, response-outparam};
 
+  // The `handle` function takes an outparam instead of returning its response
+  // so that the component may stream its response while streaming any other
+  // request or response bodies. The callee MUST write a response to the
+  // `response-out` and then finish the response before returning. The caller
+  // is expected to start streaming the response once `set-response-outparam`
+  // is called and finish streaming the response when `drop-response-outparam`
+  // is called. The `handle` function is then allowed to continue executing
+  // any post-response logic before returning. While this post-response
+  // execution is taken off the critical path, since there is no return value,
+  // there is no way to report its success or failure.
   handle: func(request: incoming-request, response-out: response-outparam);
 }
 
+// The `wasi:http/outgoing-handler` interface is meant to be imported by
+// components and implemented by the host.
+//
+// NOTE: in Preview3, this interface will be merged with
+// `wasi:http/outgoing-handler` into a single `wasi:http/handler` interface
+// that takes a `request` parameter and returns a `response` result.
 interface outgoing-handler {
   use types.{outgoing-request, request-options, future-incoming-response};
 
+  // The parameter and result types of the `handle` function allow the caller
+  // to concurrently stream the bodies of the outgoing request and the incoming
+  // response.
   handle: func(request: outgoing-request, options: option<request-options>) -> future-incoming-response;
 }
 
+// The `wasi:http/proxy` world captures a widely-implementable intersection of
+// hosts that includes HTTP forward and reverse proxies. Components targeting
+// this world may concurrently stream in and out any number of incoming and
+// outgoing HTTP requests.
 world proxy {
   import wasi:random/random;
   import wasi:logging/handler;
diff --git a/crates/wit-parser/tests/ui/comments.wit.json b/crates/wit-parser/tests/ui/comments.wit.json
index fd763de8..f2a04f9d 100644
--- a/crates/wit-parser/tests/ui/comments.wit.json
+++ b/crates/wit-parser/tests/ui/comments.wit.json
@@ -8,6 +8,9 @@
         "bar": 1
       },
       "functions": {},
+      "docs": {
+        "contents": "hello\nworld\nwhy, yes\nthis is a comment\n* this too */\n* is a comment */\n* this /* is /* a */ nested */ comment */"
+      },
       "package": 0
     }
   ],
diff --git a/crates/wit-parser/tests/ui/types.wit.json b/crates/wit-parser/tests/ui/types.wit.json
index 2839365b..fb9316aa 100644
--- a/crates/wit-parser/tests/ui/types.wit.json
+++ b/crates/wit-parser/tests/ui/types.wit.json
@@ -736,6 +736,9 @@
       },
       "owner": {
         "interface": 0
+      },
+      "docs": {
+        "contents": "type order doesn't matter"
       }
     }
   ],
diff --git a/crates/wit-parser/tests/ui/wasi.wit.json b/crates/wit-parser/tests/ui/wasi.wit.json
index d568d859..e784fbd2 100644
--- a/crates/wit-parser/tests/ui/wasi.wit.json
+++ b/crates/wit-parser/tests/ui/wasi.wit.json
@@ -19,10 +19,16 @@
         "enum": {
           "cases": [
             {
-              "name": "realtime"
+              "name": "realtime",
+              "docs": {
+                "contents": "The clock measuring real time. Time value zero corresponds with\n1970-01-01T00:00:00Z."
+              }
             },
             {
-              "name": "monotonic"
+              "name": "monotonic",
+              "docs": {
+                "contents": "The store-wide monotonic clock, which is defined as a clock measuring\nreal time, whose value cannot be adjusted and which cannot have negative\nclock jumps. The epoch of this clock is undefined. The absolute time\nvalue of this clock therefore has no meaning."
+              }
             }
           ]
         }
@@ -38,6 +44,9 @@
       },
       "owner": {
         "interface": 0
+      },
+      "docs": {
+        "contents": "Timestamp in nanoseconds."
       }
     },
     {
@@ -46,241 +55,475 @@
         "enum": {
           "cases": [
             {
-              "name": "success"
+              "name": "success",
+              "docs": {
+                "contents": "No error occurred. System call completed successfully."
+              }
             },
             {
-              "name": "toobig"
+              "name": "toobig",
+              "docs": {
+                "contents": "Argument list too long."
+              }
             },
             {
-              "name": "access"
+              "name": "access",
+              "docs": {
+                "contents": "Permission denied."
+              }
             },
             {
-              "name": "addrinuse"
+              "name": "addrinuse",
+              "docs": {
+                "contents": "Address in use."
+              }
             },
             {
-              "name": "addrnotavail"
+              "name": "addrnotavail",
+              "docs": {
+                "contents": "Address not available."
+              }
             },
             {
-              "name": "afnosupport"
+              "name": "afnosupport",
+              "docs": {
+                "contents": "Address family not supported."
+              }
             },
             {
-              "name": "again"
+              "name": "again",
+              "docs": {
+                "contents": "Resource unavailable, or operation would block."
+              }
             },
             {
-              "name": "already"
+              "name": "already",
+              "docs": {
+                "contents": "Connection already in progress."
+              }
             },
             {
-              "name": "badf"
+              "name": "badf",
+              "docs": {
+                "contents": "Bad file descriptor."
+              }
             },
             {
-              "name": "badmsg"
+              "name": "badmsg",
+              "docs": {
+                "contents": "Bad message."
+              }
             },
             {
-              "name": "busy"
+              "name": "busy",
+              "docs": {
+                "contents": "Device or resource busy."
+              }
             },
             {
-              "name": "canceled"
+              "name": "canceled",
+              "docs": {
+                "contents": "Operation canceled."
+              }
             },
             {
-              "name": "child"
+              "name": "child",
+              "docs": {
+                "contents": "No child processes."
+              }
             },
             {
-              "name": "connaborted"
+              "name": "connaborted",
+              "docs": {
+                "contents": "Connection aborted."
+              }
             },
             {
-              "name": "connrefused"
+              "name": "connrefused",
+              "docs": {
+                "contents": "Connection refused."
+              }
             },
             {
-              "name": "connreset"
+              "name": "connreset",
+              "docs": {
+                "contents": "Connection reset."
+              }
             },
             {
-              "name": "deadlk"
+              "name": "deadlk",
+              "docs": {
+                "contents": "Resource deadlock would occur."
+              }
             },
             {
-              "name": "destaddrreq"
+              "name": "destaddrreq",
+              "docs": {
+                "contents": "Destination address required."
+              }
             },
             {
-              "name": "dom"
+              "name": "dom",
+              "docs": {
+                "contents": "Mathematics argument out of domain of function."
+              }
             },
             {
-              "name": "dquot"
+              "name": "dquot",
+              "docs": {
+                "contents": "Reserved."
+              }
             },
             {
-              "name": "exist"
+              "name": "exist",
+              "docs": {
+                "contents": "File exists."
+              }
             },
             {
-              "name": "fault"
+              "name": "fault",
+              "docs": {
+                "contents": "Bad address."
+              }
             },
             {
-              "name": "fbig"
+              "name": "fbig",
+              "docs": {
+                "contents": "File too large."
+              }
             },
             {
-              "name": "hostunreach"
+              "name": "hostunreach",
+              "docs": {
+                "contents": "Host is unreachable."
+              }
             },
             {
-              "name": "idrm"
+              "name": "idrm",
+              "docs": {
+                "contents": "Identifier removed."
+              }
             },
             {
-              "name": "ilseq"
+              "name": "ilseq",
+              "docs": {
+                "contents": "Illegal byte sequence."
+              }
             },
             {
-              "name": "inprogress"
+              "name": "inprogress",
+              "docs": {
+                "contents": "Operation in progress."
+              }
             },
             {
-              "name": "intr"
+              "name": "intr",
+              "docs": {
+                "contents": "Interrupted function."
+              }
             },
             {
-              "name": "inval"
+              "name": "inval",
+              "docs": {
+                "contents": "Invalid argument."
+              }
             },
             {
-              "name": "io"
+              "name": "io",
+              "docs": {
+                "contents": "I/O error."
+              }
             },
             {
-              "name": "isconn"
+              "name": "isconn",
+              "docs": {
+                "contents": "Socket is connected."
+              }
             },
             {
-              "name": "isdir"
+              "name": "isdir",
+              "docs": {
+                "contents": "Is a directory."
+              }
             },
             {
-              "name": "loop"
+              "name": "loop",
+              "docs": {
+                "contents": "Too many levels of symbolic links."
+              }
             },
             {
-              "name": "mfile"
+              "name": "mfile",
+              "docs": {
+                "contents": "File descriptor value too large."
+              }
             },
             {
-              "name": "mlink"
+              "name": "mlink",
+              "docs": {
+                "contents": "Too many links."
+              }
             },
             {
-              "name": "msgsize"
+              "name": "msgsize",
+              "docs": {
+                "contents": "Message too large."
+              }
             },
             {
-              "name": "multihop"
+              "name": "multihop",
+              "docs": {
+                "contents": "Reserved."
+              }
             },
             {
-              "name": "nametoolong"
+              "name": "nametoolong",
+              "docs": {
+                "contents": "Filename too long."
+              }
             },
             {
-              "name": "netdown"
+              "name": "netdown",
+              "docs": {
+                "contents": "Network is down."
+              }
             },
             {
-              "name": "netreset"
+              "name": "netreset",
+              "docs": {
+                "contents": "Connection aborted by network."
+              }
             },
             {
-              "name": "netunreach"
+              "name": "netunreach",
+              "docs": {
+                "contents": "Network unreachable."
+              }
             },
             {
-              "name": "nfile"
+              "name": "nfile",
+              "docs": {
+                "contents": "Too many files open in system."
+              }
             },
             {
-              "name": "nobufs"
+              "name": "nobufs",
+              "docs": {
+                "contents": "No buffer space available."
+              }
             },
             {
-              "name": "nodev"
+              "name": "nodev",
+              "docs": {
+                "contents": "No such device."
+              }
             },
             {
-              "name": "noent"
+              "name": "noent",
+              "docs": {
+                "contents": "No such file or directory."
+              }
             },
             {
-              "name": "noexec"
+              "name": "noexec",
+              "docs": {
+                "contents": "Executable file format error."
+              }
             },
             {
-              "name": "nolck"
+              "name": "nolck",
+              "docs": {
+                "contents": "No locks available."
+              }
             },
             {
-              "name": "nolink"
+              "name": "nolink",
+              "docs": {
+                "contents": "Reserved."
+              }
             },
             {
-              "name": "nomem"
+              "name": "nomem",
+              "docs": {
+                "contents": "Not enough space."
+              }
             },
             {
-              "name": "nomsg"
+              "name": "nomsg",
+              "docs": {
+                "contents": "No message of the desired type."
+              }
             },
             {
-              "name": "noprotoopt"
+              "name": "noprotoopt",
+              "docs": {
+                "contents": "Protocol not available."
+              }
             },
             {
-              "name": "nospc"
+              "name": "nospc",
+              "docs": {
+                "contents": "No space left on device."
+              }
             },
             {
-              "name": "nosys"
+              "name": "nosys",
+              "docs": {
+                "contents": "Function not supported."
+              }
             },
             {
-              "name": "notconn"
+              "name": "notconn",
+              "docs": {
+                "contents": "The socket is not connected."
+              }
             },
             {
-              "name": "notdir"
+              "name": "notdir",
+              "docs": {
+                "contents": "Not a directory or a symbolic link to a directory."
+              }
             },
             {
-              "name": "notempty"
+              "name": "notempty",
+              "docs": {
+                "contents": "Directory not empty."
+              }
             },
             {
-              "name": "notrecoverable"
+              "name": "notrecoverable",
+              "docs": {
+                "contents": "State not recoverable."
+              }
             },
             {
-              "name": "notsock"
+              "name": "notsock",
+              "docs": {
+                "contents": "Not a socket."
+              }
             },
             {
-              "name": "notsup"
+              "name": "notsup",
+              "docs": {
+                "contents": "Not supported, or operation not supported on socket."
+              }
             },
             {
-              "name": "notty"
+              "name": "notty",
+              "docs": {
+                "contents": "Inappropriate I/O control operation."
+              }
             },
             {
-              "name": "nxio"
+              "name": "nxio",
+              "docs": {
+                "contents": "No such device or address."
+              }
             },
             {
-              "name": "overflow"
+              "name": "overflow",
+              "docs": {
+                "contents": "Value too large to be stored in data type."
+              }
             },
             {
-              "name": "ownerdead"
+              "name": "ownerdead",
+              "docs": {
+                "contents": "Previous owner died."
+              }
             },
             {
-              "name": "perm"
+              "name": "perm",
+              "docs": {
+                "contents": "Operation not permitted."
+              }
             },
             {
-              "name": "pipe"
+              "name": "pipe",
+              "docs": {
+                "contents": "Broken pipe."
+              }
             },
             {
-              "name": "proto"
+              "name": "proto",
+              "docs": {
+                "contents": "Protocol error."
+              }
             },
             {
-              "name": "protonosupport"
+              "name": "protonosupport",
+              "docs": {
+                "contents": "Protocol not supported."
+              }
             },
             {
-              "name": "prototype"
+              "name": "prototype",
+              "docs": {
+                "contents": "Protocol wrong type for socket."
+              }
             },
             {
-              "name": "range"
+              "name": "range",
+              "docs": {
+                "contents": "Result too large."
+              }
             },
             {
-              "name": "rofs"
+              "name": "rofs",
+              "docs": {
+                "contents": "Read-only file system."
+              }
             },
             {
-              "name": "spipe"
+              "name": "spipe",
+              "docs": {
+                "contents": "Invalid seek."
+              }
             },
             {
-              "name": "srch"
+              "name": "srch",
+              "docs": {
+                "contents": "No such process."
+              }
             },
             {
-              "name": "stale"
+              "name": "stale",
+              "docs": {
+                "contents": "Reserved."
+              }
             },
             {
-              "name": "timedout"
+              "name": "timedout",
+              "docs": {
+                "contents": "Connection timed out."
+              }
             },
             {
-              "name": "txtbsy"
+              "name": "txtbsy",
+              "docs": {
+                "contents": "Text file busy."
+              }
             },
             {
-              "name": "xdev"
+              "name": "xdev",
+              "docs": {
+                "contents": "Cross-device link."
+              }
             },
             {
-              "name": "notcapable"
+              "name": "notcapable",
+              "docs": {
+                "contents": "Extension: Capabilities insufficient."
+              }
             }
           ]
         }
       },
       "owner": {
         "interface": 0
+      },
+      "docs": {
+        "contents": "Error codes returned by functions.\nNot all of these error codes are returned by the functions provided by this\nAPI/ some are used in higher-level library layers, and others are provided\nmerely for alignment with POSIX."
       }
     }
   ],
diff --git a/tests/snapshots/testsuite/proposals/exception-handling/try_table.wast/0.print b/tests/snapshots/testsuite/proposals/exception-handling/try_table.wast/0.print
deleted file mode 100644
index 4ca55c3b..00000000
--- a/tests/snapshots/testsuite/proposals/exception-handling/try_table.wast/0.print
+++ /dev/null
@@ -1,9 +0,0 @@
-(module
-  (type (;0;) (func))
-  (func (;0;) (type 0)
-    throw 0
-  )
-  (tag (;0;) (type 0))
-  (export "e0" (tag 0))
-  (export "throw" (func 0))
-)
\ No newline at end of file