Skip to content

@rust JSDoc tags and --annotate side files - #66

Open
guybedford wants to merge 9 commits into
mainfrom
gbedford/annotations
Open

guybedford wants to merge 9 commits into
mainfrom
gbedford/annotations

Conversation

@guybedford

@guybedford guybedford commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

TS number lowers to f64, but most numbers a platform API carries are integers with a known width (Response.status is a u16, a list limit a u32), and upstream declaration files cannot carry annotations.

  • @rust <type> on a property or callable forces the member's own type; @rust <param>: <type> one named parameter. The scalar replaces the leaf through ? / | null, T[] and Promise<…>, wasm-bindgen converting at the boundary; a scalar slice parameter (ports: &[u16]) crosses as a JS array (slice = "array"), like the number[] it types. u64/i64 apply to a bigint leaf (lifted for number in Inline wrappers: @rust u64/i64 on a number, literal-keyed overloads; @throws {never} on constructors #68). A tag is chosen over a branded type u16 = number alias since upstream never uses one and an alias suggests a TS-level distinction that does not exist.
  • --annotate <side.d.ts> layers @rust and @throws tags onto the input by declaration and member name; the side file is not emitted; a side name the input lacks, an invalid tag, or a tag the type cannot carry fails the run.
  • A side declaration's type-parameter defaults replace the input's. An alias instantiated at its defaults takes the side's (RequestInfo → &Request<JsValue, Incoming> in the fixture); on an interface or class they take effect once the type declaration carries its defaults (Type-level generics usable by their bare names #63), so the bare Request can read cf() as IncomingRequestCfProperties instead of upstream's erased CfProperties<…> union.

Unit tests cover every --annotate error path. workers-types snapshot unchanged (annotations are supplied by the consumer).

@guybedford
guybedford added this pull request to stack #70 October 15, 2026 06:48
@guybedford guybedford changed the title gbedford/annotations @rust JSDoc tags and --annotate side files Oct 15, 2026
@guybedford
guybedford marked this pull request as ready for review October 15, 2026 06:49
@guybedford
guybedford force-pushed the gbedford/annotations branch from 9530900 to f2b4a00 Compare October 16, 2026 06:56
@guybedford
guybedford force-pushed the gbedford/annotations branch from f2b4a00 to a29db92 Compare October 17, 2026 15:12
@guybedford
guybedford removed this pull request from stack #70 October 17, 2026 15:14
@guybedford
guybedford added this pull request to stack #158 October 17, 2026 15:15
@guybedford
guybedford removed this pull request from stack #158 October 17, 2026 19:18
@guybedford
guybedford added this pull request to stack #159 October 17, 2026 19:18
@guybedford
guybedford force-pushed the gbedford/annotations branch from a29db92 to e4ec68d Compare October 18, 2026 00:09
@guybedford
guybedford removed this pull request from stack #159 October 24, 2026 01:29
@guybedford
guybedford changed the base branch from gbedford/inheritance-namespaces-unions to main October 24, 2026 01:30
@guybedford
guybedford force-pushed the gbedford/annotations branch from e4ec68d to d8bed42 Compare October 24, 2026 01:30
@guybedford
guybedford added this pull request to stack #178 October 24, 2026 01:32

@wasm-bindgen-bot wasm-bindgen-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR adds @rust <scalar> JSDoc tags, which narrow a number/bigint leaf to a Rust integer or float type, and a --annotate flag that layers @rust/@throws tags (and type-parameter defaults) from a side .d.ts onto the input by name. It looks aimed at better @cloudflare/workers-types bindings: u16 statuses, an incoming cf. It chooses a tag over a branded type u16 = number alias and a side file over editing the vendored input, and the error reporting for unmatched names is thorough. The main problem: a tagged leaf inside a generic handle (Promise<number> outside an async return, number[][], (number | null)[]) lowers to a bare u32 inside Promise<…>/Array<…>/JsNullable<…>, which needs a JsGeneric type, so the generated code won't compile. There are also two smaller gaps: an alias in the side file whose name the input lacks is skipped silently, and the snapshot doesn't show the documented effect of a side default on a bare Request.

Not merge-ready:

  • @rust on a leaf inside a generic handle (Promise<number> getter or argument, number[][], (number | null)[]) emits a non-JsGeneric u32, so the output doesn't compile.
  • An alias in the side file whose name the input lacks is silently skipped instead of failing the run.
  • The documented side-default effect on a bare Request isn't reflected in the snapshot output.

Comment thread src/codegen/typemap.rs Resolved
Comment thread src/parse/annotate.rs Resolved
Comment thread CONVENTIONS.md Resolved
Comment thread src/parse/annotate.rs Resolved
@wasm-bindgen-bot wasm-bindgen-bot Bot added the merge-ready The bot's review of its own PR found no concerns; ready for a human approval label Oct 24, 2026
@guybedford guybedford added the do not land PR should not be merged label Oct 24, 2026
TS `number` lowers to `f64`, but most numbers a platform API carries
are integers with a known width: `Response.status` is a `u16`, a
socket port a `u16`, a list limit a `u32`. Bindings consumers then
cast at every call site, and the f64 signature is a lossy contract.

`@rust <type>` on a property or callable forces the member's own type
(property getter, setter and builder method, or a callable's return);
`@rust <param>: <type>` forces one named parameter. The scalar replaces
the leaf through `?`/`| null`, `T[]` and `Promise<...>`, and wasm-bindgen
converts the JS number at the boundary. wasm-bindgen crosses u64/i64 as
BigInt, so those apply to a `bigint` leaf only and every other scalar
to a `number` leaf; anything else, an unknown scalar or an unknown
parameter name is a diagnostic and the tag is ignored.

A JSDoc tag is chosen over a branded `type u16 = number` alias because
upstream declarations never use such an alias, so it would need
layering anyway, and an alias suggests a TS-level distinction that
does not exist.
Upstream declaration files (@cloudflare/workers-types, lib.dom.d.ts)
carry no @rust or @throws tags, and editing a vendored copy does not
survive an upgrade. A side file re-declares only the members it tags;
its tags are applied to the input's declarations by declaration and
member name and the side file itself is not emitted. @rust applies as
if written on the input member (parameters by name), @throws replaces
the member's failure mode, a side member applies to every overload of
the input method or function, and its rendered doc (the `## Errors`
section) is appended once. A declaration, member or parameter named in the side
file that the input lacks fails the run, as does an invalid @rust tag
or one the input's type cannot carry, so a mistake in the side file
cannot silently miss a binding.
…input's

Upstream's Request<CfHostMetadata, Cf = CfProperties<CfHostMetadata>>
defaults Cf to the union of the incoming and outgoing cf shapes, which
erases to JsValue, so every handler had to cast req.cf() to
IncomingRequestCfProperties. The default is a TypeScript choice the
side file can now override by parameter name, so the bare Request reads
cf() as the incoming properties; a default for a parameter the input
lacks fails the run.
@guybedford
guybedford force-pushed the gbedford/annotations branch from 9c29257 to e9b07ab Compare October 30, 2026 04:08
@wasm-bindgen-bot wasm-bindgen-bot Bot removed the merge-ready The bot's review of its own PR found no concerns; ready for a human approval label Oct 30, 2026

@wasm-bindgen-bot wasm-bindgen-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since my last review the PR was rebased onto #63, and new commits fix all three earlier concerns; their threads are already resolved. A tagged leaf inside a generic handle now stays Number/BigInt, and integration tests cover it. A side alias whose name is missing or ambiguous now fails the run. Only the ## Errors section of a side doc is carried over. One problem is new: since the rebase, the snapshot gives the emitted Request Rust defaults (Cf = Incoming) and fetch takes a bare &Request, but CONVENTIONS and the fixture comment still say the opposite.

Not merge-ready:

  • CONVENTIONS.md (the type-parameter defaults paragraph) and the annotate.d.ts fixture comment say a bare Request is unchanged and fetch takes &Request<JsValue, Incoming>. Since the rebase, the snapshot shows Request<…, Cf = Incoming> with Rust defaults and fetch(input: &Request).

Comment thread CONVENTIONS.md Resolved
Since type declarations carry their defaults, the side file's
`interface Request<Meta = unknown, Cf = Incoming>` makes the emitted
`Request<Meta = JsValue, Cf = Incoming>`, and the `RequestInfo`
expansion collapses to the bare `&Request`. The type-parameter defaults
paragraph and the `annotate` fixture comment now say so, matching the
snapshot.
@wasm-bindgen-bot wasm-bindgen-bot Bot added the merge-ready The bot's review of its own PR found no concerns; ready for a human approval label Oct 30, 2026
@guybedford guybedford removed the do not land PR should not be merged label Nov 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-ready The bot's review of its own PR found no concerns; ready for a human approval

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant