Skip to content

super_errors: error variant catalog + final fixture sweep#8446

Open
JonoPrest wants to merge 17 commits into
rescript-lang:masterfrom
JonoPrest:jono/expand-coverage-4
Open

super_errors: error variant catalog + final fixture sweep#8446
JonoPrest wants to merge 17 commits into
rescript-lang:masterfrom
JonoPrest:jono/expand-coverage-4

Conversation

@JonoPrest
Copy link
Copy Markdown
Contributor

Follow-up to #8433. Closes out every remaining reachable error / warning
variant in super_errors / super_errors_multi and turns
tests/ERROR_VARIANTS.md into an exhaustive map.

After this PR every row in ERROR_VARIANTS.md
is one of: covered, verified dead with a source-level
reason, or ☐ (needs <X> harness) reachable only with a CLI /
binary / PPX / build harness. No ? rows remain.

New fixtures (50)

Module Single-file Multi-file
compiler/ml/typecore.ml 14
compiler/ml/typedecl.ml 3
compiler/ml/typetexp.ml 2
compiler/ml/typemod.ml 1
compiler/frontend/bs_syntaxerr.ml 5
compiler/ml/ast_untagged_variants.ml 13
compiler/ml/includecore.ml 9
Other clash sub-cases (Expr_type_clash / Pattern_type_clash) 3

Plus tests/ERROR_VARIANTS.md
(605 lines) — a per-module table keyed off type error = …
constructors, every row tagged ✓ / ⚠ / ☐ with the fixture, blocker, or
required harness. AGENTS.md is updated with a "keep the catalog in
sync" rule.

Newly classified as dead (⚠)

10 variants are promoted from ? to ⚠ with a specific source-level
blocker (defensive try unify paths subsumed by sibling errors, AST
shapes the parser doesn't construct, warnings suppressed before their
own check fires, etc.). Combined with prior PRs, the catalog now lists
27 verified-dead error variants and 14 dead warning constructors
— all candidates for a follow-up removal PR.

Coverage impact

Measured locally with make coverage (bisect_ppx point coverage).
Baseline is upstream/master (0a29fc8a9).

Metric Baseline This branch Δ
Overall 52.33% (38219 / 73037) 53.07% (38762 / 73037) +0.74 pp / +543 points

Per-file moves (top by Δ lines covered):

File Baseline This branch Δ pp Δ
compiler/frontend/bs_syntaxerr.ml 65.71% 88.57% +22.86 +8
compiler/ml/ast_untagged_variants.ml 50.26% 57.25% +6.99 +27
compiler/ml/typetexp.ml 72.65% 78.70% +6.05 +27
compiler/ml/includecore.ml 61.94% 67.61% +5.67 +14
compiler/ml/typemod.ml 59.60% 62.99% +3.39 +27
compiler/ml/typedecl.ml 69.95% 73.30% +3.35 +33
compiler/ml/typecore.ml 75.12% 76.53% +1.41 +31

JonoPrest added 17 commits May 26, 2026 15:01
A single reference doc enumerating every error variant the compiler can
emit, with each entry mapped to either a fixture, a reason the variant
is unreachable, or an open TODO.
Cover Unknown_literal (0z), Empty_record_literal (let bad = {}),
No_value_clauses (switch with only exception clauses),
Type_params_not_supported (variant spread pattern with type params),
and Not_a_variant_type (variant spread pattern of non-variant).
Cover Rebind_mismatch (rebinding constructor into different
extensible type) and Rebind_private (rebinding a private extension
constructor as public).
Cover Expect_int_or_string_or_json_literal (@as(bool) on wildcard
external arg), Unsupported_predicates (@get({weird: true}) on object
field), Bs_this_simple_pattern (@this with destructured self pattern),
and Optional_in_uncurried_bs_attribute (@this function with optional
argument).
Mark the typecore/typedecl/bs_syntaxerr variants now covered by the
new super_errors fixtures. Move warnings 10, 16 and 108 to the
"Confirmed dead" section: Statement_type is unreachable because
the only caller passes statement=false; Unerasable_optional_argument
is explicitly disabled inside type_function before its check; and
Bs_uninterpreted_delimiters needs a string literal with the legacy
"js" delimiter, which the modern scanner never produces. Warning 5
stays "live but no fixture" with a note that the raise sites look
effectively unreachable from plain ReScript source.
Cover Kind (record vs variant), Constraint (impl adds constraint
that the abstract interface doesn't), Manifest (different manifest
types), Variance (interface annotates +'a, impl differs), Field_arity
(constructor arg counts differ), Field_names (record field names
differ at position), Record_representation (boxed vs @unboxed), and
Immediate (@Immediate vs non-immediate manifest).
Type_mismatch fires when applying a type constructor with arguments
that violate a constraint clause (`type t<'a> = 'a constraint 'a = int`
called with `t<string>`). Not_a_variant fires when a polyvariant
inherits from a non-polyvariant (`[#X | int]`). Also catalog the new
fixtures, mark Conflict_bs_bs_this_bs_meth as dead (declared but
never raised), and downgrade Rebind_wrong_type to ? since I couldn't
construct a triggering shape.
…expected

Covers every ☐ variant in ast_untagged_variants.ml: OnlyOneUnknown,
AtMostOneObject/Instance/Function/Number/Bigint/Boolean,
DuplicateLiteral, ConstructorMoreThanOneArg, InvalidVariantAsAnnotation,
Duplicated_bs_as, InvalidVariantTagAnnotation, TagFieldNameConflict.
Also adds typemod_signature_expected.res — a `with type` on an inner
functor-typed module triggers Signature_expected when the parent
sig-extraction hits a non-Mty_signature module type. Updates the
catalog and marks ast_utf8_string_interp.* as dead (production
pipeline never calls check_and_transform; only OUnit tests do).
`@obj external make: (~x: _) => _ = ""` triggers the error at
optional-label position when no @as is supplied. Also marks
typedecl.Val_in_structure as dead: the only way to reach it is via
manually-constructed AST, since the parser's external-recovery path
emits a syntax error before the typechecker ever sees the
value declaration.
Adding `type t += A(int)` to a parametric extensible type
`type t<'a> = ..` triggers Extension_mismatch with an Arity
sub-error. Single-file fixture suffices — the variant fires inside
`transl_type_extension` regardless of cross-module context.
Mark each remaining ☐ row with the specific harness it would need
("needs build harness", "needs binary harness", "needs CLI
harness", "needs PPX harness") so the catalog distinguishes
"reachable but blocked by the source-only harnesses" from
"reachable and just unwritten". Also extend the Confirmed-dead
section with the new findings: Conflict_bs_bs_this_bs_meth (never
raised), ast_utf8_string_interp.* (only called from OUnit
transform_test), Val_in_structure (parser recovery emits syntax
error first), Illegal_value_name (parser never produces "->" or
#-containing idents), and warnings 10/16/108 (each blocked by a
specific code path).
Every remaining ☐ row now carries a parenthetical tag ("needs build
harness" / "needs binary harness" / "needs CLI harness" / "needs
PPX harness" / "needs harness flag") so a reader can tell at a
glance whether a variant is unwritten coverage vs. blocked by what
the harness invokes bsc with. Promotes Label_mismatch, Type_clash,
Cannot_eliminate_dependency, Non_generalizable_module, and
Illegal_reference_to_recursive_module from ☐ to ? — they have live
raise sites but every attempted reproduction hit a different error
first. Marks With_makes_applicative_functor_ill_typed and
includemod.Unbound_modtype_path as dead with the source-level
reason.
Each fixture exercises a distinct trace-shape that the existing
catalog notes as untested: StringConcat ("hi" ++ 42 routes through
the dedicated string-concat context message), IfReturn
(`let x: string = if … { 1 } else { 2 }` swaps the generic
"expected to have type" for the if-statement-specific framing),
and MaybeUnwrapOption (`let x: int = Some(5)` — option-vs-non-option
recognition path).
Six new fixtures cover variants previously marked ?:

- polymorphic_label.res — pattern that instantiates a 'a. record field
- cannot_quantify.res — 'a. (int as 'a) rebinds the universal
- polyvariant_present_has_conjunction.res — [< #A(int) & (string) > #A]
- polyvariant_present_has_no_type.res — [< #B > #A]
- non_generalizable_module.res — inner ref(None) escapes module type
- illegal_recursive_module_reference.res — module rec A: B.S = …

Eleven other ? rows are formally promoted to ⚠ with the specific
source-level reason the trigger site can't be reached from ReScript
syntax: Label_mismatch, Abstract_wrong_label, Incoherent_label_order,
Type_clash, Parameters_differ, Rebind_wrong_type, Bad_fixed_type,
Varying_anonymous, Unbound_type_constructor_2,
Cannot_eliminate_dependency, With_cannot_remove_constrained_type,
Unhandled_poly_type.

After this pass the catalog has no remaining ? rows — every variant
is either covered, marked dead with a concrete reason, or tagged
with the harness it would need to exercise.
The formatter normalised six fixture sources (whitespace around @this
self-pattern parens, object-type spread spacing, if/then/else
formatting on a single line). The error locations move with the
source, so re-snapshot the matching expected files.
Coverage (`make coverage`) showed 53% line coverage across the
compiler; the lowest-coverage error-emitting modules are
ast_untagged_variants.ml (56%), typemod.ml (62%), includecore.ml (63%),
includemod.ml (68%), typedecl.ml (73%), error_message_utils.ml (75%).
A targeted audit of the report-side branches in error_message_utils.ml
flagged the following untested user-visible sub-cases:

- pattern_type_clash_polyvariant.res — `switch (x: int) { | #A => …}`
  trips a Pattern_type_clash with the polyvariant-vs-concrete trace.
- pattern_type_clash_tuple_arity.res — pattern `(a, b, c)` against a
  `(int, string)` value (tuple-arity mismatch path).
- labeled_fn_argument_type_clash.res — `f(~x="hi")` against
  `(~x: int) =>` hits FunctionArgument with an explicit label name
  (distinct from the existing optional-arg / record-field sub-cases).
- Iface_variant_extra_constructor (multi) — implementation declares
  one more constructor than the interface; covers the `Field_missing`
  branch where the impl is the side with the extra.

Catalog notes updated to enumerate the Expr_type_clash and
Pattern_type_clash trace-shape fixtures so future readers can
audit sub-case coverage without spelunking the source.
@JonoPrest JonoPrest marked this pull request as ready for review May 26, 2026 16:06
@JonoPrest
Copy link
Copy Markdown
Contributor Author

@codex review

@JonoPrest JonoPrest requested a review from cknitt May 26, 2026 16:06
@codecov
Copy link
Copy Markdown

codecov Bot commented May 26, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 60.45%. Comparing base (0a29fc8) to head (06e5eaf).

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #8446      +/-   ##
==========================================
+ Coverage   60.10%   60.45%   +0.35%     
==========================================
  Files         373      373              
  Lines       54231    54231              
==========================================
+ Hits        32593    32787     +194     
+ Misses      21638    21444     -194     

see 22 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment thread tests/ERROR_VARIANTS.md
Comment on lines +1 to +16
# Compiler error variant catalog

A per-module table of every named error and warning variant the compiler
declares, tagged with whether a test fixture currently exercises it.

The catalog has two practical uses:

1. **Coverage expansion** — find rows tagged `☐` (reachable but no
fixture) and add a `super_errors` / `super_errors_multi` fixture for
each. The Notes column points at the trigger site and any required
AST shape.
2. **Dead-code removal** — rows tagged `⚠` are variants whose trigger
site is unreachable in the current parser / compiler, with a named
blocker. They can be deleted in a follow-up PR. The "Confirmed dead"
summary at the bottom groups them by reason.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This was really an intermediary working file for the agents to catalogue and work through error variants.

I have one more PR on top of this that removes the identified dead code and we can remove this file as well unless you think its valuable.

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Chef's kiss.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 26, 2026

Open in StackBlitz

rescript

npm i https://pkg.pr.new/rescript@8446

@rescript/darwin-arm64

npm i https://pkg.pr.new/@rescript/darwin-arm64@8446

@rescript/darwin-x64

npm i https://pkg.pr.new/@rescript/darwin-x64@8446

@rescript/linux-arm64

npm i https://pkg.pr.new/@rescript/linux-arm64@8446

@rescript/linux-x64

npm i https://pkg.pr.new/@rescript/linux-x64@8446

@rescript/runtime

npm i https://pkg.pr.new/@rescript/runtime@8446

@rescript/win32-x64

npm i https://pkg.pr.new/@rescript/win32-x64@8446

commit: 06e5eaf

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