Skip to content

Commit f05efbe

Browse files
committed
super_errors: add high-value sub-case fixtures from coverage audit
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.
1 parent ea961d2 commit f05efbe

10 files changed

Lines changed: 72 additions & 2 deletions

tests/ERROR_VARIANTS.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,11 @@ Source: [typecore.ml:27](../compiler/ml/typecore.ml).
6767
| `Polymorphic_label` || `polymorphic_label.res` | Pattern that instantiates a polymorphic record field: `({f: (f: int => int)}: t) =>` constrains the universal `'a` of `f: 'a. 'a => 'a` to `int => int`. |
6868
| `Constructor_arity_mismatch` || `constructor_arity_mismatch.res`, `constructor_arity_mismatch_pattern.res`, `arity_mismatch*.res` | Triggers in both expression (4028) and pattern (1426) paths. |
6969
| `Label_mismatch` ||| typecore.ml:1543/3589. Defensive `try unify ty_res ty_expected with Unify -> Label_mismatch`. The only way to reach the unify is after label disambiguation (`type_label_a_list` / `type_label_pat` / `Wrong_name`-style logic), which always locks `ty_res` to a record type already unifiable with the expected. The unify therefore can't fail in practice — every reproduction hits `Wrong_name`, `Pattern_type_clash`, or `Expr_type_clash` instead. The constructor is a defensive leftover from the OCaml inheritance. |
70-
| `Pattern_type_clash` || many `*_pattern_type_clash.res` etc. | Most-fired pattern error; covered through many fixtures but report-side sub-cases (option-vs-non-option trace, polyvariant context, etc.) remain partly untested. |
70+
| `Pattern_type_clash` || many `*_pattern_type_clash.res` etc. | Most-fired pattern error. Sub-case fixtures: `pattern_matching_on_option_but_value_not_option.res` and `pattern_matching_on_value_but_is_option.res` (option-vs-non-option trace), `pattern_type_clash_polyvariant.res` (polyvariant tag against concrete type), `pattern_type_clash_tuple_arity.res` (tuple arity mismatch). |
7171
| `Or_pattern_type_clash` || `or_pattern_type_clash.res` | |
7272
| `Multiply_bound_variable` || `multiply_bound_variable.res` | |
7373
| `Orpat_vars` || `orpat_vars_unbalanced.res` | |
74-
| `Expr_type_clash` || many `*.res` | Most-fired expression error. Many trace-shape sub-cases (function-arg context, JSX, dict, async, polyvariant) covered piecemeal; sub-case coverage is the biggest open area for this variant. |
74+
| `Expr_type_clash` || many `*.res` | Most-fired expression error. Trace-shape sub-cases covered: `if_return_type_mismatch.res` (IfReturn), `maybe_unwrap_option.res` (MaybeUnwrapOption), `string_concat_non_string.res` (StringConcat), `labeled_fn_argument_type_clash.res` (FunctionArgument with explicit label), `math_operator_*.res` (MathOperator family), `ternary_branch_mismatch.res`, `switch_different_types.res`, `try_catch_same_type.res`, `comparison_operator.res`, `array_item_type_mismatch.res`, `array_literal_passed_to_tuple.res`, `if_condition_mismatch.res`, `while_condition.res`, `for_loop_condition.res`, `assert_condition.res`, `function_call_mismatch.res`, `awaiting_non_promise.res`, multiple `jsx_*` fixtures. |
7575
| `Apply_non_function` || `apply_non_function.res` | |
7676
| `Apply_wrong_label` || `apply_wrong_label.res` | |
7777
| `Label_multiply_defined` || `label_multiply_defined_literal.res` | |
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
2+
We've found a bug for you!
3+
/.../fixtures/labeled_fn_argument_type_clash.res:3:14-17
4+
5+
1 │ let f = (~x: int) => x
6+
2 │
7+
3 │ let _ = f(~x="hi")
8+
4 │
9+
10+
This has type: string
11+
But this function argument ~x is expecting: int
12+
13+
You can convert string to int with Int.fromString.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
We've found a bug for you!
3+
/.../fixtures/pattern_type_clash_polyvariant.res:3:5-6
4+
5+
1 │ let f = (x: int) =>
6+
2 │ switch x {
7+
3 │ | #A => 1
8+
4 │ | _ => 0
9+
5 │ }
10+
11+
This pattern matches values of type [? #A]
12+
but a pattern was expected which matches values of type int
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
2+
We've found a bug for you!
3+
/.../fixtures/pattern_type_clash_tuple_arity.res:3:5-13
4+
5+
1 │ let f = (x: (int, string)) =>
6+
2 │ switch x {
7+
3 │ | (a, b, c) => a + Int.fromString(b)->Option.getOr(0) + c
8+
4 │ }
9+
5 │
10+
11+
This pattern matches values of type ('a, 'b, 'c)
12+
but a pattern was expected which matches values of type (int, string)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
let f = (~x: int) => x
2+
3+
let _ = f(~x="hi")
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
let f = (x: int) =>
2+
switch x {
3+
| #A => 1
4+
| _ => 0
5+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
let f = (x: (int, string)) =>
2+
switch x {
3+
| (a, b, c) => a + Int.fromString(b)->Option.getOr(0) + c
4+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
===== Foo.res =====
2+
3+
We've found a bug for you!
4+
/.../fixtures/Iface_variant_extra_constructor/Foo.res:1:1-18
5+
6+
1 │ type t = A | B | C
7+
2 │
8+
9+
The implementation /.../fixtures/Iface_variant_extra_constructor/Foo.res
10+
does not match the interface /.../fixtures/Iface_variant_extra_constructor/foo.cmi:
11+
Type declarations do not match:
12+
type t = A | B | C
13+
is not included in
14+
type t = A | B
15+
/.../fixtures/Iface_variant_extra_constructor/Foo.resi:1:1-14:
16+
Expected declaration
17+
/.../fixtures/Iface_variant_extra_constructor/Foo.res:1:1-18:
18+
Actual declaration
19+
The field C is only present in the first declaration.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
type t = A | B | C
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
type t = A | B

0 commit comments

Comments
 (0)