diff --git a/AGENTS.md b/AGENTS.md index 4408a02822..838b5325cb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -168,7 +168,25 @@ tests/ 2. **Integration tests** (`tests/tests/`) - End-to-end behavior 3. **Unit tests** (`tests/ounit_tests/`) - Compiler functions 4. **Build tests** (`tests/build_tests/`) - Error cases and edge cases -5. **Type tests** (`tests/build_tests/super_errors/`) - Type checking behavior +5. **Type tests** (`tests/build_tests/super_errors/`) - Single-file type checking errors +6. **Multi-file error tests** (`tests/build_tests/super_errors_multi/`) - Cross-module errors that need separate `.res` / `.resi` files + +#### Error variant catalog + +[`tests/ERROR_VARIANTS.md`](tests/ERROR_VARIANTS.md) is a per-module +catalog of every error and warning variant the compiler can emit, with +each entry mapped to a fixture (or a documented reason it's unreachable). + +**When adding or removing an error variant**, also update the catalog: + +1. Add (or remove) the row in the relevant module section. +2. Set the status (`✓` covered / `⚠` unreachable / `☐` TODO). +3. If covered, link the fixture path; if unreachable, note the reason. + +**When adding or removing a fixture**, update the corresponding row's +`Fixture` and status columns so the catalog stays in sync with the test +suite. The catalog is the primary tool for finding coverage gaps and +dead-code removal candidates; stale entries make both jobs harder. ## Build Commands & Development diff --git a/CHANGELOG.md b/CHANGELOG.md index 04975c0288..a822740977 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,7 @@ - Expand `super_errors` fixture coverage for the remaining reachable single-file error variants. https://github.com/rescript-lang/rescript/pull/8432 - Cache OPAM env, rewatch build, and instrumented dune state in the coverage workflow. https://github.com/rescript-lang/rescript/pull/8434 - Add a multi-file fixture harness (`super_errors_multi`) for cross-module errors and warnings. https://github.com/rescript-lang/rescript/pull/8433 +- Catalog every named compiler error variant in `tests/ERROR_VARIANTS.md` and add fixtures for the remaining reachable ones. https://github.com/rescript-lang/rescript/pull/8446 # 13.0.0-alpha.4 diff --git a/tests/ERROR_VARIANTS.md b/tests/ERROR_VARIANTS.md new file mode 100644 index 0000000000..6f45ebbbf4 --- /dev/null +++ b/tests/ERROR_VARIANTS.md @@ -0,0 +1,605 @@ +# 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. + +## Status legend + +| Symbol | Meaning | +|---|---| +| ✓ | Covered by at least one fixture under `tests/build_tests/super_errors/` or `tests/build_tests/super_errors_multi/`. | +| ⚠ | **Verified** unreachable: the trigger site has a specific blocker named in source (an exception declared but never raised, an AST node the parser doesn't construct, a guard that's always false). Candidate for dead-code removal. | +| ☐ | Reachable but no fixture yet; would be valuable to add. | +| ? | Trigger site is live but reachability from regular ReScript source isn't confirmed. Distinct from ⚠: a `?` means "I couldn't find a fixture that reaches it" rather than "the path is provably blocked". | + +The "Confirmed dead" summary section at the bottom only includes ⚠. + +## Scope + +This catalog enumerates **named error variants** — constructors of +`type error = …` declarations across `compiler/`. The compiler also has +~94 `Location.raise_errorf` and similar inline calls that produce +user-facing errors without a named variant. Those are **not** catalogued +here because there's no constructor to track. If someone wants to add +coverage for one of those messages, the first step is refactoring it +into a named variant. + +## Fixture paths + +- Single-file fixtures live in [`build_tests/super_errors/fixtures/`](build_tests/super_errors/fixtures/) with expected output in [`build_tests/super_errors/expected/`](build_tests/super_errors/expected/). +- Multi-file fixtures live in [`build_tests/super_errors_multi/fixtures//`](build_tests/super_errors_multi/fixtures/) with expected output in [`build_tests/super_errors_multi/expected/.expected`](build_tests/super_errors_multi/expected/). + +## How to update + +When you add or remove an error variant in `compiler/`, update the +corresponding row here too: + +1. Add (or remove) the row in the appropriate module section below. +2. Write a fixture if the variant is reachable. Use `super_errors/` for + single-file scenarios, `super_errors_multi/` for cross-file ones. +3. Snapshot with `node tests/build_tests/super_errors{,_multi}/input.js update`. +4. Set the status column accordingly. + +If a variant turns out to be unreachable, document the named blocker +here (so it gets ⚠ instead of `?`) and file a follow-up to delete the +dead code. + +--- + +## `compiler/ml/typecore.ml` + +The largest error type; covers expression / pattern type-checking. +Source: [typecore.ml:27](../compiler/ml/typecore.ml). + +| Variant | Status | Fixture | Notes | +|---|---|---|---| +| `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`. | +| `Constructor_arity_mismatch` | ✓ | `constructor_arity_mismatch.res`, `constructor_arity_mismatch_pattern.res`, `arity_mismatch*.res` | Triggers in both expression (4028) and pattern (1426) paths. | +| `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. | +| `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). | +| `Or_pattern_type_clash` | ✓ | `or_pattern_type_clash.res` | | +| `Multiply_bound_variable` | ✓ | `multiply_bound_variable.res` | | +| `Orpat_vars` | ✓ | `orpat_vars_unbalanced.res` | | +| `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. | +| `Apply_non_function` | ✓ | `apply_non_function.res` | | +| `Apply_wrong_label` | ✓ | `apply_wrong_label.res` | | +| `Label_multiply_defined` | ✓ | `label_multiply_defined_literal.res` | | +| `Labels_missing` | ✓ | `missing_label.res`, `missing_labels.res` | | +| `Label_not_mutable` | ✓ | `label_not_mutable.res` | | +| `Wrong_name` | ✓ | `wrong_name_record_field.res`, `Cross_record_extra_field` (multi) | | +| `Name_type_mismatch` | ✓ | `super_errors_multi/Cross_qualified_constructor_mismatch` | Cross-module constructor disambiguation. | +| `Undefined_method` | ✓ | `super_errors_multi/Cross_module_alias_dot_access`, `undefined_method` | | +| `Private_type` | ✓ | `private_type_construction.res` | | +| `Private_label` | ✓ | `private_label.res` | | +| `Not_subtype` | ✓ | `subtype_*.res`, `dict_show_no_coercion.res`, etc. | | +| `Too_many_arguments` | ✓ | `too_many_arguments.res`, `moreArguments*.res` | | +| `Abstract_wrong_label` | ⚠ | — | typecore.ml:3502. Fires in `type_function` when `filter_arrow` with the literal's label fails *and* the expected type is `Tarrow`. In modern ReScript the function literal's type is fully inferred from its own args first, then unified — that path emits `Expr_type_clash`, not `Abstract_wrong_label`. Several attempted reproductions all surfaced as `Expr_type_clash`. Treating as effectively dead. | +| `Scoping_let_module` | ✓ | `scoping_let_module.res` | | +| `Not_a_variant_type` | ✓ | `variant_spread_pattern_not_a_variant.res` | Pattern-level variant spread of a non-variant type. | +| `Incoherent_label_order` | ⚠ | — | typecore.ml:3894. Reached only after `arity_ok` is true *and* the label is present in `ty_fun` but not at the current arrow position. ReScript's labeled-argument reordering happens earlier in `type_args` / `type_unknown_args`, so by the time we hit this branch the label is already at the right position. Every attempted reproduction landed on `Apply_wrong_label` or `Expr_type_clash`. | +| `Less_general` | ✓ | `less_general_universal.res` | | +| `Modules_not_allowed` | ✓ | `super_errors_multi/Modules_not_allowed_toplevel` | Toplevel `let module(M) = …` pattern with `allow_modules=false`. | +| `Cannot_infer_signature` | ✓ | `cannot_infer_signature.res` | | +| `Not_a_packed_module` | ✓ | `not_a_packed_module.res` | | +| `Recursive_local_constraint` | ⚠ | — | typecore.ml:369. Routed via `Unification_recursive_abbrev` in `ctype.ml`, which is raised only when `ctype.ml`'s `Recursive_abbrev` exception fires. **`Recursive_abbrev` is defined (ctype.ml:110, ctype.mli:61) but never raised anywhere in `compiler/`.** Confirmed dead. | +| `Unexpected_existential` | ✓ | `super_errors_multi/Unexpected_existential_in_let` | Destructuring GADT constructor with existential in toplevel `let`. | +| `Unqualified_gadt_pattern` | ✓ | `super_errors_multi/Cross_gadt_pattern` | Only reachable via cross-module GADT disambiguation; in single-file matching the constructor would resolve before this check. | +| `Invalid_interval` | ⚠ | — | typecore.ml:1349. Triggered by `Ppat_interval` pattern. **Verified: `Ppat_interval` has no construction site in `compiler/syntax/src/res_core.ml`** — only printer and ast_debugger handle it. | +| `Invalid_for_loop_index` | ✓ | `invalid_for_loop_index.res` | | +| `Invalid_for_of_pattern` | ⚠ | — | typecore.ml:3120/3152. Verified: parser `normalize_for_of_pattern` (`res_core.ml:3841`) replaces non-var / non-`_` patterns with `Ppat_any` before the typer sees them. | +| `No_value_clauses` | ✓ | `no_value_clauses.res` | | +| `Exception_pattern_below_toplevel` | ✓ | `exception_pattern_below_toplevel.res` | | +| `Inlined_record_escape` | ✓ | `inline_record_escape.res` | | +| `Inlined_record_expected` | ✓ | `inlined_record_expected.res`, `super_errors_multi/Cross_inline_record_constructor` | | +| `Invalid_extension_constructor_payload` | ✓ | `invalid_extension_constructor_payload.res` | | +| `Not_an_extension_constructor` | ✓ | `not_an_extension_constructor.res` | | +| `Break_outside_loop` | ✓ | `break_outside_loop.res`, `break_in_nested_function.res` | | +| `Continue_outside_loop` | ✓ | `continue_outside_loop.res`, `continue_in_nested_function.res` | | +| `Literal_overflow` | ✓ | `intoverflow.res` | | +| `Unknown_literal` | ✓ | `unknown_literal.res` | | +| `Illegal_letrec_pat` | ✓ | `illegal_letrec_pat.res` | | +| `Empty_record_literal` | ✓ | `empty_record_literal.res` | | +| `Uncurried_arity_mismatch` | ✓ | `arity_mismatch3.res` etc. | | +| `Field_not_optional` | ✓ | `fieldNotOptional.res` | | +| `Type_params_not_supported` | ✓ | `variant_spread_pattern_type_params.res` | Pattern-level variant spread (`| ...a as v`) where `a` has type params; typedecl path covered by `variant_spread_type_parameters.res`. | +| `Field_access_on_dict_type` | ✓ | `field_access_on_dict_type.res` | | +| `Jsx_not_enabled` | ☐ (needs harness flag) | — | typecore.ml:218/3470. Fires when JSX is used without `-bs-jsx N`. The `super_errors` runner hard-codes `-bs-jsx 4` in `bscFlags`; adding a per-fixture opt-out (e.g. a `.opts` sidecar) would expose this. Until then, it's reachable in real code but blocked at the harness level. | + +--- + +## `compiler/ml/typedecl.ml` + +Type-declaration errors. Source: [typedecl.ml:27](../compiler/ml/typedecl.ml). + +| Variant | Status | Fixture | Notes | +|---|---|---|---| +| `Repeated_parameter` | ✓ | `repeated_type_parameter.res` | | +| `Duplicate_constructor` | ✓ | `duplicate_variant_constructor.res` | | +| `Duplicate_label` | ✓ | `duplicate_labels_error.res` | | +| `Object_spread_with_record_field` | ✓ | `object_spread_with_record_field.res` | | +| `Recursive_abbrev` | ✓ | `recursive_type_abbreviation.res`, `recursive_type.res` | | +| `Cycle_in_def` | ✓ | `recursive_type_abbrev_cycle.res` | | +| `Definition_mismatch` | ✓ | `definition_mismatch.res` | | +| `Constraint_failed` | ✓ | `constraint_failed.res` | | +| `Inconsistent_constraint` | ✓ | `inconsistent_constraint.res` | | +| `Type_clash` | ⚠ | — | typedecl.ml:125. Fires when `Ctype.unify env (newconstr path params) manifest` fails inside `update_type` for a `type rec` block. For ReScript types this unify either trivially succeeds (aliases unify with their manifest because the cycle/arity machinery has already accepted the shape) or the declaration is rejected earlier by `Cycle_in_def` / `Recursive_abbrev`. I couldn't construct a recursive shape that reaches the failing unify without being caught first. | +| `Parameters_differ` | ⚠ | — | typedecl.ml:988. Fires for non-uniform recursive type *abbreviations* (`type rec t<'a> = … t …`). ReScript treats variant types as having a manifest of None, so `check_regular` is a no-op for them. For abbreviations, `Cycle_in_def` fires first because the recursive reference is direct. I couldn't construct an abbreviation shape that hits Parameters_differ without being cyclic. | +| `Null_arity_external` | ⚠ | — | typedecl.ml:1900. The guard requires `prim_arity = 0` and `prim_native_name` not having the magic 20-byte encoding (`\132\149...`) and `prim_name` not starting with `%` or `#`. The encoding gets applied to every concrete external by `Primitive.parse_declaration`, and empty `prim_name` is rejected earlier by `external_ffi_types.ml` with "Not a valid global name". No path through the parser reaches it. | +| `Unbound_type_var` | ✓ | `unbound_type_var.res` | | +| `Cannot_extend_private_type` | ✓ | `cannot_extend_private_type.res` | | +| `Not_extensible_type` | ✓ | `not_extensible_type.res` | | +| `Extension_mismatch` | ✓ | `extension_arity_mismatch.res` | `type t<'a> = ..` extended with `type t += A(int)` — arity differs from the extensible type. | +| `Rebind_wrong_type` | ⚠ | — | typedecl.ml:1653. The unify is `cstr_res` (source constructor's result, freshly instantiated) against `res` (extension's target type with fresh param vars). For non-GADT sources both sides are `t` and trivially unify; for GADT-style sources (`type t<'a> += A: t`) `cstr_res = t` against `res = t` still unifies (`v1 := int`). The parser doesn't allow rebinding with explicit args (`exception B(string) = A` is rejected at `res_core.ml:6660`), so the result-type relationship is always compatible by construction. | +| `Rebind_mismatch` | ✓ | `extension_rebind_mismatch.res` | Rebinding constructor into a different extensible type. | +| `Rebind_private` | ✓ | `extension_rebind_private.res` | Rebinding a private extension constructor as public. | +| `Bad_variance` | ✓ | `bad_variance.res`, `bad_variance_contra.res` | | +| `Unavailable_type_constructor` | ☐ (needs build harness) | — | typedecl.ml:778. Requires a type path findable at parse time but missing during constraint enforcement; only cross-unit scenarios where a `.cmi` was found but later removed. | +| `Bad_fixed_type` | ⚠ | — | typedecl.ml:190/193. `set_fixed_row` runs only when `is_fixed_type` returns true, which requires a `private` abstract type with a syntactically open object / polyvariant manifest (typedecl.ml:160-174). For a manifest written that way, `expand_head` returns exactly the same `Tobject` / `Tvariant`, so the check at line 190 passes and the row variable check at line 193 also passes (rows from those syntactic forms have a Tvar `row_more`). No alias chain in ReScript syntax can collapse the open row while still passing `has_row_var` on the syntactic side. | +| `Unbound_type_var_ext` | ✓ | `unbound_type_var_extension.res` | | +| `Varying_anonymous` | ⚠ | — | typedecl.ml:1263. Fires in variance computation when an anonymous (`_`) type parameter is constrained against other params under specific variance requirements. ReScript's parser doesn't produce `_` in type parameter position for `type` declarations (`type t<_>` is rejected) — only explicit `'x`-style params, which are never "anonymous" in the sense `Varying_anonymous` checks. | +| `Val_in_structure` | ⚠ | — | typedecl.ml:1887 requires `pval_prim = []` outside a signature. The parser's `external` recovery sets `prim = []` (`res_core.ml:6617`) but only after emitting a `Syntax error`, so the typechecker never reaches the value declaration. From plain source there's no path that produces a non-signature `Val` with empty `pval_prim` — only PPX-rewritten AST could, and the AST shape would have to bypass the parser. | +| `Invalid_attribute` | ✓ | `invalid_attribute_not_undefined.res` | | +| `Bad_immediate_attribute` | ✓ | `bad_immediate_attribute.res` | | +| `Bad_unboxed_attribute` | ✓ | `bad_unboxed_attribute_abstract.res`, `bad_unboxed_attribute_mutable.res`, `bad_unboxed_attribute_many_fields.res`, `bad_unboxed_attribute_extensible.res` | All 4 sub-cases covered. | +| `Boxed_and_unboxed` | ✓ | `boxed_and_unboxed.res` | | +| `Nonrec_gadt` | ✓ | `nonrec_gadt.res` | | +| `Variant_runtime_representation_mismatch` | ✓ | `variant_coercion_*.res` (many) | | +| `Variant_spread_fail` | ✓ | `variant_spread_*.res` (many), `variant_spread_non_variant.res` | | + +--- + +## `compiler/ml/typemod.ml` + +Module-level errors. Source: [typemod.ml:24](../compiler/ml/typemod.ml). + +| Variant | Status | Fixture | Notes | +|---|---|---|---| +| `Cannot_apply` | ✓ | `cannot_apply_non_functor.res` | | +| `Not_included` | ✓ | All `super_errors_multi/Iface_*` fixtures wrap to this via `compunit`. | | +| `Cannot_eliminate_dependency` | ⚠ | — | typemod.ml:1335. Reached only when `Mtype.nondep_supertype` raises `Not_found` for an anonymous functor application. ReScript's `nondep_supertype` falls back to existential abstraction for any module-typed binding it can't eliminate cleanly, so the `Not_found` branch never fires. Multiple anonymous functor applications (including ones where the result genuinely references the argument's abstract type) all type-check. | +| `Signature_expected` | ✓ | `typemod_signature_expected.res` | `with type M.t = …` where `M` is functor-typed inside the outer signature. | +| `Structure_expected` | ✓ | `super_errors_multi/Smoke_unbound_module_reference` (indirect); also `open_functor.res` | | +| `With_no_component` | ✓ | `with_no_component.res` | | +| `With_mismatch` | ✓ | `with_mismatch.res` | | +| `With_makes_applicative_functor_ill_typed` | ⚠ | — | typemod.ml:258. Reached only through the applicative-functor path of `Btype.it_path` (`Papply`); ReScript's parser doesn't emit `Papply` (no parsed construction site in `res_core.ml`), so the iterator never visits this branch. | +| `With_changes_module_alias` | ☐ (needs build harness) | — | typemod.ml:240. Fires during `with module := M2` substitution when an aliased sub-module inside the constrained signature is affected. ReScript parses `with module N := M2` (destructive substitution), but constructing a sub-module alias chain that gets invalidated requires multiple `.resi` files and a specific shape I couldn't reproduce single-file. | +| `With_cannot_remove_constrained_type` | ⚠ | — | typemod.ml:443. Fires for `Twith_typesubst` (the `:=` form) when `params_are_constrained` returns true — i.e. the substitution's params are non-`Tvar`. The parser only accepts `'x`-style identifiers in `with type X<…>` param positions (`res_core.ml` rejects `with type x := …` with "Type params start with a singlequote"), so the params are always fresh `Tvar`s and the check never triggers. | +| `Repeated_name` | ✓ | `repeated_def_*.res` (multiple) | | +| `Non_generalizable` | ✓ | `non_generalizable.res` | | +| `Non_generalizable_module` | ✓ | `non_generalizable_module.res` | Nested module containing `let r = ref(None)` — the outer module's `md_type` carries the free `'_weak1` from the inner ref, so `closed_modtype` returns false and the `Sig_module` branch fires. | +| `Interface_not_compiled` | ✓ | `super_errors_multi/Iface_not_compiled` | | +| `Not_allowed_in_functor_body` | ✓ | `super_errors_multi/not_allowed_in_functor_body` (TODO: confirm path) | | +| `Not_a_packed_module` | ✓ | `not_a_packed_module.res` | | +| `Incomplete_packed_module` | ✓ | `incomplete_packed_module.res` | | +| `Scoping_pack` | ⚠ | — | typemod.ml:1717. Requires first-class module pack where a constraint type has a level mismatch; very contrived. | +| `Recursive_module_require_explicit_type` | ✓ | `recursive_module_require_explicit_type.res` | | +| `Apply_generative` | ✓ | `apply_generative.res` | | +| `Cannot_scrape_alias` | ☐ (needs build harness) | — | typemod.ml:77, 83, 1347. Requires `Env.scrape_alias` to return `Mty_alias` for an alias whose target `.cmi` couldn't be loaded. The `super_errors_multi` runner pre-compiles every file in the fixture, so the alias target is always present. | + +--- + +## `compiler/ml/typetexp.ml` + +Type-expression errors. Source: [typetexp.ml:28](../compiler/ml/typetexp.ml). + +| Variant | Status | Fixture | Notes | +|---|---|---|---| +| `Unbound_type_variable` | ✓ | (covered indirectly via many fixtures) | | +| `Unbound_type_constructor` | ✓ | `typetexp_unbound_type_constructor.res` | | +| `Unbound_type_constructor_2` | ⚠ | — | typetexp.ml:475/619. Reached in two object/polyvariant-inherit code paths when the inherited type is `Tconstr p` and after `expand_head` is still `Tvar` (the body of `p`'s declaration is a bare type variable). ReScript's parser doesn't accept `type t = 'a` at the top level (only via `with type t<'a> = 'a` which doesn't apply here), so the lookup never returns a Tvar-bodied Tconstr. Every reproduction lands on `Not_an_object` or `Not_a_variant`. | +| `Type_arity_mismatch` | ✓ | `type_arity_mismatch.res` | | +| `Type_mismatch` | ✓ | `typetexp_type_mismatch.res` | Type-constructor application that violates a `constraint 'a = …` on the declaration. | +| `Alias_type_mismatch` | ✓ | `typetexp_alias_type_mismatch.res` | | +| `Present_has_conjunction` | ✓ | `polyvariant_present_has_conjunction.res` | `[< #A(int) & (string) > #A]` — `<` syntax marks `#A` as a "present" tag, and the body has both `(int)` and `& (string)` types, so the conjunctive payload triggers the check at line 451. | +| `Present_has_no_type` | ✓ | `polyvariant_present_has_no_type.res` | `[< #B > #A]` — `#A` is listed as a "present" tag but isn't defined in the polyvariant body. | +| `Constructor_mismatch` | ✓ | `polyvariant_constructor_mismatch.res` | | +| `Not_a_variant` | ✓ | `typetexp_not_a_variant.res` | Polyvariant `[#X \| a]` where `a` is not a polyvariant. | +| `Variant_tags` | ⚠ | — | typetexp.ml:39. Raised at typecore.ml:342, 349, 367 via `Tags` exception from `ctype.ml`. **Verified: `exception Tags` is defined (ctype.ml:60) but never raised in `compiler/`.** Confirmed dead. | +| `Invalid_variable_name` | ✓ | `invalid_type_variable_name.res` | | +| `Cannot_quantify` | ✓ | `cannot_quantify.res` | `type t = {f: 'a. (int as 'a) => int}` — `'a` is universally quantified but the alias `int as 'a` rebinds it to `int`, so the proxy is no longer a fresh `Tvar` when the quantification check runs. | +| `Multiple_constraints_on_type` | ✓ | `multiple_constraints_on_type.res` | | +| `Method_mismatch` | ✓ | `object_method_mismatch.res` | | +| `Unbound_value` | ✓ | `typetexp_unbound_value.res` | | +| `Unbound_constructor` | ✓ | `typetexp_unbound_constructor.res` | | +| `Unbound_label` | ✓ | `typetexp_unbound_label.res` | | +| `Unbound_module` | ✓ | `suggest_module_for_missing_identifier.res`, `super_errors_multi/Smoke_unbound_module_reference` | | +| `Unbound_modtype` | ✓ | `typetexp_unbound_modtype.res` | | +| `Ill_typed_functor_application` | ⚠ | — | typetexp.ml:102. In the `Longident.Lapply` branch. **Verified: parser has no construction site for `Longident.Lapply`** (no result in `res_core.ml`). Confirmed dead. | +| `Illegal_reference_to_recursive_module` | ✓ | `illegal_recursive_module_reference.res` | `module rec A: B.S = …` references another recmodule's module-type before signatures are sealed. During `approx_modtype` of A, `Env.lookup_module B` returns the `#recmod#` placeholder and raises `Env.Recmodule`. | +| `Access_functor_as_structure` | ✓ | `access_functor_as_structure.res` | | +| `Apply_structure_as_functor` | ⚠ | — | typetexp.ml:93. In the `Longident.Lapply` branch. Same dead reason as `Ill_typed_functor_application`. | +| `Cannot_scrape_alias` | ☐ (needs build harness) | — | typetexp.ml:86 (Ldot path, live), 95/101 (Lapply path, dead since `Lapply` isn't parsed). The live Ldot trigger needs `Env.scrape_alias` to return `Mty_alias` — an alias whose target `.cmi` couldn't be loaded. The `super_errors_multi` harness pre-compiles every alias target. | +| `Opened_object` | ✓ | `object_inherit_opened.res` | | +| `Not_an_object` | ✓ | `object_inherit_not_an_object.res` | | + +--- + +## `compiler/ml/includemod.ml` (symptom) + +Wrapper symptoms attached to inclusion failures. Source: [includemod.ml:23](../compiler/ml/includemod.ml). + +| Variant | Status | Fixture | Notes | +|---|---|---|---| +| `Missing_field` | ✓ | `super_errors_multi/Iface_missing_value` | | +| `Value_descriptions` | ✓ | `super_errors_multi/Iface_value_descriptions`, `super_errors_multi/Smoke_interface_mismatch` | | +| `Type_declarations` | ✓ | `super_errors_multi/Iface_type_decl_record`, `super_errors_multi/Iface_type_decl_variant`, `RecordInclusion.res` | | +| `Extension_constructors` | ✓ | `super_errors_multi/Iface_extension_constructors` | | +| `Module_types` | ✓ | `super_errors_multi/Iface_module_types` | | +| `Modtype_infos` | ✓ | `super_errors_multi/Iface_modtype_infos` | | +| `Modtype_permutation` | ✓ | `super_errors_multi/include_modtype_permutation` | | +| `Interface_mismatch` | ✓ | wrapper added to all `Iface_*` failures (line 476). | | +| `Unbound_modtype_path` | ⚠ | — | includemod.ml:94. Fires inside `modtype_path` comparison when `Env.find_modtype` raises `Not_found`. The only callers run after both signatures have been fully typed, so the module-type path is always findable from the local env — `Not_found` would imply a stale `.cmi`, which the multi-file harness can't produce since it always pre-compiles. Treating as dead from the source-only harnesses' point of view. | +| `Unbound_module_path` | ☐ (needs build harness) | — | includemod.ml:226/233. Alias comparison where `Env.normalize_path` raises `Not_found`. Requires a module alias whose target `.cmi` is absent at inclusion time — multi-unit only. | +| `Invalid_module_alias` | ☐ (needs build harness) | — | includemod.ml:211. Requires both sides `Mty_alias` with one pointing to a functor argument. Reachable only when the alias chain crosses a functor application that the `super_errors_multi` harness doesn't construct. | + +--- + +## `compiler/ml/includecore.ml` (`type_mismatch`) + +Sub-symptoms produced during signature inclusion (rendered inside `Type_declarations`). +Source: [includecore.ml:159](../compiler/ml/includecore.ml). + +| Variant | Status | Fixture | Notes | +|---|---|---|---| +| `Arity` | ✓ | `definition_mismatch.res` | | +| `Privacy` | ✓ | `super_errors_multi/Iface_privacy_mismatch` | | +| `Kind` | ✓ | `super_errors_multi/Iface_kind_mismatch` | Record-in-impl vs variant-in-interface. | +| `Constraint` | ✓ | `super_errors_multi/Iface_constraint_mismatch` | Implementation adds a `constraint 'a = …`; interface has none. | +| `Manifest` | ✓ | `super_errors_multi/Iface_manifest_mismatch` | Manifest types differ (`int` vs `string`). | +| `Variance` | ✓ | `super_errors_multi/Iface_variance_mismatch` | Interface annotates `+'a`; implementation's inferred variance differs. | +| `Field_type` | ✓ | `super_errors_multi/Iface_type_decl_record` | | +| `Field_mutable` | ✓ | `super_errors_multi/Iface_field_mutable_mismatch` | | +| `Field_optional` | ✓ | `super_errors_multi/Iface_field_optional_mismatch` | | +| `Field_arity` | ✓ | `super_errors_multi/Iface_field_arity_mismatch` | Constructor with different argument count between `.resi` / `.res`. | +| `Field_names` | ✓ | `super_errors_multi/Iface_field_names_mismatch` | Record field names differ at the same position. | +| `Field_missing` | ✓ | `super_errors_multi/Iface_missing_value` (indirect) | | +| `Record_representation` | ✓ | `super_errors_multi/Iface_record_representation_mismatch` | Interface declares `@unboxed`; implementation is boxed. | +| `Unboxed_representation` | ✓ | `super_errors_multi/Iface_unboxed_variant_mismatch` | | +| `Immediate` | ✓ | `super_errors_multi/Iface_immediate_mismatch` | Interface adds `@immediate`; implementation manifests a non-immediate (`string`). | +| `Tag_name` | ✓ | `super_errors_multi/Iface_tag_name_mismatch` | | +| `Variant_representation` | ✓ | `super_errors_multi/Iface_variant_representation_mismatch` | | + +--- + +## `compiler/frontend/bs_syntaxerr.ml` + +FFI / attribute / experimental-feature errors. Source: [bs_syntaxerr.ml:27](../compiler/frontend/bs_syntaxerr.ml). + +| Variant | Status | Fixture | Notes | +|---|---|---|---| +| `Unsupported_predicates` | ✓ | `bs_unsupported_predicates.res` | `@get({weird: true})` on object type field. | +| `Conflict_bs_bs_this_bs_meth` | ⚠ | — | bs_syntaxerr.ml:29 declares the variant but `Bs_syntaxerr.err _ Conflict_bs_bs_this_bs_meth` is **never raised** anywhere in `compiler/`. | +| `Duplicated_bs_deriving` | ✓ | `duplicated_bs_deriving.res` | | +| `Conflict_attributes` | ✓ | `bs_conflict_attributes.res` | | +| `Expect_int_literal` | ✓ | `bs_expect_int_literal.res` | | +| `Expect_string_literal` | ✓ | `bs_expect_string_literal.res` | | +| `Expect_int_or_string_or_json_literal` | ✓ | `bs_expect_int_or_string_or_json_literal.res` | `@as(true)` on a wildcard external argument. | +| `Unhandled_poly_type` | ⚠ | — | ast_core_type.ml:141. Reached only when an external's arrow chain contains `Ptyp_poly` inline. The parser's `parse_poly_type_expr` only emits `Ptyp_poly` for record field types and explicit `let f: type t. …` annotations; inside arrow chains, the `'a.` is misread as the deprecated `(. …)` uncurried syntax (`res_core.ml` lexer). Inline polytypes in an external's arrow can only come from PPX-rewritten AST. | +| `Invalid_underscore_type_in_external` | ✓ | `bs_invalid_underscore_type_in_external.res` | `@obj external make: (~x: _) => _ = ""` — `_` at an optional-label position without `@as`. | +| `Invalid_bs_string_type` | ✓ | `bs_invalid_bs_string_type.res` | | +| `Invalid_bs_int_type` | ✓ | `bs_invalid_bs_int_type.res` | | +| `Invalid_bs_unwrap_type` | ✓ | `bs_invalid_bs_unwrap_type.res` | | +| `Conflict_ffi_attribute` | ✓ | `conflicting_ffi_attributes.res` | | +| `Illegal_attribute` | ✓ | `bs_illegal_attribute_scope.res` | | +| `Not_supported_directive_in_bs_return` | ✓ | `bs_not_supported_directive_in_bs_return.res` | | +| `Expect_opt_in_bs_return_to_opt` | ✓ | `bs_expect_opt_in_bs_return_to_opt.res` | | +| `Misplaced_label_syntax` | ⚠ | — | bs_syntaxerr.ml:116. Only fires from `check_and_discard` in `ast_exp_apply.ml:49`, applied to the args of `->`, `#=`, `##` operators. The parser always emits those args as `Nolabel`. | +| `Optional_in_uncurried_bs_attribute` | ✓ | `bs_optional_in_uncurried_bs_attribute.res` | `@this` function with optional argument. | +| `Bs_this_simple_pattern` | ✓ | `bs_this_simple_pattern.res` | `@this` with destructured self pattern. | +| `Experimental_feature_not_enabled` | ✓ | `let_unwrap_on_top_level_not_enabled.res` (and other let-unwrap variants) | Currently only `LetUnwrap` is checked. | +| `LetUnwrap_not_supported_in_position` | ✓ | `let_unwrap_on_top_level.res`, `let_unwrap_on_not_supported_variant.res` | | + +--- + +## `compiler/ml/ast_untagged_variants.ml` + +Untagged-variant validation errors. Source: [ast_untagged_variants.ml:52](../compiler/ml/ast_untagged_variants.ml). + +### `untagged_error` + +| Variant | Status | Fixture | Notes | +|---|---|---|---| +| `OnlyOneUnknown` | ✓ | `UntaggedOnlyOneUnknown.res` | Abstract payload alongside another payload-carrying case. | +| `AtMostOneObject` | ✓ | `UntaggedAtMostOneObject.res` | Two record payloads in the same untagged variant. | +| `AtMostOneInstance` | ✓ | `UntaggedAtMostOneInstance.res` | Two cases with the same JS instance type (`Date.t` in both). | +| `AtMostOneFunction` | ✓ | `UntaggedAtMostOneFunction.res` | Two function-typed payloads. | +| `AtMostOneString` | ✓ | `UntaggedNonUnary*.res` (some sub-cases) | | +| `AtMostOneNumber` | ✓ | `UntaggedAtMostOneNumber.res` | `int` and `float` payloads collide on the number runtime check. | +| `AtMostOneBigint` | ✓ | `UntaggedAtMostOneBigint.res` | Two bigint payloads. | +| `AtMostOneBoolean` | ✓ | `UntaggedAtMostOneBoolean.res` | Two boolean payloads. | +| `DuplicateLiteral` | ✓ | `UntaggedDuplicateLiteral.res` | `@as("x")` on two different constructors. | +| `ConstructorMoreThanOneArg` | ✓ | `UntaggedConstructorMoreThanOneArg.res` | `A(int, int)` payload in an untagged variant. | + +### `error` + +| Variant | Status | Fixture | Notes | +|---|---|---|---| +| `InvalidVariantAsAnnotation` | ✓ | `UntaggedInvalidVariantAsAnnotation.res` | `@as(foo)` with a non-`null` / non-`undefined` identifier payload. | +| `Duplicated_bs_as` | ✓ | `UntaggedDuplicatedBsAs.res` | Two `@as("...")` attributes on the same constructor. | +| `InvalidVariantTagAnnotation` | ✓ | `UntaggedInvalidVariantTagAnnotation.res` | `@tag(123)` (non-string payload). | +| `InvalidUntaggedVariantDefinition` | ✓ | `UntaggedUnknown.res`, `UntaggedNonUnary*.res`, `UntaggedTupleAndArray.res`, `UntaggedImplIntf.res`, etc. | | +| `TagFieldNameConflict` | ✓ | `UntaggedTagFieldNameConflict.res` | `@tag("kind")` plus inline record field named `kind` on a constructor. | + +--- + +## `compiler/depends/bs_exception.ml` + +Build / dependency errors. Mostly need the `rescript build` runtime to fire — not reachable from raw `bsc`. Source: [bs_exception.ml:25](../compiler/depends/bs_exception.ml). + +| Variant | Status | Fixture | Notes | +|---|---|---|---| +| `Cmj_not_found` | ☐ (needs build harness) | — | Missing `.cmj` from a dependent module. Reachable from `rescript build` but not from raw `bsc`. | +| `Js_not_found` | ✓ | implicitly — bypassed via `-bs-cmi-only` in `super_errors_multi` runner. Not a fixture, but the harness commit documents the workaround. | | +| `Bs_cyclic_depends` | ☐ (needs build harness) | — | Cycle across compilation units; the dependency graph that detects this is owned by `rewatch` / `bsb`, not raw `bsc`. | +| `Bs_duplicated_module` | ☐ (needs build harness) | — | Same module name in two source paths under a single package. | +| `Bs_duplicate_exports` | ☐ (needs build harness) | — | Same export emitted twice across compilation units. | +| `Bs_package_not_found` | ☐ (needs build harness) | — | `rescript.json`-referenced package not resolvable. | +| `Bs_main_not_exist` | ☐ (needs build harness) | — | `rescript.json` `main` entry missing. | +| `Bs_invalid_path` | ☐ (needs build harness) | — | `-I` / source path with invalid form. | +| `Missing_ml_dependency` | ☐ (needs build harness) | — | Compile-time missing dependency from a `.cmj` lookup table. | +| `Dependency_script_module_dependent_not` | ☐ (needs build harness) | — | `js_name_of_module_id.cppo.ml:122`. **Reachable** when a dependent module is in script mode (`Package_script`) but the current module is in package mode (`Package_found _`). Legacy script-vs-package interaction; needs `rescript.json` harness. | + +--- + +## `compiler/ml/env.ml` + +Environment / `.cmi`-consistency errors. Source: [env.ml:57](../compiler/ml/env.ml). + +| Variant | Status | Fixture | Notes | +|---|---|---|---| +| `Illegal_renaming` | ☐ (needs build harness) | — | Triggered when a `.cmi` filename and the module name inside it disagree. Reachable via `rescript.json` setups that rename the produced artefact, but not from a single-process `bsc` invocation that always writes `Module.cmi` to match the source. | +| `Inconsistent_import` | ☐ (needs build harness) | — | Triggered when two `.cmi` files transitively imported by the same unit declare different CRCs for the same type. Needs an artificially-mutated build state across multiple compile invocations. | +| `Missing_module` | ☐ (needs build harness) | — | `.cmi` referenced but absent from `-I` paths at compile time. The `super_errors_multi` runner pre-compiles every fixture file via `-bs-read-cmi`, so it never reaches this code path. | +| `Illegal_value_name` | ⚠ | — | env.ml:1622/1625 raises when an identifier is `"->"` or starts/contains `#`. The ReScript parser never emits such identifiers; only PPX-rewritten AST could reach the check. | + +--- + +## `compiler/ml/cmi_format.ml` + +`.cmi` file format errors. Need binary-level manipulation to trigger +(write a non-`.cmi` file in place of one and reference it; downgrade +compiler version; truncate the file). Out of scope for the +`super_errors{,_multi}` harnesses, which only invoke `bsc` on +hand-written `.res` / `.resi` sources. + +| Variant | Status | Fixture | Notes | +|---|---|---|---| +| `Not_an_interface` | ☐ (needs binary harness) | — | Pass an arbitrary file as `.cmi`. | +| `Wrong_version_interface` | ☐ (needs binary harness) | — | Mismatched compiler versions writing/reading. | +| `Corrupted_interface` | ☐ (needs binary harness) | — | Truncated or corrupted `.cmi`. | + +--- + +## `compiler/core/cmd_ast_exception.ml` + +PPX-runtime errors. Source: [cmd_ast_exception.ml:24](../compiler/core/cmd_ast_exception.ml). +Both require running `bsc` with `-ppx ` and exercising the +external process boundary. Not reachable from the single-file or +multi-file harnesses, which never set `-ppx`. + +| Variant | Status | Fixture | Notes | +|---|---|---|---| +| `CannotRun` | ☐ (needs PPX harness) | — | PPX binary fails to execute (missing or non-executable). | +| `WrongMagic` | ☐ (needs PPX harness) | — | PPX returns wrong AST magic number (e.g. PPX built against a different compiler ABI). | + +--- + +## Single-variant modules + +| Module | Variant | Status | Fixture | Notes | +|---|---|---|---|---| +| `compiler/ml/translcore.ml` | `Unknown_builtin_primitive` | ✓ | `unknown_builtin_primitive.res` | | +| `compiler/ml/translmod.ml` | `Fragile_pattern_in_toplevel` | ✓ | `fragile_pattern_toplevel.res` | | +| `compiler/ml/transl_recmodule.ml` | `Circular_dependency` | ✓ | `recmodule_circular_dependency.res` | | +| `compiler/ml/rec_check.ml` | `Illegal_letrec_expr` | ✓ | `illegal_letrec_expr.res` | | +| `compiler/ml/syntaxerr.ml` | `Variable_in_scope` | ⚠ | — | Reachable via `let f: type t. (t, 't) => t = …` (locally-abstract `t` collides with type variable `'t` during `varify_constructors`), but `Syntaxerr.error` has no registered pretty-printer, so it propagates as an uncaught `Fatal error: exception Syntaxerr.Error(_)`. The variant is live; the printer is dead. Treat as broken until either the printer is wired up or the variant is removed in favor of a proper diagnostic. | +| `compiler/ml/cmt_format.cppo.ml` | `Not_a_typedtree` | ☐ (needs binary harness) | — | cmt_format.cppo.ml:147. Fires when a tool reads a `.cmt` file whose first block isn't a typed tree. Reachable in principle by pointing the analyzer at an arbitrary file with a `.cmt` extension; out of scope for the source-only fixture harnesses. | +| `compiler/ext/bsc_args.ml` | `Unknown` | ☐ (needs CLI harness) | — | bsc_args.ml:45. Reachable trivially via `bsc --bogus`, but the `super_errors{,_multi}` runners only pass `bsc` a fixed flag list plus the source file — they can't exercise CLI-level errors. | +| `compiler/ext/bsc_args.ml` | `Missing` | ☐ (needs CLI harness) | — | Same as above: `bsc -o` (no following filename). Needs a harness that invokes `bsc` with crafted argv. | + +--- + +## `compiler/frontend/ast_utf8_string.ml` (dead family) + +Source: [ast_utf8_string.ml:25](../compiler/frontend/ast_utf8_string.ml). All variants here are reached only via the legacy `{j|…|j}` delimiter, which the modern ReScript parser doesn't emit. Backtick template strings skip the transform entirely. + +| Variant | Status | +|---|---| +| `Invalid_code_point` | ⚠ Dead | +| `Unterminated_backslash` | ⚠ Dead | +| `Invalid_hex_escape` | ⚠ Dead | +| `Invalid_unicode_escape` | ⚠ Dead | +| `Invalid_unicode_codepoint_escape` | ⚠ Dead | + +## `compiler/frontend/ast_utf8_string_interp.ml` (dead family) + +Source: [ast_utf8_string_interp.ml:25](../compiler/frontend/ast_utf8_string_interp.ml). + +`pos_error` is reached only through `check_and_transform`, whose only +caller in `compiler/` is `transform_test` — used by OUnit tests, not the +production pipeline. Modern ReScript backtick templates take the +`BackQuotes` branch of `transform_exp` (line 311) and skip the +interpolation parser entirely. The legacy `{j|…|j}` delimiter the +parser would otherwise route here is no longer accepted by the +scanner. All variants below are unreachable from regular ReScript +source. + +| Variant | Status | +|---|---| +| `Invalid_code_point` | ⚠ Dead | +| `Unterminated_backslash` | ⚠ Dead | +| `Invalid_escape_code` | ⚠ Dead | +| `Invalid_hex_escape` | ⚠ Dead | +| `Invalid_unicode_escape` | ⚠ Dead | +| `Unterminated_variable` | ⚠ Dead | +| `Unmatched_paren` | ⚠ Dead | +| `Invalid_syntax_of_var` | ⚠ Dead | + +--- + +## Confirmed dead variants — candidates for removal + +Only variants with a concrete, source-level reason are listed. Each row +has been re-verified against the source as of this audit. Variants marked +`?` in the tables above are **not** included here — those may turn out to +be live and just hard to reproduce. + +**Verified dead by missing raise / construction site:** + +- `typecore.Variant_tags`, `typetexp.Variant_tags` — relayed via the + `Tags` exception which is declared in `ctype.ml:60` / `ctype.mli:57` + but **never raised** in `compiler/`. +- `typecore.Recursive_local_constraint` — relayed via + `Unification_recursive_abbrev`, raised only from the `Recursive_abbrev` + exception which is declared (`ctype.ml:110`, `ctype.mli:61`) but + **never raised**. +- `typecore.Invalid_interval` — needs `Ppat_interval`; **no construction + site** for that AST node in `compiler/syntax/src/`. +- `typecore.Invalid_for_of_pattern` — parser's + `normalize_for_of_pattern` (`res_core.ml:3841`) replaces every non-var, + non-`_` pattern with `Ppat_any` before the typer runs. +- `bs_syntaxerr.Conflict_bs_bs_this_bs_meth` — variant is declared but + no `Bs_syntaxerr.err _ Conflict_bs_bs_this_bs_meth` call exists in + `compiler/`. + +**Verified dead because parser doesn't produce required AST shape:** + +- `typetexp.Ill_typed_functor_application`, + `typetexp.Apply_structure_as_functor` — in the + `Longident.Lapply` branch; `Lapply` has no construction site in + the parser (`res_core.ml`). +- `bs_syntaxerr.Misplaced_label_syntax` — fires for labeled args to + `->`/`#=`/`##` operators; the parser always emits those with + `Nolabel`. +- `typedecl.Null_arity_external` — primitives parsed by + `Primitive.parse_declaration` always get the magic 20-byte + `prim_native_name` encoding, which bypasses the trigger; empty + `prim_name` is rejected earlier with "Not a valid global name". +- `ast_utf8_string.*` (Invalid_code_point, Unterminated_backslash, + Invalid_hex_escape, Invalid_unicode_escape, + Invalid_unicode_codepoint_escape) — the scanner + (`res_scanner.ml:350-417`) already validates escape sequences and + unicode code points; the transform never sees a string that would + fail its own re-validation. +- `ast_utf8_string_interp.*` (the whole module's error variants) — + `check_and_transform` is only ever called from `transform_test`, which + exists for OUnit tests, not the production pipeline. Modern ReScript + backtick templates take the `BackQuotes` branch of `transform_exp` + and skip the interpolation parser entirely. +- `typedecl.Val_in_structure` — typedecl.ml:1887 requires `pval_prim + = []` outside a signature; the parser's `external` recovery sets + `prim = []` only after emitting a syntax error, so the typechecker + never reaches the value declaration. +- `env.Illegal_value_name` — env.ml:1622/1625 rejects `"->"` and + identifiers containing `#`. The parser never produces such names; + PPX-rewritten AST is the only path that could trigger it. +- `bs_warnings.Statement_type` (warning 10) — only caller of + `check_application_result` passes `statement = false`, so the + `if statement then …` branch never fires. +- `bs_warnings.Unerasable_optional_argument` (warning 16) — + `type_function` (typecore.ml:3479) explicitly disables this warning + via `Warnings.parse_options false "-16"` before the check runs. +- `bs_warnings.Bs_uninterpreted_delimiters` (warning 108) — raised at + `bs_warnings.ml:29` for `Pconst_string` with delimiter `"js"`, but + the modern scanner has no `{js|…|js}` form and template strings tag + with `"bq"` after rewriting. + +**`Syntaxerr.Variable_in_scope` is a special case** — reachable from +`let f: type t. (t, 't) => t = …` but raised without a registered +printer, so it surfaces as `Fatal error: exception Syntaxerr.Error(_)`. +The variant is live; the diagnostic path is broken. Fix should either +wire up a printer or convert the check into a regular typed error. + +**Newly verified dead** (the variants the second-pass audit promoted +from `?` to ⚠, with the reason in the table): + +- `typecore.Label_mismatch`, `Abstract_wrong_label`, + `Incoherent_label_order` — defensive `try unify with Unify ->` paths + that are subsumed by `Wrong_name` / `Expr_type_clash` / + `Apply_wrong_label` in modern ReScript. +- `typedecl.Type_clash` — every recursive `type` shape that would reach + the failing `unify` is rejected earlier by `Cycle_in_def` / + `Recursive_abbrev`. +- `typedecl.Parameters_differ` — `check_regular` runs only on + abbreviations, and ReScript's parser produces `Cycle_in_def` for + every recursive abbreviation shape before this check. +- `typedecl.Rebind_wrong_type` — the parser refuses extension rebind + syntax that carries args or a result type, so source and target's + result types always unify trivially. +- `typedecl.Bad_fixed_type` — `is_fixed_type` checks the syntactic + manifest for an open row, and `expand_head` preserves that row; + there's no ReScript syntax that satisfies one check and fails the + other. +- `typedecl.Varying_anonymous` — needs `_` in a `type` parameter + position, which the parser doesn't accept. +- `typetexp.Unbound_type_constructor_2` — needs an inherited type + whose `Tconstr` body is a bare `Tvar`; the parser rejects + `type t = 'a` at top-level. +- `typemod.Cannot_eliminate_dependency` — `Mtype.nondep_supertype` + falls back to existential abstraction in every reachable case; + the `Not_found` branch never fires. +- `typemod.With_cannot_remove_constrained_type` — the parser only + accepts `'x`-style identifiers in `with type` param positions, so + the params are always fresh `Tvar`s and `params_are_constrained` + returns false. +- `bs_syntaxerr.Unhandled_poly_type` — the only way an external's + arrow chain gets an inline `Ptyp_poly` is via PPX-rewritten AST; + the parser misreads `'a.` inline as the deprecated `(. …)` + uncurried syntax. + +--- + +## Warnings + +Warnings are declared in [`compiler/ext/warnings.ml`](../compiler/ext/warnings.ml). +The default warning set is `+a-4-9-20-41-50-102` +([`compiler/ext/bsc_warnings.ml`](../compiler/ext/bsc_warnings.ml)), so +warnings 4, 9, 20, 41, 50, and 102 are disabled by default; the rest are +enabled. Fixtures use `-w +A` (everything on) so default-disabled +warnings still fire. + +Fixtures follow the naming convention `warning__.res` +so coverage gaps stay greppable. + +### Confirmed dead (no `prerr_warning` site in the compiler) + +These warning constructors exist in `warnings.ml` but are never raised +anywhere in `compiler/`. They are candidates for removal. + +| Number | Variant | Reason | +|---|---|---| +| 1 | `Comment_start` | Lexer warning; modern parser doesn't emit. | +| 2 | `Comment_not_end` | Lexer warning; modern parser doesn't emit. | +| 7 | `Method_override` | OCaml class system, not exposed by ReScript. | +| 13 | `Instance_variable_override` | OCaml class system. | +| 14 | `Illegal_backslash` | Lexer-level escape warning; parser doesn't emit. | +| 15 | `Implicit_public_methods` | OCaml class system. | +| 29 | `Eol_in_string` | Lexer-level string warning. | +| 48 | `Eliminated_optional_arguments` | Declared but never raised. | +| 50 | `Bad_docstring` | Declared but never raised; also default-disabled. | +| 105 | `Bs_fragile_external` | Declared but never raised. | +| 106 | `Bs_unimplemented_primitive` | Declared but never raised. | +| 10 | `Statement_type` | Raised at typecore.ml:2052 inside `check_application_result`, but `statement` is always `false` at the only call site (typecore.ml:3983); sequence statements hit `Expr_type_clash` via `type_statement` unifying to `unit`. | +| 16 | `Unerasable_optional_argument` | Raised at typecore.ml:3526, but `type_function` (typecore.ml:3479) explicitly disables this warning before the check runs (`Warnings.parse_options false "-16"`). | +| 108 | `Bs_uninterpreted_delimiters` | Raised at bs_warnings.ml:29 for `Pconst_string` with delimiter `"js"`; the modern scanner has no `{js\|...\|js}` form and template strings don't tag with `"js"`. | + +### Live but no fixture yet + +These warnings have `prerr_warning` raise sites in `compiler/` and are +reachable from regular ReScript code, but no `super_errors` fixture +currently exercises them. + +| Number | Variant | Trigger | +|---|---|---| +| 5 | `Partial_application` | `typecore.ml:2049`, `:3980` — fires from `check_application_result` and a guarded branch in the `ignore` special case. The 3980 branch needs `not total_app`, which would require `ignore(arg, ...)` partial application — syntactically non-sensical. The 2049 site fires via a delayed check whose only path is hard to trigger from plain source. Status: live raise sites but I couldn't construct a reproduction; may be effectively dead. | diff --git a/tests/build_tests/super_errors/expected/UntaggedAtMostOneBigint.res.expected b/tests/build_tests/super_errors/expected/UntaggedAtMostOneBigint.res.expected new file mode 100644 index 0000000000..6575292d23 --- /dev/null +++ b/tests/build_tests/super_errors/expected/UntaggedAtMostOneBigint.res.expected @@ -0,0 +1,9 @@ + + We've found a bug for you! + /.../fixtures/UntaggedAtMostOneBigint.res:2:20-30 + + 1 │ @unboxed + 2 │ type t = A(bigint) | B(bigint) + 3 │ + + This untagged variant definition is invalid: At most one case can be a bigint type. \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/UntaggedAtMostOneBoolean.res.expected b/tests/build_tests/super_errors/expected/UntaggedAtMostOneBoolean.res.expected new file mode 100644 index 0000000000..7458940150 --- /dev/null +++ b/tests/build_tests/super_errors/expected/UntaggedAtMostOneBoolean.res.expected @@ -0,0 +1,9 @@ + + We've found a bug for you! + /.../fixtures/UntaggedAtMostOneBoolean.res:2:18-26 + + 1 │ @unboxed + 2 │ type t = A(bool) | B(bool) + 3 │ + + This untagged variant definition is invalid: At most one case can be a boolean type. \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/UntaggedAtMostOneFunction.res.expected b/tests/build_tests/super_errors/expected/UntaggedAtMostOneFunction.res.expected new file mode 100644 index 0000000000..439f9c17de --- /dev/null +++ b/tests/build_tests/super_errors/expected/UntaggedAtMostOneFunction.res.expected @@ -0,0 +1,9 @@ + + We've found a bug for you! + /.../fixtures/UntaggedAtMostOneFunction.res:2:24-44 + + 1 │ @unboxed + 2 │ type t = A(int => int) | B(string => string) + 3 │ + + This untagged variant definition is invalid: At most one case can be a function type. \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/UntaggedAtMostOneInstance.res.expected b/tests/build_tests/super_errors/expected/UntaggedAtMostOneInstance.res.expected new file mode 100644 index 0000000000..841902fcbd --- /dev/null +++ b/tests/build_tests/super_errors/expected/UntaggedAtMostOneInstance.res.expected @@ -0,0 +1,9 @@ + + We've found a bug for you! + /.../fixtures/UntaggedAtMostOneInstance.res:2:20-30 + + 1 │ @unboxed + 2 │ type t = A(Date.t) | B(Date.t) + 3 │ + + This untagged variant definition is invalid: At most one case can be a Date type. \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/UntaggedAtMostOneNumber.res.expected b/tests/build_tests/super_errors/expected/UntaggedAtMostOneNumber.res.expected new file mode 100644 index 0000000000..cf28363354 --- /dev/null +++ b/tests/build_tests/super_errors/expected/UntaggedAtMostOneNumber.res.expected @@ -0,0 +1,9 @@ + + We've found a bug for you! + /.../fixtures/UntaggedAtMostOneNumber.res:2:17-26 + + 1 │ @unboxed + 2 │ type t = A(int) | B(float) + 3 │ + + This untagged variant definition is invalid: At most one case can be a number type (int or float). \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/UntaggedAtMostOneObject.res.expected b/tests/build_tests/super_errors/expected/UntaggedAtMostOneObject.res.expected new file mode 100644 index 0000000000..c1a32783d8 --- /dev/null +++ b/tests/build_tests/super_errors/expected/UntaggedAtMostOneObject.res.expected @@ -0,0 +1,10 @@ + + We've found a bug for you! + /.../fixtures/UntaggedAtMostOneObject.res:4:15-20 + + 2 │ type b = {y: int} + 3 │ @unboxed + 4 │ type t = A(a) | B(b) + 5 │ + + This untagged variant definition is invalid: At most one case can be an object type. \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/UntaggedConstructorMoreThanOneArg.res.expected b/tests/build_tests/super_errors/expected/UntaggedConstructorMoreThanOneArg.res.expected new file mode 100644 index 0000000000..585544c428 --- /dev/null +++ b/tests/build_tests/super_errors/expected/UntaggedConstructorMoreThanOneArg.res.expected @@ -0,0 +1,9 @@ + + We've found a bug for you! + /.../fixtures/UntaggedConstructorMoreThanOneArg.res:2:1-32 + + 1 │ @unboxed + 2 │ type t = A(int, int) | B(string) + 3 │ + + This untagged variant definition is invalid: Constructor A has more than one argument. \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/UntaggedDuplicateLiteral.res.expected b/tests/build_tests/super_errors/expected/UntaggedDuplicateLiteral.res.expected new file mode 100644 index 0000000000..61bf9dcad3 --- /dev/null +++ b/tests/build_tests/super_errors/expected/UntaggedDuplicateLiteral.res.expected @@ -0,0 +1,9 @@ + + We've found a bug for you! + /.../fixtures/UntaggedDuplicateLiteral.res:2:23-34 + + 1 │ @unboxed + 2 │ type t = | @as("x") A | @as("x") B + 3 │ + + This untagged variant definition is invalid: Duplicate literal x. \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/UntaggedDuplicatedBsAs.res.expected b/tests/build_tests/super_errors/expected/UntaggedDuplicatedBsAs.res.expected new file mode 100644 index 0000000000..c986d7dd34 --- /dev/null +++ b/tests/build_tests/super_errors/expected/UntaggedDuplicatedBsAs.res.expected @@ -0,0 +1,9 @@ + + We've found a bug for you! + /.../fixtures/UntaggedDuplicatedBsAs.res:2:21-23 + + 1 │ @unboxed + 2 │ type t = | @as("x") @as("y") A | B + 3 │ + + duplicate @as \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/UntaggedInvalidVariantAsAnnotation.res.expected b/tests/build_tests/super_errors/expected/UntaggedInvalidVariantAsAnnotation.res.expected new file mode 100644 index 0000000000..4eb46507df --- /dev/null +++ b/tests/build_tests/super_errors/expected/UntaggedInvalidVariantAsAnnotation.res.expected @@ -0,0 +1,8 @@ + + We've found a bug for you! + /.../fixtures/UntaggedInvalidVariantAsAnnotation.res:1:12-14 + + 1 │ type t = | @as(foo) A | B + 2 │ + + A variant case annotation @as(...) must be a string or integer, boolean, null, undefined \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/UntaggedInvalidVariantTagAnnotation.res.expected b/tests/build_tests/super_errors/expected/UntaggedInvalidVariantTagAnnotation.res.expected new file mode 100644 index 0000000000..b81dcaaea8 --- /dev/null +++ b/tests/build_tests/super_errors/expected/UntaggedInvalidVariantTagAnnotation.res.expected @@ -0,0 +1,9 @@ + + We've found a bug for you! + /.../fixtures/UntaggedInvalidVariantTagAnnotation.res:1:1-4 + + 1 │ @tag(123) + 2 │ type t = A({foo: int}) | B({bar: string}) + 3 │ + + A variant tag annotation @tag(...) must be a string \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/UntaggedOnlyOneUnknown.res.expected b/tests/build_tests/super_errors/expected/UntaggedOnlyOneUnknown.res.expected new file mode 100644 index 0000000000..1406dd58d8 --- /dev/null +++ b/tests/build_tests/super_errors/expected/UntaggedOnlyOneUnknown.res.expected @@ -0,0 +1,10 @@ + + We've found a bug for you! + /.../fixtures/UntaggedOnlyOneUnknown.res:4:10-18 + + 2 │ + 3 │ @unboxed + 4 │ type t = A(opaque) | B(int) + 5 │ + + This untagged variant definition is invalid: Case A has a payload that is not of one of the recognized shapes (object, array, etc). Then it must be the only case with payloads. \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/UntaggedTagFieldNameConflict.res.expected b/tests/build_tests/super_errors/expected/UntaggedTagFieldNameConflict.res.expected new file mode 100644 index 0000000000..4a04a0f4fc --- /dev/null +++ b/tests/build_tests/super_errors/expected/UntaggedTagFieldNameConflict.res.expected @@ -0,0 +1,11 @@ + + We've found a bug for you! + /.../fixtures/UntaggedTagFieldNameConflict.res:3:3-18 + + 1 │ @tag("kind") + 2 │ type t = + 3 │ | A({kind: int}) + 4 │ | B({other: string}) + 5 │ + + Constructor "A": the @tag name "kind" conflicts with the runtime value of inline record field "kind". Use a different @tag name or rename the field. \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/bs_expect_int_or_string_or_json_literal.res.expected b/tests/build_tests/super_errors/expected/bs_expect_int_or_string_or_json_literal.res.expected new file mode 100644 index 0000000000..8629a05f44 --- /dev/null +++ b/tests/build_tests/super_errors/expected/bs_expect_int_or_string_or_json_literal.res.expected @@ -0,0 +1,8 @@ + + We've found a bug for you! + /.../fixtures/bs_expect_int_or_string_or_json_literal.res:1:21-23 + + 1 │ @val external foo: (@as(true) _, int) => unit = "foo" + 2 │ + + expect int, string literal or json literal {json|text here|json} \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/bs_invalid_underscore_type_in_external.res.expected b/tests/build_tests/super_errors/expected/bs_invalid_underscore_type_in_external.res.expected new file mode 100644 index 0000000000..12ddf90961 --- /dev/null +++ b/tests/build_tests/super_errors/expected/bs_invalid_underscore_type_in_external.res.expected @@ -0,0 +1,8 @@ + + We've found a bug for you! + /.../fixtures/bs_invalid_underscore_type_in_external.res:1:26 + + 1 │ @obj external make: (~x: _) => _ = "" + 2 │ + + _ is not allowed in combination with external optional type \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/bs_optional_in_uncurried_bs_attribute.res.expected b/tests/build_tests/super_errors/expected/bs_optional_in_uncurried_bs_attribute.res.expected new file mode 100644 index 0000000000..637cbfb278 --- /dev/null +++ b/tests/build_tests/super_errors/expected/bs_optional_in_uncurried_bs_attribute.res.expected @@ -0,0 +1,8 @@ + + We've found a bug for you! + /.../fixtures/bs_optional_in_uncurried_bs_attribute.res:1:15-55 + + 1 │ let f = @this (self, ~x=?) => self + x->Option.getOr(0) + 2 │ + + Uncurried function doesn't support optional arguments yet \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/bs_this_simple_pattern.res.expected b/tests/build_tests/super_errors/expected/bs_this_simple_pattern.res.expected new file mode 100644 index 0000000000..012572aaf2 --- /dev/null +++ b/tests/build_tests/super_errors/expected/bs_this_simple_pattern.res.expected @@ -0,0 +1,8 @@ + + We've found a bug for you! + /.../fixtures/bs_this_simple_pattern.res:1:16-21 + + 1 │ let f = @this ((a, b), x) => a + b + x + 2 │ + + %@this expect its pattern variable to be simple form \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/bs_unsupported_predicates.res.expected b/tests/build_tests/super_errors/expected/bs_unsupported_predicates.res.expected new file mode 100644 index 0000000000..9ea344a934 --- /dev/null +++ b/tests/build_tests/super_errors/expected/bs_unsupported_predicates.res.expected @@ -0,0 +1,8 @@ + + We've found a bug for you! + /.../fixtures/bs_unsupported_predicates.res:1:19-23 + + 1 │ type t = {..@get({weird: true}) "x": int} + 2 │ + + unsupported predicates \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/cannot_quantify.res.expected b/tests/build_tests/super_errors/expected/cannot_quantify.res.expected new file mode 100644 index 0000000000..81b7ada256 --- /dev/null +++ b/tests/build_tests/super_errors/expected/cannot_quantify.res.expected @@ -0,0 +1,8 @@ + + We've found a bug for you! + /.../fixtures/cannot_quantify.res:1:14-35 + + 1 │ type t = {f: 'a. (int as 'a) => int} + 2 │ + + The universal type variable 'a cannot be generalized: it is not a variable. \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/empty_record_literal.res.expected b/tests/build_tests/super_errors/expected/empty_record_literal.res.expected new file mode 100644 index 0000000000..eea4c3efcd --- /dev/null +++ b/tests/build_tests/super_errors/expected/empty_record_literal.res.expected @@ -0,0 +1,8 @@ + + We've found a bug for you! + /.../fixtures/empty_record_literal.res:1:11-12 + + 1 │ let bad = {} + 2 │ + + Empty record literal {} should be type annotated or used in a record context. \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/extension_arity_mismatch.res.expected b/tests/build_tests/super_errors/expected/extension_arity_mismatch.res.expected new file mode 100644 index 0000000000..6c22fed77c --- /dev/null +++ b/tests/build_tests/super_errors/expected/extension_arity_mismatch.res.expected @@ -0,0 +1,11 @@ + + We've found a bug for you! + /.../fixtures/extension_arity_mismatch.res:3:1-16 + + 1 │ type t<'a> = .. + 2 │ + 3 │ type t += A(int) + 4 │ + + This extension does not match the definition of type t + They have different arities. \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/extension_rebind_mismatch.res.expected b/tests/build_tests/super_errors/expected/extension_rebind_mismatch.res.expected new file mode 100644 index 0000000000..9a8be7df8c --- /dev/null +++ b/tests/build_tests/super_errors/expected/extension_rebind_mismatch.res.expected @@ -0,0 +1,10 @@ + + We've found a bug for you! + /.../fixtures/extension_rebind_mismatch.res:5:15 + + 3 │ + 4 │ type a += A(int) + 5 │ type b += B = A + 6 │ + + The constructor A has type a but was expected to be of type b \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/extension_rebind_private.res.expected b/tests/build_tests/super_errors/expected/extension_rebind_private.res.expected new file mode 100644 index 0000000000..e3d9fd1288 --- /dev/null +++ b/tests/build_tests/super_errors/expected/extension_rebind_private.res.expected @@ -0,0 +1,10 @@ + + We've found a bug for you! + /.../fixtures/extension_rebind_private.res:5:15 + + 3 │ type t += private A(int) + 4 │ + 5 │ type t += B = A + 6 │ + + The constructor A is private \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/if_return_type_mismatch.res.expected b/tests/build_tests/super_errors/expected/if_return_type_mismatch.res.expected new file mode 100644 index 0000000000..9c71e16b6e --- /dev/null +++ b/tests/build_tests/super_errors/expected/if_return_type_mismatch.res.expected @@ -0,0 +1,15 @@ + + We've found a bug for you! + /.../fixtures/if_return_type_mismatch.res:2:3 + + 1 │ let x: string = if true { + 2 │ 1 + 3 │ } else { + 4 │ 2 + + This has type: int + But this if statement is expected to return: string + + if expressions must return the same type in all branches (if, else if, else). + + You can convert int to string with Int.toString. \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/illegal_recursive_module_reference.res.expected b/tests/build_tests/super_errors/expected/illegal_recursive_module_reference.res.expected new file mode 100644 index 0000000000..28e915eec5 --- /dev/null +++ b/tests/build_tests/super_errors/expected/illegal_recursive_module_reference.res.expected @@ -0,0 +1,9 @@ + + We've found a bug for you! + /.../fixtures/illegal_recursive_module_reference.res:1:15-17 + + 1 │ module rec A: B.S = { + 2 │ let x = 1 + 3 │ } + + Illegal recursive module reference \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/labeled_fn_argument_type_clash.res.expected b/tests/build_tests/super_errors/expected/labeled_fn_argument_type_clash.res.expected new file mode 100644 index 0000000000..c9b182ba0f --- /dev/null +++ b/tests/build_tests/super_errors/expected/labeled_fn_argument_type_clash.res.expected @@ -0,0 +1,13 @@ + + We've found a bug for you! + /.../fixtures/labeled_fn_argument_type_clash.res:3:14-17 + + 1 │ let f = (~x: int) => x + 2 │ + 3 │ let _ = f(~x="hi") + 4 │ + + This has type: string + But this function argument ~x is expecting: int + + You can convert string to int with Int.fromString. \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/maybe_unwrap_option.res.expected b/tests/build_tests/super_errors/expected/maybe_unwrap_option.res.expected new file mode 100644 index 0000000000..ddab2a4936 --- /dev/null +++ b/tests/build_tests/super_errors/expected/maybe_unwrap_option.res.expected @@ -0,0 +1,9 @@ + + We've found a bug for you! + /.../fixtures/maybe_unwrap_option.res:1:14-20 + + 1 │ let x: int = Some(5) + 2 │ + + This has type: option<'a> + But it's expected to have type: int \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/no_value_clauses.res.expected b/tests/build_tests/super_errors/expected/no_value_clauses.res.expected new file mode 100644 index 0000000000..4be6acd3c4 --- /dev/null +++ b/tests/build_tests/super_errors/expected/no_value_clauses.res.expected @@ -0,0 +1,11 @@ + + We've found a bug for you! + /.../fixtures/no_value_clauses.res:2:3-4:3 + + 1 │ let f = x => + 2 │ switch x { + 3 │  | exception Not_found => 1 + 4 │  } + 5 │ + + None of the patterns in this 'match' expression match values. \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/non_generalizable_module.res.expected b/tests/build_tests/super_errors/expected/non_generalizable_module.res.expected new file mode 100644 index 0000000000..f0c6d435a3 --- /dev/null +++ b/tests/build_tests/super_errors/expected/non_generalizable_module.res.expected @@ -0,0 +1,21 @@ + + We've found a bug for you! + /.../fixtures/non_generalizable_module.res:1:8-5:1 + + 1 │ module M = { + 2 │  module Inner = { + 3 │  let r = ref(None) + 4 │  } + 5 │ } + 6 │ + + The type of this module contains type variables that cannot be generalized: + { + module Inner: { + let r: ref> + } +} + + This happens when the type system senses there's a mutation/side-effect, + in combination with a polymorphic value. + Using or annotating that value usually solves it. \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/pattern_type_clash_polyvariant.res.expected b/tests/build_tests/super_errors/expected/pattern_type_clash_polyvariant.res.expected new file mode 100644 index 0000000000..5f261f93c6 --- /dev/null +++ b/tests/build_tests/super_errors/expected/pattern_type_clash_polyvariant.res.expected @@ -0,0 +1,12 @@ + + We've found a bug for you! + /.../fixtures/pattern_type_clash_polyvariant.res:3:5-6 + + 1 │ let f = (x: int) => + 2 │ switch x { + 3 │ | #A => 1 + 4 │ | _ => 0 + 5 │ } + + This pattern matches values of type [? #A] + but a pattern was expected which matches values of type int \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/pattern_type_clash_tuple_arity.res.expected b/tests/build_tests/super_errors/expected/pattern_type_clash_tuple_arity.res.expected new file mode 100644 index 0000000000..81d39208bd --- /dev/null +++ b/tests/build_tests/super_errors/expected/pattern_type_clash_tuple_arity.res.expected @@ -0,0 +1,12 @@ + + We've found a bug for you! + /.../fixtures/pattern_type_clash_tuple_arity.res:3:5-13 + + 1 │ let f = (x: (int, string)) => + 2 │ switch x { + 3 │ | (a, b, c) => a + Int.fromString(b)->Option.getOr(0) + c + 4 │ } + 5 │ + + This pattern matches values of type ('a, 'b, 'c) + but a pattern was expected which matches values of type (int, string) \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/polymorphic_label.res.expected b/tests/build_tests/super_errors/expected/polymorphic_label.res.expected new file mode 100644 index 0000000000..0a8383c0dc --- /dev/null +++ b/tests/build_tests/super_errors/expected/polymorphic_label.res.expected @@ -0,0 +1,10 @@ + + We've found a bug for you! + /.../fixtures/polymorphic_label.res:3:11 + + 1 │ type t = {f: 'a. 'a => 'a} + 2 │ + 3 │ let g = ({f: (f: int => int)}: t) => f(42) + 4 │ + + The record field f is polymorphic. You cannot instantiate it in a pattern. \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/polyvariant_present_has_conjunction.res.expected b/tests/build_tests/super_errors/expected/polyvariant_present_has_conjunction.res.expected new file mode 100644 index 0000000000..f30f31bfbf --- /dev/null +++ b/tests/build_tests/super_errors/expected/polyvariant_present_has_conjunction.res.expected @@ -0,0 +1,8 @@ + + We've found a bug for you! + /.../fixtures/polyvariant_present_has_conjunction.res:1:10-35 + + 1 │ type t = [< #A(int) & (string) > #A] + 2 │ + + The present constructor A has a conjunctive type \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/polyvariant_present_has_no_type.res.expected b/tests/build_tests/super_errors/expected/polyvariant_present_has_no_type.res.expected new file mode 100644 index 0000000000..78f0adedca --- /dev/null +++ b/tests/build_tests/super_errors/expected/polyvariant_present_has_no_type.res.expected @@ -0,0 +1,8 @@ + + We've found a bug for you! + /.../fixtures/polyvariant_present_has_no_type.res:1:10-19 + + 1 │ type t = [< #B > #A] + 2 │ + + The present constructor A has no type \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/string_concat_non_string.res.expected b/tests/build_tests/super_errors/expected/string_concat_non_string.res.expected new file mode 100644 index 0000000000..b94d1198a4 --- /dev/null +++ b/tests/build_tests/super_errors/expected/string_concat_non_string.res.expected @@ -0,0 +1,11 @@ + + We've found a bug for you! + /.../fixtures/string_concat_non_string.res:1:17-18 + + 1 │ let x = "hi" ++ 42 + 2 │ + + This has type: int + But string concatenation is expecting: string + + You can convert int to string with Int.toString. \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/typemod_signature_expected.res.expected b/tests/build_tests/super_errors/expected/typemod_signature_expected.res.expected new file mode 100644 index 0000000000..b709aa9750 --- /dev/null +++ b/tests/build_tests/super_errors/expected/typemod_signature_expected.res.expected @@ -0,0 +1,10 @@ + + We've found a bug for you! + /.../fixtures/typemod_signature_expected.res:4:18-38 + + 2 │ module M: (X: {}) => {} + 3 │ } + 4 │ module type T2 = T with type M.t = int + 5 │ + + This module type is not a signature \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/typetexp_not_a_variant.res.expected b/tests/build_tests/super_errors/expected/typetexp_not_a_variant.res.expected new file mode 100644 index 0000000000..2b31397965 --- /dev/null +++ b/tests/build_tests/super_errors/expected/typetexp_not_a_variant.res.expected @@ -0,0 +1,9 @@ + + We've found a bug for you! + /.../fixtures/typetexp_not_a_variant.res:2:16 + + 1 │ type a = int + 2 │ type b = [#X | a] + 3 │ + + The type a does not expand to a polymorphic variant type \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/typetexp_type_mismatch.res.expected b/tests/build_tests/super_errors/expected/typetexp_type_mismatch.res.expected new file mode 100644 index 0000000000..6643198019 --- /dev/null +++ b/tests/build_tests/super_errors/expected/typetexp_type_mismatch.res.expected @@ -0,0 +1,9 @@ + + We've found a bug for you! + /.../fixtures/typetexp_type_mismatch.res:2:12-17 + + 1 │ type t<'a> = 'a constraint 'a = int + 2 │ type x = t<string> + 3 │ + + This type string should be an instance of type int \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/unknown_literal.res.expected b/tests/build_tests/super_errors/expected/unknown_literal.res.expected new file mode 100644 index 0000000000..c002136467 --- /dev/null +++ b/tests/build_tests/super_errors/expected/unknown_literal.res.expected @@ -0,0 +1,8 @@ + + We've found a bug for you! + /.../fixtures/unknown_literal.res:1:9-10 + + 1 │ let x = 0z + 2 │ + + Unknown modifier 'z' for literal 0z \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/variant_spread_pattern_not_a_variant.res.expected b/tests/build_tests/super_errors/expected/variant_spread_pattern_not_a_variant.res.expected new file mode 100644 index 0000000000..36b638c039 --- /dev/null +++ b/tests/build_tests/super_errors/expected/variant_spread_pattern_not_a_variant.res.expected @@ -0,0 +1,12 @@ + + We've found a bug for you! + /.../fixtures/variant_spread_pattern_not_a_variant.res:6:8 + + 4 │ let f = (b: b) => + 5 │ switch b { + 6 │ | ...a as v => v + 7 │ | Three => One + 8 │ } + + The type a +is not a variant type \ No newline at end of file diff --git a/tests/build_tests/super_errors/expected/variant_spread_pattern_type_params.res.expected b/tests/build_tests/super_errors/expected/variant_spread_pattern_type_params.res.expected new file mode 100644 index 0000000000..eb89478018 --- /dev/null +++ b/tests/build_tests/super_errors/expected/variant_spread_pattern_type_params.res.expected @@ -0,0 +1,12 @@ + + We've found a bug for you! + /.../fixtures/variant_spread_pattern_type_params.res:6:8 + + 4 │ let f = (b: b) => + 5 │ switch b { + 6 │ | ...a as x => x + 7 │ | Three => One + 8 │ } + + The type a +has type parameters, but type parameters is not supported here. \ No newline at end of file diff --git a/tests/build_tests/super_errors/fixtures/UntaggedAtMostOneBigint.res b/tests/build_tests/super_errors/fixtures/UntaggedAtMostOneBigint.res new file mode 100644 index 0000000000..8162d2398a --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/UntaggedAtMostOneBigint.res @@ -0,0 +1,2 @@ +@unboxed +type t = A(bigint) | B(bigint) diff --git a/tests/build_tests/super_errors/fixtures/UntaggedAtMostOneBoolean.res b/tests/build_tests/super_errors/fixtures/UntaggedAtMostOneBoolean.res new file mode 100644 index 0000000000..7a78734d25 --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/UntaggedAtMostOneBoolean.res @@ -0,0 +1,2 @@ +@unboxed +type t = A(bool) | B(bool) diff --git a/tests/build_tests/super_errors/fixtures/UntaggedAtMostOneFunction.res b/tests/build_tests/super_errors/fixtures/UntaggedAtMostOneFunction.res new file mode 100644 index 0000000000..42ae1f810f --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/UntaggedAtMostOneFunction.res @@ -0,0 +1,2 @@ +@unboxed +type t = A(int => int) | B(string => string) diff --git a/tests/build_tests/super_errors/fixtures/UntaggedAtMostOneInstance.res b/tests/build_tests/super_errors/fixtures/UntaggedAtMostOneInstance.res new file mode 100644 index 0000000000..1df14c658d --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/UntaggedAtMostOneInstance.res @@ -0,0 +1,2 @@ +@unboxed +type t = A(Date.t) | B(Date.t) diff --git a/tests/build_tests/super_errors/fixtures/UntaggedAtMostOneNumber.res b/tests/build_tests/super_errors/fixtures/UntaggedAtMostOneNumber.res new file mode 100644 index 0000000000..7cef4afa40 --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/UntaggedAtMostOneNumber.res @@ -0,0 +1,2 @@ +@unboxed +type t = A(int) | B(float) diff --git a/tests/build_tests/super_errors/fixtures/UntaggedAtMostOneObject.res b/tests/build_tests/super_errors/fixtures/UntaggedAtMostOneObject.res new file mode 100644 index 0000000000..f771b6239e --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/UntaggedAtMostOneObject.res @@ -0,0 +1,4 @@ +type a = {x: int} +type b = {y: int} +@unboxed +type t = A(a) | B(b) diff --git a/tests/build_tests/super_errors/fixtures/UntaggedConstructorMoreThanOneArg.res b/tests/build_tests/super_errors/fixtures/UntaggedConstructorMoreThanOneArg.res new file mode 100644 index 0000000000..fd987b0d32 --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/UntaggedConstructorMoreThanOneArg.res @@ -0,0 +1,2 @@ +@unboxed +type t = A(int, int) | B(string) diff --git a/tests/build_tests/super_errors/fixtures/UntaggedDuplicateLiteral.res b/tests/build_tests/super_errors/fixtures/UntaggedDuplicateLiteral.res new file mode 100644 index 0000000000..22e0d23861 --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/UntaggedDuplicateLiteral.res @@ -0,0 +1,2 @@ +@unboxed +type t = | @as("x") A | @as("x") B diff --git a/tests/build_tests/super_errors/fixtures/UntaggedDuplicatedBsAs.res b/tests/build_tests/super_errors/fixtures/UntaggedDuplicatedBsAs.res new file mode 100644 index 0000000000..1bff00d725 --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/UntaggedDuplicatedBsAs.res @@ -0,0 +1,2 @@ +@unboxed +type t = | @as("x") @as("y") A | B diff --git a/tests/build_tests/super_errors/fixtures/UntaggedInvalidVariantAsAnnotation.res b/tests/build_tests/super_errors/fixtures/UntaggedInvalidVariantAsAnnotation.res new file mode 100644 index 0000000000..1bb9457562 --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/UntaggedInvalidVariantAsAnnotation.res @@ -0,0 +1 @@ +type t = | @as(foo) A | B diff --git a/tests/build_tests/super_errors/fixtures/UntaggedInvalidVariantTagAnnotation.res b/tests/build_tests/super_errors/fixtures/UntaggedInvalidVariantTagAnnotation.res new file mode 100644 index 0000000000..afc50a0b0a --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/UntaggedInvalidVariantTagAnnotation.res @@ -0,0 +1,2 @@ +@tag(123) +type t = A({foo: int}) | B({bar: string}) diff --git a/tests/build_tests/super_errors/fixtures/UntaggedOnlyOneUnknown.res b/tests/build_tests/super_errors/fixtures/UntaggedOnlyOneUnknown.res new file mode 100644 index 0000000000..22756e6e7a --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/UntaggedOnlyOneUnknown.res @@ -0,0 +1,4 @@ +type opaque + +@unboxed +type t = A(opaque) | B(int) diff --git a/tests/build_tests/super_errors/fixtures/UntaggedTagFieldNameConflict.res b/tests/build_tests/super_errors/fixtures/UntaggedTagFieldNameConflict.res new file mode 100644 index 0000000000..f89e40319f --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/UntaggedTagFieldNameConflict.res @@ -0,0 +1,4 @@ +@tag("kind") +type t = + | A({kind: int}) + | B({other: string}) diff --git a/tests/build_tests/super_errors/fixtures/bs_expect_int_or_string_or_json_literal.res b/tests/build_tests/super_errors/fixtures/bs_expect_int_or_string_or_json_literal.res new file mode 100644 index 0000000000..5b43399d9b --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/bs_expect_int_or_string_or_json_literal.res @@ -0,0 +1 @@ +@val external foo: (@as(true) _, int) => unit = "foo" diff --git a/tests/build_tests/super_errors/fixtures/bs_invalid_underscore_type_in_external.res b/tests/build_tests/super_errors/fixtures/bs_invalid_underscore_type_in_external.res new file mode 100644 index 0000000000..96c5a0c218 --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/bs_invalid_underscore_type_in_external.res @@ -0,0 +1 @@ +@obj external make: (~x: _) => _ = "" diff --git a/tests/build_tests/super_errors/fixtures/bs_optional_in_uncurried_bs_attribute.res b/tests/build_tests/super_errors/fixtures/bs_optional_in_uncurried_bs_attribute.res new file mode 100644 index 0000000000..f95ef930d1 --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/bs_optional_in_uncurried_bs_attribute.res @@ -0,0 +1 @@ +let f = @this (self, ~x=?) => self + x->Option.getOr(0) diff --git a/tests/build_tests/super_errors/fixtures/bs_this_simple_pattern.res b/tests/build_tests/super_errors/fixtures/bs_this_simple_pattern.res new file mode 100644 index 0000000000..fffc2aaad6 --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/bs_this_simple_pattern.res @@ -0,0 +1 @@ +let f = @this ((a, b), x) => a + b + x diff --git a/tests/build_tests/super_errors/fixtures/bs_unsupported_predicates.res b/tests/build_tests/super_errors/fixtures/bs_unsupported_predicates.res new file mode 100644 index 0000000000..6630acd60a --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/bs_unsupported_predicates.res @@ -0,0 +1 @@ +type t = {..@get({weird: true}) "x": int} diff --git a/tests/build_tests/super_errors/fixtures/cannot_quantify.res b/tests/build_tests/super_errors/fixtures/cannot_quantify.res new file mode 100644 index 0000000000..be7a1f2764 --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/cannot_quantify.res @@ -0,0 +1 @@ +type t = {f: 'a. (int as 'a) => int} diff --git a/tests/build_tests/super_errors/fixtures/empty_record_literal.res b/tests/build_tests/super_errors/fixtures/empty_record_literal.res new file mode 100644 index 0000000000..149d1a86b9 --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/empty_record_literal.res @@ -0,0 +1 @@ +let bad = {} diff --git a/tests/build_tests/super_errors/fixtures/extension_arity_mismatch.res b/tests/build_tests/super_errors/fixtures/extension_arity_mismatch.res new file mode 100644 index 0000000000..aab2fdaf7f --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/extension_arity_mismatch.res @@ -0,0 +1,3 @@ +type t<'a> = .. + +type t += A(int) diff --git a/tests/build_tests/super_errors/fixtures/extension_rebind_mismatch.res b/tests/build_tests/super_errors/fixtures/extension_rebind_mismatch.res new file mode 100644 index 0000000000..da18506222 --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/extension_rebind_mismatch.res @@ -0,0 +1,5 @@ +type a = .. +type b = .. + +type a += A(int) +type b += B = A diff --git a/tests/build_tests/super_errors/fixtures/extension_rebind_private.res b/tests/build_tests/super_errors/fixtures/extension_rebind_private.res new file mode 100644 index 0000000000..5c56ba18ee --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/extension_rebind_private.res @@ -0,0 +1,5 @@ +type t = .. + +type t += private A(int) + +type t += B = A diff --git a/tests/build_tests/super_errors/fixtures/if_return_type_mismatch.res b/tests/build_tests/super_errors/fixtures/if_return_type_mismatch.res new file mode 100644 index 0000000000..a835f12117 --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/if_return_type_mismatch.res @@ -0,0 +1,5 @@ +let x: string = if true { + 1 +} else { + 2 +} diff --git a/tests/build_tests/super_errors/fixtures/illegal_recursive_module_reference.res b/tests/build_tests/super_errors/fixtures/illegal_recursive_module_reference.res new file mode 100644 index 0000000000..0e4cfdc78b --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/illegal_recursive_module_reference.res @@ -0,0 +1,12 @@ +module rec A: B.S = { + let x = 1 +} +and B: { + module type S = { + let x: int + } +} = { + module type S = { + let x: int + } +} diff --git a/tests/build_tests/super_errors/fixtures/labeled_fn_argument_type_clash.res b/tests/build_tests/super_errors/fixtures/labeled_fn_argument_type_clash.res new file mode 100644 index 0000000000..baf06ae031 --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/labeled_fn_argument_type_clash.res @@ -0,0 +1,3 @@ +let f = (~x: int) => x + +let _ = f(~x="hi") diff --git a/tests/build_tests/super_errors/fixtures/maybe_unwrap_option.res b/tests/build_tests/super_errors/fixtures/maybe_unwrap_option.res new file mode 100644 index 0000000000..ef814deefd --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/maybe_unwrap_option.res @@ -0,0 +1 @@ +let x: int = Some(5) diff --git a/tests/build_tests/super_errors/fixtures/no_value_clauses.res b/tests/build_tests/super_errors/fixtures/no_value_clauses.res new file mode 100644 index 0000000000..1f4afba1e0 --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/no_value_clauses.res @@ -0,0 +1,4 @@ +let f = x => + switch x { + | exception Not_found => 1 + } diff --git a/tests/build_tests/super_errors/fixtures/non_generalizable_module.res b/tests/build_tests/super_errors/fixtures/non_generalizable_module.res new file mode 100644 index 0000000000..2338cf7785 --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/non_generalizable_module.res @@ -0,0 +1,5 @@ +module M = { + module Inner = { + let r = ref(None) + } +} diff --git a/tests/build_tests/super_errors/fixtures/pattern_type_clash_polyvariant.res b/tests/build_tests/super_errors/fixtures/pattern_type_clash_polyvariant.res new file mode 100644 index 0000000000..79dd489709 --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/pattern_type_clash_polyvariant.res @@ -0,0 +1,5 @@ +let f = (x: int) => + switch x { + | #A => 1 + | _ => 0 + } diff --git a/tests/build_tests/super_errors/fixtures/pattern_type_clash_tuple_arity.res b/tests/build_tests/super_errors/fixtures/pattern_type_clash_tuple_arity.res new file mode 100644 index 0000000000..786c0d830b --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/pattern_type_clash_tuple_arity.res @@ -0,0 +1,4 @@ +let f = (x: (int, string)) => + switch x { + | (a, b, c) => a + Int.fromString(b)->Option.getOr(0) + c + } diff --git a/tests/build_tests/super_errors/fixtures/polymorphic_label.res b/tests/build_tests/super_errors/fixtures/polymorphic_label.res new file mode 100644 index 0000000000..8221bf50b0 --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/polymorphic_label.res @@ -0,0 +1,3 @@ +type t = {f: 'a. 'a => 'a} + +let g = ({f: (f: int => int)}: t) => f(42) diff --git a/tests/build_tests/super_errors/fixtures/polyvariant_present_has_conjunction.res b/tests/build_tests/super_errors/fixtures/polyvariant_present_has_conjunction.res new file mode 100644 index 0000000000..7ab6b7df90 --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/polyvariant_present_has_conjunction.res @@ -0,0 +1 @@ +type t = [< #A(int) & (string) > #A] diff --git a/tests/build_tests/super_errors/fixtures/polyvariant_present_has_no_type.res b/tests/build_tests/super_errors/fixtures/polyvariant_present_has_no_type.res new file mode 100644 index 0000000000..014466549a --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/polyvariant_present_has_no_type.res @@ -0,0 +1 @@ +type t = [< #B > #A] diff --git a/tests/build_tests/super_errors/fixtures/string_concat_non_string.res b/tests/build_tests/super_errors/fixtures/string_concat_non_string.res new file mode 100644 index 0000000000..f1985516c9 --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/string_concat_non_string.res @@ -0,0 +1 @@ +let x = "hi" ++ 42 diff --git a/tests/build_tests/super_errors/fixtures/typemod_signature_expected.res b/tests/build_tests/super_errors/fixtures/typemod_signature_expected.res new file mode 100644 index 0000000000..3f19e510ed --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/typemod_signature_expected.res @@ -0,0 +1,4 @@ +module type T = { + module M: (X: {}) => {} +} +module type T2 = T with type M.t = int diff --git a/tests/build_tests/super_errors/fixtures/typetexp_not_a_variant.res b/tests/build_tests/super_errors/fixtures/typetexp_not_a_variant.res new file mode 100644 index 0000000000..6ca2a4f099 --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/typetexp_not_a_variant.res @@ -0,0 +1,2 @@ +type a = int +type b = [#X | a] diff --git a/tests/build_tests/super_errors/fixtures/typetexp_type_mismatch.res b/tests/build_tests/super_errors/fixtures/typetexp_type_mismatch.res new file mode 100644 index 0000000000..de39d0e28b --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/typetexp_type_mismatch.res @@ -0,0 +1,2 @@ +type t<'a> = 'a constraint 'a = int +type x = t diff --git a/tests/build_tests/super_errors/fixtures/unknown_literal.res b/tests/build_tests/super_errors/fixtures/unknown_literal.res new file mode 100644 index 0000000000..0705906df1 --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/unknown_literal.res @@ -0,0 +1 @@ +let x = 0z diff --git a/tests/build_tests/super_errors/fixtures/variant_spread_pattern_not_a_variant.res b/tests/build_tests/super_errors/fixtures/variant_spread_pattern_not_a_variant.res new file mode 100644 index 0000000000..2a2aae331f --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/variant_spread_pattern_not_a_variant.res @@ -0,0 +1,8 @@ +type a = {x: int} +type b = One | Two | Three + +let f = (b: b) => + switch b { + | ...a as v => v + | Three => One + } diff --git a/tests/build_tests/super_errors/fixtures/variant_spread_pattern_type_params.res b/tests/build_tests/super_errors/fixtures/variant_spread_pattern_type_params.res new file mode 100644 index 0000000000..9ff3872894 --- /dev/null +++ b/tests/build_tests/super_errors/fixtures/variant_spread_pattern_type_params.res @@ -0,0 +1,8 @@ +type a<'x> = One | Two('x) +type b = One | Two(int) | Three + +let f = (b: b) => + switch b { + | ...a as x => x + | Three => One + } diff --git a/tests/build_tests/super_errors_multi/expected/Iface_constraint_mismatch.expected b/tests/build_tests/super_errors_multi/expected/Iface_constraint_mismatch.expected new file mode 100644 index 0000000000..f829519e4e --- /dev/null +++ b/tests/build_tests/super_errors_multi/expected/Iface_constraint_mismatch.expected @@ -0,0 +1,20 @@ +===== Foo.res ===== + + We've found a bug for you! + /.../fixtures/Iface_constraint_mismatch/Foo.res:1:1-35 + + 1 │ type t<'a> = 'a constraint 'a = int + 2 │ + + The implementation /.../fixtures/Iface_constraint_mismatch/Foo.res + does not match the interface /.../fixtures/Iface_constraint_mismatch/foo.cmi: + Type declarations do not match: + type t<'a> = 'a + constraint 'a = int + is not included in + type t<'a> + /.../fixtures/Iface_constraint_mismatch/Foo.resi:1:1-10: + Expected declaration + /.../fixtures/Iface_constraint_mismatch/Foo.res:1:1-35: + Actual declaration + Their constraints differ. \ No newline at end of file diff --git a/tests/build_tests/super_errors_multi/expected/Iface_field_arity_mismatch.expected b/tests/build_tests/super_errors_multi/expected/Iface_field_arity_mismatch.expected new file mode 100644 index 0000000000..5f1609f1f9 --- /dev/null +++ b/tests/build_tests/super_errors_multi/expected/Iface_field_arity_mismatch.expected @@ -0,0 +1,19 @@ +===== Foo.res ===== + + We've found a bug for you! + /.../fixtures/Iface_field_arity_mismatch/Foo.res:1:1-19 + + 1 │ type t = A(int) | B + 2 │ + + The implementation /.../fixtures/Iface_field_arity_mismatch/Foo.res + does not match the interface /.../fixtures/Iface_field_arity_mismatch/foo.cmi: + Type declarations do not match: + type t = A(int) | B + is not included in + type t = A(int, int) | B + /.../fixtures/Iface_field_arity_mismatch/Foo.resi:1:1-24: + Expected declaration + /.../fixtures/Iface_field_arity_mismatch/Foo.res:1:1-19: + Actual declaration + The arities for field A differ. \ No newline at end of file diff --git a/tests/build_tests/super_errors_multi/expected/Iface_field_names_mismatch.expected b/tests/build_tests/super_errors_multi/expected/Iface_field_names_mismatch.expected new file mode 100644 index 0000000000..50e347af7e --- /dev/null +++ b/tests/build_tests/super_errors_multi/expected/Iface_field_names_mismatch.expected @@ -0,0 +1,19 @@ +===== Foo.res ===== + + We've found a bug for you! + /.../fixtures/Iface_field_names_mismatch/Foo.res:1:1-25 + + 1 │ type t = {a: int, b: int} + 2 │ + + The implementation /.../fixtures/Iface_field_names_mismatch/Foo.res + does not match the interface /.../fixtures/Iface_field_names_mismatch/foo.cmi: + Type declarations do not match: + type t = {a: int, b: int} + is not included in + type t = {x: int, y: int} + /.../fixtures/Iface_field_names_mismatch/Foo.resi:1:1-25: + Expected declaration + /.../fixtures/Iface_field_names_mismatch/Foo.res:1:1-25: + Actual declaration + Fields number 1 have different names, a and x. \ No newline at end of file diff --git a/tests/build_tests/super_errors_multi/expected/Iface_immediate_mismatch.expected b/tests/build_tests/super_errors_multi/expected/Iface_immediate_mismatch.expected new file mode 100644 index 0000000000..7b6273d7c0 --- /dev/null +++ b/tests/build_tests/super_errors_multi/expected/Iface_immediate_mismatch.expected @@ -0,0 +1,19 @@ +===== Foo.res ===== + + We've found a bug for you! + /.../fixtures/Iface_immediate_mismatch/Foo.res:1:1-15 + + 1 │ type t = string + 2 │ + + The implementation /.../fixtures/Iface_immediate_mismatch/Foo.res + does not match the interface /.../fixtures/Iface_immediate_mismatch/foo.cmi: + Type declarations do not match: + type t = string + is not included in + @immediate type t + /.../fixtures/Iface_immediate_mismatch/Foo.resi:2:1-6: + Expected declaration + /.../fixtures/Iface_immediate_mismatch/Foo.res:1:1-15: + Actual declaration + the first is not an immediate type. \ No newline at end of file diff --git a/tests/build_tests/super_errors_multi/expected/Iface_kind_mismatch.expected b/tests/build_tests/super_errors_multi/expected/Iface_kind_mismatch.expected new file mode 100644 index 0000000000..c00ea7282a --- /dev/null +++ b/tests/build_tests/super_errors_multi/expected/Iface_kind_mismatch.expected @@ -0,0 +1,19 @@ +===== Foo.res ===== + + We've found a bug for you! + /.../fixtures/Iface_kind_mismatch/Foo.res:1:1-17 + + 1 │ type t = {x: int} + 2 │ + + The implementation /.../fixtures/Iface_kind_mismatch/Foo.res + does not match the interface /.../fixtures/Iface_kind_mismatch/foo.cmi: + Type declarations do not match: + type t = {x: int} + is not included in + type t = A | B + /.../fixtures/Iface_kind_mismatch/Foo.resi:1:1-14: + Expected declaration + /.../fixtures/Iface_kind_mismatch/Foo.res:1:1-17: + Actual declaration + Their kinds differ. \ No newline at end of file diff --git a/tests/build_tests/super_errors_multi/expected/Iface_manifest_mismatch.expected b/tests/build_tests/super_errors_multi/expected/Iface_manifest_mismatch.expected new file mode 100644 index 0000000000..7271fca599 --- /dev/null +++ b/tests/build_tests/super_errors_multi/expected/Iface_manifest_mismatch.expected @@ -0,0 +1,18 @@ +===== Foo.res ===== + + We've found a bug for you! + /.../fixtures/Iface_manifest_mismatch/Foo.res:1:1-15 + + 1 │ type t = string + 2 │ + + The implementation /.../fixtures/Iface_manifest_mismatch/Foo.res + does not match the interface /.../fixtures/Iface_manifest_mismatch/foo.cmi: + Type declarations do not match: + type t = string + is not included in + type t = int + /.../fixtures/Iface_manifest_mismatch/Foo.resi:1:1-12: + Expected declaration + /.../fixtures/Iface_manifest_mismatch/Foo.res:1:1-15: + Actual declaration \ No newline at end of file diff --git a/tests/build_tests/super_errors_multi/expected/Iface_record_representation_mismatch.expected b/tests/build_tests/super_errors_multi/expected/Iface_record_representation_mismatch.expected new file mode 100644 index 0000000000..7eb863109e --- /dev/null +++ b/tests/build_tests/super_errors_multi/expected/Iface_record_representation_mismatch.expected @@ -0,0 +1,20 @@ +===== Foo.res ===== + + We've found a bug for you! + /.../fixtures/Iface_record_representation_mismatch/Foo.res:1:1-17 + + 1 │ type t = {x: int} + 2 │ + + The implementation /.../fixtures/Iface_record_representation_mismatch/Foo.res + does not match the interface /.../fixtures/Iface_record_representation_mismatch/foo.cmi: + Type declarations do not match: + type t = {x: int} + is not included in + @unboxed type t = {x: int} + /.../fixtures/Iface_record_representation_mismatch/Foo.resi:2:1-17: + Expected declaration + /.../fixtures/Iface_record_representation_mismatch/Foo.res:1:1-17: + Actual declaration + Their internal representations differ: + the second declaration uses unboxed representation. \ No newline at end of file diff --git a/tests/build_tests/super_errors_multi/expected/Iface_variance_mismatch.expected b/tests/build_tests/super_errors_multi/expected/Iface_variance_mismatch.expected new file mode 100644 index 0000000000..39b96b60e2 --- /dev/null +++ b/tests/build_tests/super_errors_multi/expected/Iface_variance_mismatch.expected @@ -0,0 +1,19 @@ +===== Foo.res ===== + + We've found a bug for you! + /.../fixtures/Iface_variance_mismatch/Foo.res:1:1-21 + + 1 │ type t<'a> = 'a => 'a + 2 │ + + The implementation /.../fixtures/Iface_variance_mismatch/Foo.res + does not match the interface /.../fixtures/Iface_variance_mismatch/foo.cmi: + Type declarations do not match: + type t<'a> = 'a => 'a + is not included in + type t<+'a> + /.../fixtures/Iface_variance_mismatch/Foo.resi:1:1-11: + Expected declaration + /.../fixtures/Iface_variance_mismatch/Foo.res:1:1-21: + Actual declaration + Their variances do not agree. \ No newline at end of file diff --git a/tests/build_tests/super_errors_multi/expected/Iface_variant_extra_constructor.expected b/tests/build_tests/super_errors_multi/expected/Iface_variant_extra_constructor.expected new file mode 100644 index 0000000000..24f4bd7892 --- /dev/null +++ b/tests/build_tests/super_errors_multi/expected/Iface_variant_extra_constructor.expected @@ -0,0 +1,19 @@ +===== Foo.res ===== + + We've found a bug for you! + /.../fixtures/Iface_variant_extra_constructor/Foo.res:1:1-18 + + 1 │ type t = A | B | C + 2 │ + + The implementation /.../fixtures/Iface_variant_extra_constructor/Foo.res + does not match the interface /.../fixtures/Iface_variant_extra_constructor/foo.cmi: + Type declarations do not match: + type t = A | B | C + is not included in + type t = A | B + /.../fixtures/Iface_variant_extra_constructor/Foo.resi:1:1-14: + Expected declaration + /.../fixtures/Iface_variant_extra_constructor/Foo.res:1:1-18: + Actual declaration + The field C is only present in the first declaration. \ No newline at end of file diff --git a/tests/build_tests/super_errors_multi/fixtures/Iface_constraint_mismatch/Foo.res b/tests/build_tests/super_errors_multi/fixtures/Iface_constraint_mismatch/Foo.res new file mode 100644 index 0000000000..5592f33e50 --- /dev/null +++ b/tests/build_tests/super_errors_multi/fixtures/Iface_constraint_mismatch/Foo.res @@ -0,0 +1 @@ +type t<'a> = 'a constraint 'a = int diff --git a/tests/build_tests/super_errors_multi/fixtures/Iface_constraint_mismatch/Foo.resi b/tests/build_tests/super_errors_multi/fixtures/Iface_constraint_mismatch/Foo.resi new file mode 100644 index 0000000000..cfacdaa67e --- /dev/null +++ b/tests/build_tests/super_errors_multi/fixtures/Iface_constraint_mismatch/Foo.resi @@ -0,0 +1 @@ +type t<'a> diff --git a/tests/build_tests/super_errors_multi/fixtures/Iface_field_arity_mismatch/Foo.res b/tests/build_tests/super_errors_multi/fixtures/Iface_field_arity_mismatch/Foo.res new file mode 100644 index 0000000000..f097aee38d --- /dev/null +++ b/tests/build_tests/super_errors_multi/fixtures/Iface_field_arity_mismatch/Foo.res @@ -0,0 +1 @@ +type t = A(int) | B diff --git a/tests/build_tests/super_errors_multi/fixtures/Iface_field_arity_mismatch/Foo.resi b/tests/build_tests/super_errors_multi/fixtures/Iface_field_arity_mismatch/Foo.resi new file mode 100644 index 0000000000..ce215b5419 --- /dev/null +++ b/tests/build_tests/super_errors_multi/fixtures/Iface_field_arity_mismatch/Foo.resi @@ -0,0 +1 @@ +type t = A(int, int) | B diff --git a/tests/build_tests/super_errors_multi/fixtures/Iface_field_names_mismatch/Foo.res b/tests/build_tests/super_errors_multi/fixtures/Iface_field_names_mismatch/Foo.res new file mode 100644 index 0000000000..e2e17af745 --- /dev/null +++ b/tests/build_tests/super_errors_multi/fixtures/Iface_field_names_mismatch/Foo.res @@ -0,0 +1 @@ +type t = {a: int, b: int} diff --git a/tests/build_tests/super_errors_multi/fixtures/Iface_field_names_mismatch/Foo.resi b/tests/build_tests/super_errors_multi/fixtures/Iface_field_names_mismatch/Foo.resi new file mode 100644 index 0000000000..b5eab82245 --- /dev/null +++ b/tests/build_tests/super_errors_multi/fixtures/Iface_field_names_mismatch/Foo.resi @@ -0,0 +1 @@ +type t = {x: int, y: int} diff --git a/tests/build_tests/super_errors_multi/fixtures/Iface_immediate_mismatch/Foo.res b/tests/build_tests/super_errors_multi/fixtures/Iface_immediate_mismatch/Foo.res new file mode 100644 index 0000000000..c6a8e288e4 --- /dev/null +++ b/tests/build_tests/super_errors_multi/fixtures/Iface_immediate_mismatch/Foo.res @@ -0,0 +1 @@ +type t = string diff --git a/tests/build_tests/super_errors_multi/fixtures/Iface_immediate_mismatch/Foo.resi b/tests/build_tests/super_errors_multi/fixtures/Iface_immediate_mismatch/Foo.resi new file mode 100644 index 0000000000..fa534f94c0 --- /dev/null +++ b/tests/build_tests/super_errors_multi/fixtures/Iface_immediate_mismatch/Foo.resi @@ -0,0 +1,2 @@ +@immediate +type t diff --git a/tests/build_tests/super_errors_multi/fixtures/Iface_kind_mismatch/Foo.res b/tests/build_tests/super_errors_multi/fixtures/Iface_kind_mismatch/Foo.res new file mode 100644 index 0000000000..78f2639963 --- /dev/null +++ b/tests/build_tests/super_errors_multi/fixtures/Iface_kind_mismatch/Foo.res @@ -0,0 +1 @@ +type t = {x: int} diff --git a/tests/build_tests/super_errors_multi/fixtures/Iface_kind_mismatch/Foo.resi b/tests/build_tests/super_errors_multi/fixtures/Iface_kind_mismatch/Foo.resi new file mode 100644 index 0000000000..cdb40e3704 --- /dev/null +++ b/tests/build_tests/super_errors_multi/fixtures/Iface_kind_mismatch/Foo.resi @@ -0,0 +1 @@ +type t = A | B diff --git a/tests/build_tests/super_errors_multi/fixtures/Iface_manifest_mismatch/Foo.res b/tests/build_tests/super_errors_multi/fixtures/Iface_manifest_mismatch/Foo.res new file mode 100644 index 0000000000..c6a8e288e4 --- /dev/null +++ b/tests/build_tests/super_errors_multi/fixtures/Iface_manifest_mismatch/Foo.res @@ -0,0 +1 @@ +type t = string diff --git a/tests/build_tests/super_errors_multi/fixtures/Iface_manifest_mismatch/Foo.resi b/tests/build_tests/super_errors_multi/fixtures/Iface_manifest_mismatch/Foo.resi new file mode 100644 index 0000000000..975adb5316 --- /dev/null +++ b/tests/build_tests/super_errors_multi/fixtures/Iface_manifest_mismatch/Foo.resi @@ -0,0 +1 @@ +type t = int diff --git a/tests/build_tests/super_errors_multi/fixtures/Iface_record_representation_mismatch/Foo.res b/tests/build_tests/super_errors_multi/fixtures/Iface_record_representation_mismatch/Foo.res new file mode 100644 index 0000000000..78f2639963 --- /dev/null +++ b/tests/build_tests/super_errors_multi/fixtures/Iface_record_representation_mismatch/Foo.res @@ -0,0 +1 @@ +type t = {x: int} diff --git a/tests/build_tests/super_errors_multi/fixtures/Iface_record_representation_mismatch/Foo.resi b/tests/build_tests/super_errors_multi/fixtures/Iface_record_representation_mismatch/Foo.resi new file mode 100644 index 0000000000..22a0ef7506 --- /dev/null +++ b/tests/build_tests/super_errors_multi/fixtures/Iface_record_representation_mismatch/Foo.resi @@ -0,0 +1,2 @@ +@unboxed +type t = {x: int} diff --git a/tests/build_tests/super_errors_multi/fixtures/Iface_variance_mismatch/Foo.res b/tests/build_tests/super_errors_multi/fixtures/Iface_variance_mismatch/Foo.res new file mode 100644 index 0000000000..5f2131755d --- /dev/null +++ b/tests/build_tests/super_errors_multi/fixtures/Iface_variance_mismatch/Foo.res @@ -0,0 +1 @@ +type t<'a> = 'a => 'a diff --git a/tests/build_tests/super_errors_multi/fixtures/Iface_variance_mismatch/Foo.resi b/tests/build_tests/super_errors_multi/fixtures/Iface_variance_mismatch/Foo.resi new file mode 100644 index 0000000000..3b5ed7d76b --- /dev/null +++ b/tests/build_tests/super_errors_multi/fixtures/Iface_variance_mismatch/Foo.resi @@ -0,0 +1 @@ +type t<+'a> diff --git a/tests/build_tests/super_errors_multi/fixtures/Iface_variant_extra_constructor/Foo.res b/tests/build_tests/super_errors_multi/fixtures/Iface_variant_extra_constructor/Foo.res new file mode 100644 index 0000000000..b24f39fe16 --- /dev/null +++ b/tests/build_tests/super_errors_multi/fixtures/Iface_variant_extra_constructor/Foo.res @@ -0,0 +1 @@ +type t = A | B | C diff --git a/tests/build_tests/super_errors_multi/fixtures/Iface_variant_extra_constructor/Foo.resi b/tests/build_tests/super_errors_multi/fixtures/Iface_variant_extra_constructor/Foo.resi new file mode 100644 index 0000000000..cdb40e3704 --- /dev/null +++ b/tests/build_tests/super_errors_multi/fixtures/Iface_variant_extra_constructor/Foo.resi @@ -0,0 +1 @@ +type t = A | B