Motivation
EFF003/EFF004 (PR #72) enforce that a higher-order fn's capability surface must cover the uses {}, reads {}, and writes {} annotations on its callback parameters. The same principle applies to throws {}: if a callback parameter declares throws { NetworkError }, the outer fn can exercise that throw through any call to the callback — so the outer fn's own throws {} must cover it.
Currently there is no check for this. Example:
```bs
?bs 0.9
fn process(
items: string[],
handler: fn(string) throws { NetworkError } -> void
) -> void { // ← should fire THR003: missing throws { NetworkError }
handler(items[0])
}
```
If the outer fn calls handler(...), it can surface NetworkError. A reviewer reading the outer fn's header sees no throws declaration and has no warning that calling it may produce a network error.
Proposed Diagnostic
THR003: fires when a function-typed parameter carries throws { X } but the containing fn does not declare throws { X } in its own header.
This is the direct analogue of EFF003 (reads on callback) and EFF004 (writes on callback), applied to the throws surface.
Implementation Sketch
Extend thr-check.ts (or a new pass) to:
- Walk each
fn declaration's parameter list for function-typed parameters.
- For each such parameter, collect
throws {} labels (same pattern as paramReads/paramWrites in parse-fn.ts for EFF003/EFF004).
- Union those labels into the fn's required-throws set.
- Emit THR003 if any required label is absent from the fn's own
throws {} declaration.
Gate: ?bs 0.9 (same as THR001/THR002).
Prior Art in This Codebase
Notes
- Strip
throws {} from callback parameter types in emitted TypeScript (same as reads {}/writes {} stripping in buildArgsTs).
- The rule is "the fn's throws surface is the union of its own declared throws AND the throws its callback parameters may exercise."
- Closes the "callback throws-leak" vector — a higher-order fn cannot advertise a narrower throws surface than it can actually exercise.
Motivation
EFF003/EFF004 (PR #72) enforce that a higher-order fn's capability surface must cover the
uses {},reads {}, andwrites {}annotations on its callback parameters. The same principle applies tothrows {}: if a callback parameter declaresthrows { NetworkError }, the outer fn can exercise that throw through any call to the callback — so the outer fn's ownthrows {}must cover it.Currently there is no check for this. Example:
```bs
?bs 0.9
fn process(
items: string[],
handler: fn(string) throws { NetworkError } -> void
) -> void { // ← should fire THR003: missing throws { NetworkError }
handler(items[0])
}
```
If the outer fn calls
handler(...), it can surfaceNetworkError. A reviewer reading the outer fn's header sees no throws declaration and has no warning that calling it may produce a network error.Proposed Diagnostic
THR003: fires when a function-typed parameter carries
throws { X }but the containing fn does not declarethrows { X }in its own header.This is the direct analogue of EFF003 (reads on callback) and EFF004 (writes on callback), applied to the throws surface.
Implementation Sketch
Extend
thr-check.ts(or a new pass) to:fndeclaration's parameter list for function-typed parameters.throws {}labels (same pattern asparamReads/paramWritesinparse-fn.tsfor EFF003/EFF004).throws {}declaration.Gate:
?bs 0.9(same as THR001/THR002).Prior Art in This Codebase
reads {}/writes {}on callback params.Notes
throws {}from callback parameter types in emitted TypeScript (same asreads {}/writes {}stripping inbuildArgsTs).