Skip to content

ocaml: make property-coverage references a call-graph closure#15

Merged
subsetpark merged 1 commit into
masterfrom
zax/ocaml-property-closure
Jun 8, 2026
Merged

ocaml: make property-coverage references a call-graph closure#15
subsetpark merged 1 commit into
masterfrom
zax/ocaml-property-closure

Conversation

@subsetpark

@subsetpark subsetpark commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Problem

The core-coverage rule ("property tests must reference every decision API") harvested references only from the QCheck2.Test.make callback. So an API exercised through the generator (gen_graph building inputs via add_dependency) or through a higher-order test helper (totality (fun s -> Core.parse s)) was invisible — a recurring false-negative we kept patching per-case.

Fix — uniform, obligatory call-graph closure

Rather than harvest at one syntactic site, expansion now follows the call graph:

  1. All top-level bindings join the graph, not just functions — so a bound generator's references expand transitively.
  2. A property check harvests every argument (callback and generator), expanded through the graph.
  3. A binding that reaches a meaningful-property-bearing binding through the graph (e.g. a property produced by a higher-order helper) contributes its references to coverage.

The meaningfulness gate is preserved: a linking property over unit that only ignores a surface still doesn't count.

Why syntactic (not semantic)

The remaining hard case after this — a property selected at runtime from a data structure — is genuine dynamic dispatch, which semantic (typed-tree) resolution also wouldn't solve (it's flow, not name resolution; see #6). Call-graph closure is the right tool for reachability and is semantic-independent.

Tests (ocaml/test.sh)

  • New closure fixture: a core whose three decision APIs are reached directly, via a generator, and via a higher-order helper — all must count (no violation).
  • Regression: a linking-only unit property that ignores the API must still fail coverage.
  • Full OCaml suite passes; onton stays at 0 violations.

🤖 Generated with Claude Code


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.

The coverage rule harvested references only from the QCheck2.Test.make
callback, so an API exercised via the *generator* (`gen_graph` building
inputs with `add_dependency`) or via a *higher-order test helper*
(`totality (fun s -> Core.parse s)`) went uncounted — a recurring
false-negative whack-a-mole.

Make expansion uniform and obligatory instead of per-site:
- All top-level bindings (not just functions) join the call graph, so a
  bound generator's references expand transitively.
- A property check harvests references from every argument — callback and
  generator — expanded through the graph.
- A binding that reaches a meaningful-property-bearing binding through the
  call graph (e.g. a property built by a higher-order helper) contributes
  its references to coverage.

The meaningfulness gate is preserved: a linking property over `unit` that
only `ignore`s a surface still does not count (regression-tested).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@subsetpark subsetpark merged commit e0a2e5c into master Jun 8, 2026
5 checks passed
@subsetpark subsetpark deleted the zax/ocaml-property-closure branch June 8, 2026 18:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant