Repository navigation
Transpile the Hack conformance fixtures into a Psalm test suite - #12049
Merged
Merged
Conversation
Split the fixtures into topic subfolders (type_variables, contexts, polymorphic_contexts) and, instead of pointing each one to an "equivalent" Psalm test, transpile them all to PHP with Psalm docblocks: bin/hack-conformance/transpile.php generates tests/HackConformanceTranspiledTest.php, asserting of Psalm the verdict HHVM gives each fixture. HackConformanceTest checks the generated suite is up to date. New fixture headers: psalm-error (the issue expected of Psalm), psalm-ignore (issues beside the point of the fixture) and psalm-divergence (a known disagreement, generated as a skipped case). Co-Authored-By: Claude Opus 5.5
HackConformanceTranspiledTest now re-runs the transpiler in setUpBeforeClass and fails if the output differs from the committed file, so running the suite on its own (or a single filtered case) cannot pass on stale output. Co-Authored-By: Claude Opus 5.5
The transpiler no longer tokenises Hack itself: it walks the full-fidelity parse tree hh_parse prints for each fixture (run in the pinned HHVM image, cached by image and contents in .hh-parse-cache/). Node kinds it does not know are errors, so unsupported Hack can no longer turn into broken or wrong PHP, and member names such as vec, shape or IS are left alone. Also fixed while moving over: - a lambda without a context gets the context of the function around it, as in Hack, instead of none - a [_] function type is only accepted on a ctx $param parameter - Hack readonly on parameters, properties and methods is rejected instead of being emitted as PHP readonly Transpiling now needs HHVM, so --check exits 3 where it cannot run: the transpiled suite then runs its committed cases, and HackConformanceTest::testTranspiledSuiteIsUpToDate skips (the Hack conformance CI job runs it with --fail-on-skipped).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Instead of pointing each Hack conformance fixture to an "equivalent" Psalm test (
//// psalm-test:), the fixtures are now transpiled automatically into a pure PHP test suite, so every fixture is checked against both HHVM and Psalm.Changes
Subfolders: fixtures are split by topic into
bin/hack-conformance/fixtures/{type_variables,contexts,polymorphic_contexts}/.run.php,HackConformanceTestand the HHVM CI job find them recursively.Transpiler (
bin/hack-conformance/Transpiler.php): converts the subset of Hack the fixtures use into PHP with Psalm docblocks. It does not parse Hack itself: it walks the parse tree HHVM's own parser prints (hh_parse --full-fidelity-json-parse-tree, run in the pinned image and cached in.hh-parse-cache/). The mapping table is at the top of the file:@psalm-capabilities. A missing context is Hack's[defaults], so it becomes@psalm-impure. Every context list includesread-props, because Hack's[]may read properties.asis the lower bound,superthe upper), and a subclass binding one becomes@extends Parent[...]. Concrete context constants become@psalm-type/@psalm-import-typealiases.[ctx $f]with(function()[_]: T)becomesClosure[_]. A[_]anywhere else is an error.inout(as by-reference pluswrite-refs),readonly,is, and function pointers.An unknown parse-tree node kind is an error naming it and its line, never a silent mistranslation.
Generator (
bin/hack-conformance/transpile.php): writestests/HackConformanceTranspiledTest.php. A fixture HHVM accepts becomes a valid-code case, and one it rejects becomes an invalid-code case.Staleness check:
transpile.php --checktranspiles again and compares the result with the committed suite.HackConformanceTranspiledTest::setUpBeforeClassruns it, so every transpiled case fails on a stale suite, even when one case is run alone.--checkexits 3, and the committed cases run as they are.HackConformanceTest::testTranspiledSuiteIsUpToDateskips in that case. The HHVM CI job runs it with--fail-on-skipped, so there it must pass.New fixture headers:
//// psalm-error:— the issue an error fixture must produce in Psalm.//// psalm-ignore:, ... — issues beside the point of the fixture.//// psalm-divergence:— a known disagreement with Hack, generated as aSKIPPED-case.Divergences the transpiled suite shows
type_variables/multiple_return_types,type_variables/empty_construction_union_return): Psalm reportsInvalidTemplateParamfor@param Closure(TKey): mixedin a@template-covariant TKeyclass unless the method is mutation-free. That position is covariant, and Hack accepts it. The hand-written tests suppressed this; the fixtures now usepsalm-ignore.type_variables/typed_closure_param_empty_construction(skipped): Psalm types an emptynew ArrayCollection()asArrayCollectionand keeps that after the assignment to anArrayCollectionproperty. It then reportsParadoxicalConditionon the typed lambda parameter, which Hack accepts.Verification
HackConformanceTranspiledTest: 28 cases pass, 1 is skipped (the divergence above).php bin/hack-conformance/run.php: HHVM agrees with all 29 fixtures.phpcsand Psalm self-analysis are clean on the changed files.🤖 Generated with Claude Code