diff --git a/CHANGELOG.md b/CHANGELOG.md index 2f80d83..8c442b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ ## Unreleased +### Reject unlisted typecheck fixtures + +- `tools/validate_typecheck_diagnostics.py` now discovers `tests/typecheck_*.claro` files and fails when a fixture is missing from the validator's positive or negative lists. +- This keeps new learner-facing typecheck examples from silently escaping release validation. + ### Explain text operands in object-field addition - Added focused typecheck coverage for `SET player.score player.name + 1`. diff --git a/tools/validate_typecheck_diagnostics.py b/tools/validate_typecheck_diagnostics.py index 2251954..7dab7bb 100644 --- a/tools/validate_typecheck_diagnostics.py +++ b/tools/validate_typecheck_diagnostics.py @@ -205,6 +205,23 @@ STALE_PHRASES = [ ] +def validate_fixture_coverage(): + discovered = { + path.relative_to(ROOT).as_posix() + for path in (ROOT / "tests").glob("typecheck_*.claro") + } + listed = set(EXPECTED) | set(EXPECTED_OK) + missing = sorted(discovered - listed) + extra = sorted(listed - discovered - {"tests/37_object_field_types.claro"}) + if missing or extra: + details = [] + if missing: + details.append("unlisted fixtures: " + ", ".join(missing)) + if extra: + details.append("missing fixture files: " + ", ".join(extra)) + raise SystemExit("Typecheck fixture coverage mismatch: " + "; ".join(details)) + + def run_typecheck(path): result = subprocess.run( [str(EXE), "typecheck", path], @@ -251,6 +268,7 @@ def main(): if not EXE.exists(): raise SystemExit(f"Missing Claro executable: {EXE}") + validate_fixture_coverage() for path in EXPECTED: run_typecheck(path)