diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 9a597938..b5880a2c 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -11,12 +11,11 @@ jobs: # Run tests on all OS's and HHVM versions, even if one fails fail-fast: false matrix: - os: [ ubuntu ] + os: [ ubuntu-20.04 ] hhvm: - - '4.128' - - latest - - nightly - runs-on: ${{matrix.os}}-latest + - '4.153' + - '4.168' + runs-on: ${{matrix.os}} steps: - uses: actions/checkout@v2 - uses: hhvm/actions/hack-lint-test@master diff --git a/composer.json b/composer.json index 0f577a62..4ec9fe48 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,7 @@ "description": "Hack Codegen is a library for programmatically generating Hack code", "keywords": ["code generation", "Hack"], "require": { - "hhvm": "^4.128" + "hhvm": "^4.153" }, "bin": [ "bin/hh-codegen-verify-signatures", "bin/hh-codegen-verify-signatures.hack" ], "authors": [ diff --git a/src/CodegenFile.hack b/src/CodegenFile.hack index ddc7d0c8..7c77727e 100644 --- a/src/CodegenFile.hack +++ b/src/CodegenFile.hack @@ -32,8 +32,9 @@ enum CodegenFileType: int { * depending on whether there are manual sections. */ final class CodegenFile { + const vec LEGACY_FILE_SUFFIX_TRIGGERS = vec['.php', '.hh', '.hhi']; - private CodegenFileType $fileType = CodegenFileType::HACK_STRICT; + private CodegenFileType $fileType; private ?string $docBlock; private string $fileName; private string $relativeFileName; @@ -63,6 +64,13 @@ final class CodegenFile { private IHackCodegenConfig $config, string $file_name, ) { + $this->fileType = C\any( + static::LEGACY_FILE_SUFFIX_TRIGGERS, + $t ==> Str\ends_with($file_name, $t), + ) + ? CodegenFileType::HACK_STRICT + : CodegenFileType::DOT_HACK; + $root = $config->getRootDir(); if (!Str\starts_with($file_name, '/')) { $this->relativeFileName = $file_name; @@ -186,6 +194,10 @@ final class CodegenFile { return $this->fileName; } + public function getFileType()[]: CodegenFileType { + return $this->fileType; + } + public function getRelativeFileName(): string { return $this->relativeFileName; } diff --git a/tests/CodegenFileTest.codegen b/tests/CodegenFileTest.codegen index ba62abba..e2e62620 100644 --- a/tests/CodegenFileTest.codegen +++ b/tests/CodegenFileTest.codegen @@ -1,13 +1,12 @@ @generated !@#$%codegentest:testAutogenerated -> + * @-generated SignedSource<> */ class AllAutogenerated { @@ -18,12 +17,11 @@ class AllAutogenerated { } !@#$%codegentest:testConstants -> + * @-generated SignedSource<<30f984475edc103094e561bce9e9fdf0>> */ namespace Foo\Bar; use namespace Herp\Derp; @@ -67,11 +65,10 @@ function main(): void { main(); !@#$%codegentest:testFormattingFullyGeneratedFile -> + * @-generated SignedSource<> */ function my_func( @@ -85,11 +82,10 @@ function my_func( } !@#$%codegentest:testFormattingFullyGeneratedFileWithOptions -> + * @-generated SignedSource<> */ function my_func( @@ -103,11 +99,10 @@ function my_func( } !@#$%codegentest:testFormattingFullyGeneratedFileWithTabs -> + * @-generated SignedSource<> */ function my_func( @@ -118,12 +113,11 @@ function my_func( } !@#$%codegentest:testFormattingPartiallyGeneratedFile -> + * @-partially-generated SignedSource<<3a7ef07d733ea4f3aa8c9d947d64c28e>> */ function my_func( @@ -139,7 +133,6 @@ function my_func( } !@#$%codegentest:testFormattingUnsignedFile -> + * @-generated SignedSource<<4c9d48e406a74cd17064ea2ebcd0492f>> */ @@ -167,12 +159,11 @@ enum TestEnum : int { } !@#$%codegentest:testGenerateTopLevelFunctions -> + * @-generated SignedSource<> */ function fun(): int { @@ -180,12 +171,11 @@ function fun(): int { } !@#$%codegentest:testNamespace -> + * @-generated SignedSource<<1e82f55cc7df937c367b4936f57d25b3>> */ namespace MyNamespace; use namespace Another\Space; @@ -197,7 +187,6 @@ class Foo { } !@#$%codegentest:testNoSignature -> + * @-partially-generated SignedSource<<4ac3ec3e124eaa6cf732ab42f6ecbc49>> */ class PartiallyGenerated { @@ -248,14 +236,13 @@ class Foo { } !@#$%codegentest:testSaveAutogenerated -> + * @-generated SignedSource<<624100da9e208d6abeaa301bce015bfd>> */ class Demo { @@ -266,7 +253,6 @@ class Demo { } !@#$%codegentest:testSavePartiallyGenerated -> + * @-partially-generated SignedSource<> */ class Demo { @@ -287,12 +273,11 @@ class Demo { } !@#$%codegentest:testStrictFile -> + * @-generated SignedSource<> */ class Foo { diff --git a/tests/CodegenFileTest.hack b/tests/CodegenFileTest.hack index d6246898..de13e732 100644 --- a/tests/CodegenFileTest.hack +++ b/tests/CodegenFileTest.hack @@ -11,6 +11,8 @@ namespace Facebook\HackCodegen; use type Facebook\HackCodegen\_Private\Filesystem; use namespace HH\Lib\Str; +use type Facebook\HackTest\DataProvider; +use function Facebook\FBExpect\expect; final class CodegenFileTest extends CodegenBaseTest { public function testAutogenerated(): void { @@ -47,10 +49,12 @@ final class CodegenFileTest extends CodegenBaseTest { $enum = $cgf->codegenEnum('TestEnum', 'int') ->addMember( $cgf->codegenEnumMember('FIRST') - ->setValue(0, HackBuilderValues::export())) + ->setValue(0, HackBuilderValues::export()), + ) ->addMember( $cgf->codegenEnumMember('SECOND') - ->setValue(1, HackBuilderValues::export())); + ->setValue(1, HackBuilderValues::export()), + ); $code = $cgf->codegenFile('no_file')->addEnum($enum)->render(); expect_with_context(static::class, $code)->toBeUnchanged(); @@ -144,7 +148,8 @@ final class CodegenFileTest extends CodegenBaseTest { public function testSaveAutogenerated(): void { $fname = $this->saveAutogeneratedFile(); - expect_with_context(static::class, Filesystem::readFile($fname))->toBeUnchanged(); + expect_with_context(static::class, Filesystem::readFile($fname)) + ->toBeUnchanged(); } public function testClobberManuallyWrittenCode(): void { @@ -175,9 +180,8 @@ final class CodegenFileTest extends CodegenBaseTest { $fname = $this->savePartiallyGeneratedFile(); $content = Filesystem::readFile($fname); expect_with_context(static::class, $content)->toBeUnchanged(); - expect( - PartiallyGeneratedSignedSource::hasValidSignature($content), - )->toBeTrue(); + expect(PartiallyGeneratedSignedSource::hasValidSignature($content)) + ->toBeTrue(); } public function testReSavePartiallyGenerated(): void { @@ -317,7 +321,7 @@ final class CodegenFileTest extends CodegenBaseTest { $cgf->codegenFunction('main') ->setReturnType('noreturn') ->addEmptyUserAttribute('__EntryPoint') - ->setBody('exit(0);') + ->setBody('exit(0);'), ) ->render(); expect_with_context(static::class, $code)->toBeUnchanged(); @@ -372,20 +376,18 @@ final class CodegenFileTest extends CodegenBaseTest { ->render(); expect_with_context(static::class, $code)->toBeUnchanged(); - expect( - SignedSourceBase::hasValidSignatureFromAnySigner($code) - )->toBeTrue('bad signed source'); - expect( - Str\ends_with($code, "\n") - )->toBeTrue('Should end with newline'); - expect( - Str\ends_with($code, "\n\n") - )->toBeFalse('Should end with one newline, not multiple'); + expect(SignedSourceBase::hasValidSignatureFromAnySigner($code))->toBeTrue( + 'bad signed source', + ); + expect(Str\ends_with($code, "\n"))->toBeTrue('Should end with newline'); + expect(Str\ends_with($code, "\n\n"))->toBeFalse( + 'Should end with one newline, not multiple', + ); $lines = Str\split($code, "\n"); - expect( - Str\starts_with($lines[8], ' ') - )->toBeTrue('use spaces instead of tabs'); + expect(Str\starts_with($lines[8], ' '))->toBeTrue( + 'use spaces instead of tabs', + ); } public function testFormattingFullyGeneratedFileWithTabs(): void { @@ -410,14 +412,14 @@ final class CodegenFileTest extends CodegenBaseTest { ) ->render(); expect_with_context(static::class, $code)->toBeUnchanged(); - expect( - SignedSourceBase::hasValidSignatureFromAnySigner($code) - )->toBeTrue('bad signed source'); + expect(SignedSourceBase::hasValidSignatureFromAnySigner($code))->toBeTrue( + 'bad signed source', + ); $lines = Str\split($code, "\n"); - expect( - Str\starts_with($lines[9], "\t") - )->toBeTrue('use tabs instead of spaces'); + expect(Str\starts_with($lines[8], "\t"))->toBeTrue( + 'use tabs instead of spaces', + ); } public function testFormattingFullyGeneratedFileWithOptions(): void { @@ -442,14 +444,14 @@ final class CodegenFileTest extends CodegenBaseTest { ) ->render(); expect_with_context(static::class, $code)->toBeUnchanged(); - expect( - SignedSourceBase::hasValidSignatureFromAnySigner($code) - )->toBeTrue('bad signed source'); + expect(SignedSourceBase::hasValidSignatureFromAnySigner($code))->toBeTrue( + 'bad signed source', + ); $lines = Str\split($code, "\n"); - expect( - Str\starts_with($lines[9], "\t") - )->toBeTrue('use tabs instead of spaces'); + expect(Str\starts_with($lines[8], "\t"))->toBeTrue( + 'use tabs instead of spaces', + ); } public function testFormattingUnsignedFile(): void { @@ -481,9 +483,9 @@ final class CodegenFileTest extends CodegenBaseTest { ) ->render(); expect_with_context(static::class, $code)->toBeUnchanged(); - expect( - SignedSourceBase::hasValidSignatureFromAnySigner($code) - )->toBeFalse('file should be unsigned, but has valid signature'); + expect(SignedSourceBase::hasValidSignatureFromAnySigner($code))->toBeFalse( + 'file should be unsigned, but has valid signature', + ); } public function testFormattingPartiallyGeneratedFile(): void { @@ -516,9 +518,9 @@ final class CodegenFileTest extends CodegenBaseTest { ) ->render(); expect_with_context(static::class, $code)->toBeUnchanged(); - expect( - SignedSourceBase::hasValidSignatureFromAnySigner($code) - )->toBeTrue('bad signed source'); + expect(SignedSourceBase::hasValidSignatureFromAnySigner($code))->toBeTrue( + 'bad signed source', + ); } public function testConstants(): void { @@ -541,6 +543,33 @@ final class CodegenFileTest extends CodegenBaseTest { ->render(); expect_with_context(static::class, $code)->toBeUnchanged(); } + + public function provideFileNamesWithExpectedModes( + )[]: dict { + return dict[ + 'Legacy extensions, such as `.php` trigger // strict mode.' => + tuple('filename.php', CodegenFileType::HACK_STRICT), + 'Legacy extensions, such as .hh trigger // strict mode.' => + tuple('filename.hh', CodegenFileType::HACK_STRICT), + 'Just guessing, but .hhi files need the + tuple('filename.hhi', CodegenFileType::HACK_STRICT), + 'Dot hack implies there is no need for a + tuple('filename.hack', CodegenFileType::DOT_HACK), + // https://hhvm.com/blog/2021/10/26/hhvm-4.133.html + 'As of hhvm 4.133 all files without a recognized extensions are treated as .hack' => + tuple('filename', CodegenFileType::DOT_HACK), + ]; + } + + <> + public function testFileModeInference( + string $filename, + CodegenFileType $mode, + ): void { + $cfg = $this->getCodegenFactory(); + $file = $cfg->codegenFile($filename); + expect($file->getFileType())->toEqual($mode); + } } final class TestTabbedCodegenConfig implements IHackCodegenConfig { diff --git a/tests/RefactorCodegenTest.codegen b/tests/RefactorCodegenTest.codegen index 1f29b94a..607b402b 100644 --- a/tests/RefactorCodegenTest.codegen +++ b/tests/RefactorCodegenTest.codegen @@ -1,12 +1,11 @@ @generated !@#$%codegentest:testClassRename -> + * @-partially-generated SignedSource<<4b7ebf80bdd2f28f2b76ca99439f57dd>> */ class NewClass { @@ -21,13 +20,12 @@ class NewClass { } !@#$%codegentest:testManualSectionMerge -> + * @-partially-generated SignedSource<<11e6ceb684e1300fab7f316d289dadc1>> */ class NewClass {