Skip to content

Commit 6beea1e

Browse files
authored
fix(wit-parser)!: preserve docs on world interface imports/exports (#2540)
Doc comments attached to a world's by-reference interface `import`/`export` statements (e.g. the comments above `import wasi:cli/stdout;` and `export handler;` in wasi:http's `world service`) were silently dropped when a WIT package was encoded to its binary component form. Fix this by: * adding a `docs` field to `WorldItem::Interface` and threading the statement docs through the resolver and world elaboration, * extracting/injecting those docs via the `package-docs` section, mirroring the existing per-interface stability mechanism, * printing them for `WorldKey::Interface` items, preferring the statement docs and only falling back to the interface definition's docs for inline interfaces. Docs on `include` statements remain unsupported. Includes are flattened into imports/exports on encode, so they cannot survive the binary round-trip. Adds a `world-interface-docs` round-trip test and updates the `wasi-http` snapshots to include the previously-dropped docs. Signed-off-by: Bailey Hayes <bailey@cosmonic.com>
1 parent d36e62a commit 6beea1e

12 files changed

Lines changed: 263 additions & 30 deletions

File tree

crates/wit-component/src/printing.rs

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -452,14 +452,26 @@ impl<O: Output> WitPrinter<O> {
452452
cur_pkg: PackageId,
453453
import_or_export_keyword: &str,
454454
) -> Result<()> {
455-
// Print inline item docs
456-
if matches!(name, WorldKey::Name(_)) {
457-
self.print_docs(match item {
458-
WorldItem::Interface { id, .. } => &resolve.interfaces[*id].docs,
459-
WorldItem::Function(f) => &f.docs,
460-
// Types are handled separately
461-
WorldItem::Type { .. } => unreachable!(),
462-
});
455+
// Print docs for this import/export statement. For interfaces, prefer
456+
// the docs attached to the statement itself (`WorldItem::Interface`'s
457+
// `docs`); for an inline `import x: interface { .. }` with no statement
458+
// docs fall back to the interface definition's docs.
459+
let docs = match item {
460+
WorldItem::Interface { id, docs, .. } => {
461+
if docs.contents.is_some() {
462+
Some(docs)
463+
} else if matches!(name, WorldKey::Name(_)) {
464+
Some(&resolve.interfaces[*id].docs)
465+
} else {
466+
None
467+
}
468+
}
469+
WorldItem::Function(f) => Some(&f.docs),
470+
// Types are handled separately
471+
WorldItem::Type { .. } => unreachable!(),
472+
};
473+
if let Some(docs) = docs {
474+
self.print_docs(docs);
463475
}
464476

465477
self.print_stability(item.stability(resolve));

crates/wit-component/tests/interfaces/wasi-http.wat

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

crates/wit-component/tests/interfaces/wasi-http/http.wit.print

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,13 +559,25 @@ world proxy {
559559
import wasi:io/error@0.2.0-rc-2023-11-10;
560560
import wasi:io/poll@0.2.0-rc-2023-11-10;
561561
import wasi:io/streams@0.2.0-rc-2023-11-10;
562+
/// Proxies have standard output and error streams which are expected to
563+
/// terminate in a developer-facing console provided by the host.
562564
import wasi:cli/stdout@0.2.0-rc-2023-12-05;
563565
import wasi:cli/stderr@0.2.0-rc-2023-12-05;
566+
/// TODO: this is a temporary workaround until component tooling is able to
567+
/// gracefully handle the absence of stdin. Hosts must return an eof stream
568+
/// for this import, which is what wasi-libc + tooling will do automatically
569+
/// when this import is properly removed.
564570
import wasi:cli/stdin@0.2.0-rc-2023-12-05;
565571
import wasi:clocks/monotonic-clock@0.2.0-rc-2023-11-10;
566572
import types;
573+
/// This is the default handler to use when user code simply wants to make an
574+
/// HTTP request (e.g., via `fetch()`).
567575
import outgoing-handler;
568576
import wasi:clocks/wall-clock@0.2.0-rc-2023-11-10;
569577

578+
/// The host delivers incoming HTTP requests to a component by calling the
579+
/// `handle` function of this exported interface. A host may arbitrarily reuse
580+
/// or not reuse component instance when delivering incoming HTTP requests and
581+
/// thus a component must be able to handle 0..N calls to `handle`.
570582
export incoming-handler;
571583
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
(component
2+
(type (;0;)
3+
(component
4+
(type (;0;)
5+
(instance
6+
(type (;0;) (func))
7+
(export (;0;) "f" (func (type 0)))
8+
)
9+
)
10+
(export (;0;) "foo:foo/imported" (instance (type 0)))
11+
)
12+
)
13+
(export (;1;) "imported" (type 0))
14+
(type (;2;)
15+
(component
16+
(type (;0;)
17+
(instance
18+
(type (;0;) (func))
19+
(export (;0;) "g" (func (type 0)))
20+
)
21+
)
22+
(export (;0;) "foo:foo/exported" (instance (type 0)))
23+
)
24+
)
25+
(export (;3;) "exported" (type 2))
26+
(type (;4;)
27+
(component
28+
(type (;0;)
29+
(component
30+
(type (;0;)
31+
(instance
32+
(type (;0;) (func))
33+
(export (;0;) "f" (func (type 0)))
34+
)
35+
)
36+
(import "foo:foo/imported" (instance (;0;) (type 0)))
37+
(type (;1;)
38+
(instance
39+
(type (;0;) (func))
40+
(export (;0;) "g" (func (type 0)))
41+
)
42+
)
43+
(export (;1;) "foo:foo/exported" (instance (type 1)))
44+
)
45+
)
46+
(export (;0;) "foo:foo/the-world" (component (type 0)))
47+
)
48+
)
49+
(export (;5;) "the-world" (type 4))
50+
(@custom "package-docs" "\01{\22docs\22:\22doc comments on by-reference interface imports/exports\5cninside a world (attached to the `import`/`export` statement itself, as\5cnopposed to the interface's own definition) must survive the binary WIT\5cnpackage round-trip via the `package-docs` custom section.\22,\22worlds\22:{\22the-world\22:{\22interface_import_docs\22:{\22foo:foo/imported\22:\22docs on the by-reference interface import statement\22},\22interface_export_docs\22:{\22foo:foo/exported\22:\22docs on the by-reference interface export statement\22}}},\22interfaces\22:{\22imported\22:{\22docs\22:\22the imported interface's own definition docs\22},\22exported\22:{\22docs\22:\22the exported interface's own definition docs\22}}}")
51+
(@producers
52+
(processed-by "wit-component" "$CARGO_PKG_VERSION")
53+
)
54+
)
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/// doc comments on by-reference interface imports/exports
2+
/// inside a world (attached to the `import`/`export` statement itself, as
3+
/// opposed to the interface's own definition) must survive the binary WIT
4+
/// package round-trip via the `package-docs` custom section.
5+
package foo:foo;
6+
7+
/// the imported interface's own definition docs
8+
interface imported {
9+
f: func();
10+
}
11+
12+
/// the exported interface's own definition docs
13+
interface exported {
14+
g: func();
15+
}
16+
17+
world the-world {
18+
/// docs on the by-reference interface import statement
19+
import imported;
20+
21+
/// docs on the by-reference interface export statement
22+
export exported;
23+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/// doc comments on by-reference interface imports/exports
2+
/// inside a world (attached to the `import`/`export` statement itself, as
3+
/// opposed to the interface's own definition) must survive the binary WIT
4+
/// package round-trip via the `package-docs` custom section.
5+
package foo:foo;
6+
7+
/// the imported interface's own definition docs
8+
interface imported {
9+
f: func();
10+
}
11+
12+
/// the exported interface's own definition docs
13+
interface exported {
14+
g: func();
15+
}
16+
17+
world the-world {
18+
/// docs on the by-reference interface import statement
19+
import imported;
20+
21+
/// docs on the by-reference interface export statement
22+
export exported;
23+
}

crates/wit-dylib/tests/roundtrip.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ fn run_one(u: &mut Unstructured<'_>) -> Result<()> {
8787
WorldItem::Interface {
8888
id: *id,
8989
stability: Default::default(),
90+
docs: Default::default(),
9091
span: Default::default(),
9192
},
9293
)
@@ -200,6 +201,7 @@ fn run_one(u: &mut Unstructured<'_>) -> Result<()> {
200201
WorldItem::Interface {
201202
id: alloc,
202203
stability: Default::default(),
204+
docs: Default::default(),
203205
span: Default::default(),
204206
},
205207
);
@@ -208,6 +210,7 @@ fn run_one(u: &mut Unstructured<'_>) -> Result<()> {
208210
WorldItem::Interface {
209211
id: alloc,
210212
stability: Default::default(),
213+
docs: Default::default(),
211214
span: Default::default(),
212215
},
213216
);

crates/wit-parser/src/ast/resolve.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -758,26 +758,31 @@ impl<'a> Resolver<'a> {
758758
Ok(WorldItem::Interface {
759759
id,
760760
stability,
761+
docs: Default::default(),
761762
span: name.span,
762763
})
763764
}
764765
ast::ExternKind::Path(path) => {
765766
let stability = self.stability(attrs)?;
767+
let docs = self.docs(docs);
766768
let (item, name, item_span) = self.resolve_ast_item_path(path)?;
767769
let id = self.extract_iface_from_item(&item, &name, item_span)?;
768770
Ok(WorldItem::Interface {
769771
id,
770772
stability,
773+
docs,
771774
span: item_span,
772775
})
773776
}
774777
ast::ExternKind::NamedPath(name, path) => {
775778
let stability = self.stability(attrs)?;
779+
let docs = self.docs(docs);
776780
let (item, iface_name, item_span) = self.resolve_ast_item_path(path)?;
777781
let id = self.extract_iface_from_item(&item, &iface_name, item_span)?;
778782
Ok(WorldItem::Interface {
779783
id,
780784
stability,
785+
docs,
781786
span: name.span,
782787
})
783788
}

crates/wit-parser/src/decoding.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,7 @@ impl WitPackageDecoder<'_> {
748748
WorldItem::Interface {
749749
id,
750750
stability: Default::default(),
751+
docs: Default::default(),
751752
span: Default::default(),
752753
},
753754
))

crates/wit-parser/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,12 @@ pub enum WorldItem {
523523
serde(skip_serializing_if = "Stability::is_unknown")
524524
)]
525525
stability: Stability,
526+
/// Documentation attached to the `import`/`export` statement.
527+
#[cfg_attr(
528+
feature = "serde",
529+
serde(default, skip_serializing_if = "Docs::is_empty")
530+
)]
531+
docs: Docs,
526532
#[cfg_attr(feature = "serde", serde(skip))]
527533
span: Span,
528534
},

0 commit comments

Comments
 (0)