From d04e34a2573bf97a5fbba7c361d59c2f31192cdd Mon Sep 17 00:00:00 2001 From: Lexidor Digital <31805625+lexidor@users.noreply.github.com> Date: Wed, 24 May 2023 18:56:50 +0200 Subject: [PATCH 1/7] Remove references to varray, darray, and varray_or_darray from src/ Update only those tests that fail the typechecker. Rest of the tests will be fixed in the next commit. --- src/TypeSpec.hack | 17 ---- src/TypeSpec/__Private/DarraySpec.hack | 76 ------------------ src/TypeSpec/__Private/ShapeSpec.hack | 26 +++--- src/TypeSpec/__Private/TupleSpec.hack | 17 ++-- src/TypeSpec/__Private/UntypedArraySpec.hack | 49 ------------ .../__Private/VArrayOrDArraySpec.hack | 60 -------------- src/TypeSpec/__Private/VarraySpec.hack | 80 ------------------- .../__Private/from_type_structure.hack | 15 ++-- tests/DArraySpecTest.hack | 12 +-- tests/VArrayOrDArraySpecTest.hack | 80 ------------------- tests/VArraySpecTest.hack | 6 +- 11 files changed, 25 insertions(+), 413 deletions(-) delete mode 100644 src/TypeSpec/__Private/DarraySpec.hack delete mode 100644 src/TypeSpec/__Private/UntypedArraySpec.hack delete mode 100644 src/TypeSpec/__Private/VArrayOrDArraySpec.hack delete mode 100644 src/TypeSpec/__Private/VarraySpec.hack delete mode 100644 tests/VArrayOrDArraySpecTest.hack diff --git a/src/TypeSpec.hack b/src/TypeSpec.hack index b4b7ac3..8599871 100644 --- a/src/TypeSpec.hack +++ b/src/TypeSpec.hack @@ -59,13 +59,6 @@ function constvector(TypeSpec $inner): TypeSpec<\ConstVector> { return new __Private\VectorSpec(\ConstVector::class, $inner); } -function darray( - TypeSpec $tsk, - TypeSpec $tsv, -): TypeSpec> { - return new __Private\DarraySpec($tsk, $tsv); -} - function dict( TypeSpec $tsk, TypeSpec $tsv, @@ -147,10 +140,6 @@ function string(): TypeSpec { return new __Private\StringSpec(); } -function varray(TypeSpec $tsv): TypeSpec> { - return new __Private\VarraySpec($tsv); -} - function vec(TypeSpec $inner): TypeSpec> { return new __Private\VecSpec($inner); } @@ -159,12 +148,6 @@ function vector(TypeSpec $inner): TypeSpec> { return new __Private\VectorSpec(Vector::class, $inner); } -function varray_or_darray( - TypeSpec $inner, -): TypeSpec> { - return new __Private\VArrayOrDArraySpec($inner); -} - function of(): TypeSpec { return __Private\from_type_structure( \HH\ReifiedGenerics\get_type_structure(), diff --git a/src/TypeSpec/__Private/DarraySpec.hack b/src/TypeSpec/__Private/DarraySpec.hack deleted file mode 100644 index 868900f..0000000 --- a/src/TypeSpec/__Private/DarraySpec.hack +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2016, Fred Emmott - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - */ - -namespace Facebook\TypeSpec\__Private; - -use type Facebook\TypeAssert\{IncorrectTypeException, TypeCoercionException}; -use type Facebook\TypeSpec\TypeSpec; - -use namespace HH\Lib\{Dict, Str}; - -final class DarraySpec - extends TypeSpec> { - - public function __construct( - private TypeSpec $tsk, - private TypeSpec $tsv, - ) { - } - - <<__Override>> - public function coerceType(mixed $value): darray { - if (!$value is KeyedTraversable<_, _>) { - throw TypeCoercionException::withValue( - $this->getTrace(), - 'darray', - $value, - ); - } - - $kt = $this->getTrace()->withFrame('darray'); - $vt = $this->getTrace()->withFrame('darray<_, Tv>'); - - return Dict\pull_with_key( - $value, - ($_k, $v) ==> $this->tsv->withTrace($vt)->coerceType($v), - ($k, $_v) ==> $this->tsk->withTrace($kt)->coerceType($k), - ) |> darray($$); - } - - <<__Override>> - public function assertType(mixed $value): darray { - if (/* HH_FIXME[2049] */ /* HH_FIXME[4107] */ !is_darray($value)) { - throw IncorrectTypeException::withValue( - $this->getTrace(), - $this->toString(), - $value, - ); - } - - $kt = $this->getTrace()->withFrame('darray'); - $vt = $this->getTrace()->withFrame('darray<_, Tv>'); - - return Dict\pull_with_key( - $value as KeyedTraversable<_, _>, - ($_k, $v) ==> $this->tsv->withTrace($vt)->assertType($v), - ($k, $_v) ==> $this->tsk->withTrace($kt)->assertType($k), - ) - |> darray($$); - } - - <<__Override>> - public function toString(): string { - return Str\format( - 'darray<%s, %s>', - $this->tsk->toString(), - $this->tsv->toString(), - ); - } -} diff --git a/src/TypeSpec/__Private/ShapeSpec.hack b/src/TypeSpec/__Private/ShapeSpec.hack index ed44aaa..a93d46f 100644 --- a/src/TypeSpec/__Private/ShapeSpec.hack +++ b/src/TypeSpec/__Private/ShapeSpec.hack @@ -31,11 +31,8 @@ final class ShapeSpec extends TypeSpec { <<__Override>> public function coerceType(mixed $value): shape() { if (!$value is KeyedTraversable<_, _>) { - throw TypeCoercionException::withValue( - $this->getTrace(), - 'shape', - $value, - ); + throw + TypeCoercionException::withValue($this->getTrace(), 'shape', $value); } $value = dict(/* HH_IGNORE_ERROR[4323] */$value); @@ -67,18 +64,16 @@ final class ShapeSpec extends TypeSpec { <<__Override>> public function assertType(mixed $value): shape() { - if (!\HH\is_dict_or_darray($value)) { - throw IncorrectTypeException::withValue( - $this->getTrace(), - 'shape', - $value, - ); + if (!$value is dict<_, _>) { + throw + IncorrectTypeException::withValue($this->getTrace(), 'shape', $value); } $out = dict[]; foreach ($this->inners as $key => $spec) { $trace = $this->getTrace()->withFrame('shape['.$key.']'); if (C\contains_key($value, $key)) { - $out[$key as arraykey] = $spec->withTrace($trace)->assertType($value[$key as dynamic] ?? null); + $out[$key as arraykey] = + $spec->withTrace($trace)->assertType($value[$key as dynamic] ?? null); continue; } @@ -112,11 +107,8 @@ final class ShapeSpec extends TypeSpec { private static function dictToShapeUNSAFE( dict $shape, ): shape() { - if (shape() is dict<_, _>) { - /* HH_IGNORE_ERROR[4110] */ - return $shape; - } - return /* HH_IGNORE_ERROR[4110] */ darray($shape); + /* HH_IGNORE_ERROR[4110] */ + return $shape; } <<__Override>> diff --git a/src/TypeSpec/__Private/TupleSpec.hack b/src/TypeSpec/__Private/TupleSpec.hack index c296d7e..e9f4b10 100644 --- a/src/TypeSpec/__Private/TupleSpec.hack +++ b/src/TypeSpec/__Private/TupleSpec.hack @@ -22,7 +22,7 @@ final class TupleSpec extends TypeSpec { <<__Override>> public function coerceType(mixed $value): BogusTuple { - if (!\HH\is_vec_or_varray($value)) { + if (!$value is vec<_>) { throw TypeCoercionException::withValue($this->getTrace(), 'tuple', $value); } @@ -48,15 +48,12 @@ final class TupleSpec extends TypeSpec { <<__Override>> public function assertType(mixed $value): BogusTuple { - if (\HH\is_vec_or_varray($value)) { - $value = vec($value); - } else if (!($value is vec<_>)) { + if (!$value is vec<_>) { throw IncorrectTypeException::withValue($this->getTrace(), 'tuple', $value); } - $values = $value; - $count = \count($values); + $count = \count($value); if ($count !== \count($this->inners)) { throw IncorrectTypeException::withValue( $this->getTrace(), @@ -69,18 +66,14 @@ final class TupleSpec extends TypeSpec { for ($i = 0; $i < $count; ++$i) { $out[] = $this->inners[$i] ->withTrace($this->getTrace()->withFrame('tuple['.$i.']')) - ->assertType($values[$i]); + ->assertType($value[$i]); } return self::vecToTuple($out); } private static function vecToTuple(vec $tuple): BogusTuple { - if (tuple('foo') is vec<_>) { - /* HH_IGNORE_ERROR[4110] */ - return $tuple; - } /* HH_IGNORE_ERROR[4110] */ - return varray($tuple); + return $tuple; } <<__Override>> diff --git a/src/TypeSpec/__Private/UntypedArraySpec.hack b/src/TypeSpec/__Private/UntypedArraySpec.hack deleted file mode 100644 index a0c8e0d..0000000 --- a/src/TypeSpec/__Private/UntypedArraySpec.hack +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2016, Fred Emmott - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - */ - -namespace Facebook\TypeSpec\__Private; - -use type Facebook\TypeAssert\{IncorrectTypeException, TypeCoercionException}; -use type Facebook\TypeSpec\TypeSpec; - -final class UntypedArraySpec extends TypeSpec> { - - <<__Override>> - public function coerceType(mixed $value): varray_or_darray { - if (!$value is KeyedTraversable<_, _>) { - throw - TypeCoercionException::withValue($this->getTrace(), 'array', $value); - } - - $out = darray[]; - foreach ($value as $k => $v) { - $out[$k as arraykey] = $v; - } - return $out; - } - - <<__Override>> - public function assertType(mixed $value): varray_or_darray { - if (!\HH\is_php_array($value)) { - throw - IncorrectTypeException::withValue($this->getTrace(), 'array', $value); - } - - if (varray($value) === $value) { - return varray($value); - } - return darray($value); - } - - <<__Override>> - public function toString(): string { - return 'varray_or_darray'; - } -} diff --git a/src/TypeSpec/__Private/VArrayOrDArraySpec.hack b/src/TypeSpec/__Private/VArrayOrDArraySpec.hack deleted file mode 100644 index 134774b..0000000 --- a/src/TypeSpec/__Private/VArrayOrDArraySpec.hack +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2016, Fred Emmott - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - */ - -namespace Facebook\TypeSpec\__Private; - -use namespace HH\Lib\{C, Vec}; -use namespace Facebook\TypeSpec; - -final class VArrayOrDArraySpec extends UnionSpec> { - private TypeSpec\TypeSpec> $darraySpec; - private TypeSpec\TypeSpec> $varraySpec; - - public function __construct(private TypeSpec\TypeSpec $inner) { - $this->darraySpec = TypeSpec\darray(TypeSpec\arraykey(), $inner); - $this->varraySpec = TypeSpec\varray($inner); - parent::__construct( - 'varray_or_darray', - $this->darraySpec, - $this->varraySpec, - ); - } - - <<__Override>> - public function toString(): string { - return 'varray_or_darray<'.$this->inner->toString().'>'; - } - - <<__Override>> - public function coerceType(mixed $value): varray_or_darray { - try { - return $this->assertType($value); - } catch (\Throwable $_) { - } - - if ($value is vec<_> || $value is /* HH_FIXME[2049] */ ConstVector<_>) { - return $this->varraySpec->coerceType($value); - } - - if ($value is dict<_, _> || $value is /* HH_FIXME[2049] */ ConstMap<_, _>) { - return $this->darraySpec->coerceType($value); - } - - $new = $this->darraySpec->coerceType($value); - if ($new === darray[]) { - return $new; - } - - if (Vec\keys($new) === Vec\range(0, C\count($new) - 1)) { - return varray($new); - } - return $new; - } -} diff --git a/src/TypeSpec/__Private/VarraySpec.hack b/src/TypeSpec/__Private/VarraySpec.hack deleted file mode 100644 index 9bb960e..0000000 --- a/src/TypeSpec/__Private/VarraySpec.hack +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2016, Fred Emmott - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - */ - -namespace Facebook\TypeSpec\__Private; - -use type Facebook\TypeAssert\{IncorrectTypeException, TypeCoercionException}; -use type Facebook\TypeSpec\TypeSpec; -use namespace HH\Lib\Vec; - -final class VarraySpec extends TypeSpec> { - public function __construct( - private TypeSpec $inner, - ) { - } - - <<__Override>> - public function coerceType(mixed $value): varray { - if (!$value is Traversable<_>) { - throw TypeCoercionException::withValue( - $this->getTrace(), - 'varray', - $value, - ); - } - - return Vec\map($value, $inner ==> $this->inner->coerceType($inner)) - |> varray($$); - } - - <<__Override>> - public function assertType(mixed $value): varray { - if (/* HH_FIXME[2049] */ /* HH_FIXME[4107] */ !is_varray($value)) { - throw IncorrectTypeException::withValue( - $this->getTrace(), - 'varray', - $value, - ); - } - - $counter = ( - (): \Generator ==> { - for ($i = 0; true; $i++) { - yield $i; - } - } - )(); - - return Vec\map_with_key( - $value as KeyedTraversable<_, _>, - ($k, $inner) ==> { - $counter->next(); - $i = $counter->current(); - if ($k !== $i) { - throw IncorrectTypeException::withValue( - $this->getTrace(), - 'key '.$i, - $k, - ); - } - return $this - ->inner - ->withTrace($this->getTrace()->withFrame('varray['.$i.']')) - ->assertType($inner); - }, - ) - |> varray($$); - } - - <<__Override>> - public function toString(): string { - return 'varray<'.$this->inner->toString().'>'; - } -} diff --git a/src/TypeSpec/__Private/from_type_structure.hack b/src/TypeSpec/__Private/from_type_structure.hack index 34a992f..8e1717f 100644 --- a/src/TypeSpec/__Private/from_type_structure.hack +++ b/src/TypeSpec/__Private/from_type_structure.hack @@ -73,25 +73,20 @@ function from_type_structure(TypeStructure $ts): TypeSpec { case TypeStructureKind::OF_VARRAY: $generics = $ts['generic_types'] as nonnull; invariant(C\count($generics) === 1, 'got varray with multiple generics'); + // When given a legacy varray<_> type, we can return a vec<_> spec instead. /* HH_IGNORE_ERROR[4110] */ - return TypeSpec\varray(from_type_structure($generics[0])); + return TypeSpec\vec(from_type_structure($generics[0])); case TypeStructureKind::OF_DARRAY: $generics = $ts['generic_types'] as nonnull; invariant( C\count($generics) === 2, 'darrays must have exactly 2 generics', ); + // When given a legacy darray<_, _> type, we can return a dict<_, _> spec instead. /* HH_IGNORE_ERROR[4110] */ - return TypeSpec\darray(from_type_structure($generics[0]), from_type_structure($generics[1])); + return TypeSpec\dict(from_type_structure($generics[0]), from_type_structure($generics[1])); case TypeStructureKind::OF_VARRAY_OR_DARRAY: - $generics = $ts['generic_types'] as nonnull; - invariant( - C\count($generics) === 1, - 'got varray_or_darray with multiple generics', - ); - /* HH_IGNORE_ERROR[4110] */ - return TypeSpec\varray_or_darray(from_type_structure($generics[0])); - + throw new UnsupportedTypeException('OF_VARRAY_OR_DARRAY'); case TypeStructureKind::OF_DICT: $generics = TypeAssert\not_null($ts['generic_types']); invariant(C\count($generics) === 2, 'dicts must have 2 generics'); diff --git a/tests/DArraySpecTest.hack b/tests/DArraySpecTest.hack index bf1615e..b3afee0 100644 --- a/tests/DArraySpecTest.hack +++ b/tests/DArraySpecTest.hack @@ -16,7 +16,7 @@ use type Facebook\TypeSpec\TypeSpec; final class DArraySpecTest extends TypeSpecTest> { <<__Override>> public function getTypeSpec(): TypeSpec> { - return TypeSpec\darray(TypeSpec\arraykey(), TypeSpec\int()); + return TypeSpec\of>(); } <<__Override>> @@ -48,14 +48,8 @@ final class DArraySpecTest extends TypeSpecTest> { public function getToStringExamples( ): vec<(TypeSpec>, string)> { return vec[ - tuple( - TypeSpec\darray(TypeSpec\string(), TypeSpec\int()), - 'darray', - ), - tuple( - TypeSpec\darray(TypeSpec\int(), TypeSpec\string()), - 'darray', - ), + tuple(TypeSpec\of>(), dict::class.''), + tuple(TypeSpec\of>(), dict::class.''), ]; } } diff --git a/tests/VArrayOrDArraySpecTest.hack b/tests/VArrayOrDArraySpecTest.hack deleted file mode 100644 index 207038b..0000000 --- a/tests/VArrayOrDArraySpecTest.hack +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (c) 2016, Fred Emmott - * Copyright (c) 2017-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - * - */ - -namespace Facebook\TypeAssert; - -use namespace Facebook\TypeSpec; -use type Facebook\TypeSpec\TypeSpec; -use function Facebook\FBExpect\expect; - -final class VArrayOrDArraySpecTest - extends TypeSpecTest> { - <<__Override>> - public function getTypeSpec(): TypeSpec> { - return TypeSpec\varray_or_darray(TypeSpec\int()); - } - - <<__Override>> - public function getValidCoercions(): vec<(mixed, varray_or_darray)> { - return vec[ - tuple(vec[], varray[]), - tuple(dict[], darray[]), - tuple(keyset[], darray[]), - tuple(vec['123'], varray[123]), - tuple(varray['123'], varray[123]), - tuple(varray[123], varray[123]), - tuple(dict['foo' => '456'], darray['foo' => 456]), - tuple(Vector {123}, varray[123]), - tuple(Pair {123, 456}, varray[123, 456]), - tuple(darray['foo' => 123], darray['foo' => 123]), - tuple(keyset['123'], darray['123' => 123]), - ]; - } - - <<__Override>> - public function getInvalidCoercions(): vec<(mixed)> { - return vec[ - tuple(false), - tuple(123), - tuple(varray['foo']), - tuple(vec['foo']), - tuple(keyset['foo']), - tuple(darray[123 => 'foo']), - ]; - } - - <<__Override>> - public function getToStringExamples( - ): vec<(TypeSpec>, string)> { - return vec[ - tuple( - TypeSpec\varray_or_darray(TypeSpec\string()), - 'varray_or_darray', - ), - tuple(TypeSpec\varray_or_darray(TypeSpec\int()), 'varray_or_darray'), - ]; - } - - public function testSubtyping(): void { - $varray = ( - (): varray ==> - TypeSpec\varray(TypeSpec\int())->assertType(varray[123]) - )(); - $darray = ( - (): darray ==> - TypeSpec\darray(TypeSpec\arraykey(), TypeSpec\int()) - ->assertType(darray['foo' => 123]) - )(); - $widened_varray = ((): varray_or_darray ==> $varray)(); - $widened_darray = ((): varray_or_darray ==> $darray)(); - expect($widened_varray)->toEqual($varray); - expect($widened_darray)->toEqual($darray); - } -} diff --git a/tests/VArraySpecTest.hack b/tests/VArraySpecTest.hack index 884fb59..b663750 100644 --- a/tests/VArraySpecTest.hack +++ b/tests/VArraySpecTest.hack @@ -16,7 +16,7 @@ use type Facebook\TypeSpec\TypeSpec; final class VArraySpecTest extends TypeSpecTest> { <<__Override>> public function getTypeSpec(): TypeSpec> { - return TypeSpec\varray(TypeSpec\int()); + return TypeSpec\of>(); } <<__Override>> @@ -48,8 +48,8 @@ final class VArraySpecTest extends TypeSpecTest> { public function getToStringExamples( ): vec<(TypeSpec>, string)> { return vec[ - tuple(TypeSpec\varray(TypeSpec\string()), 'varray'), - tuple(TypeSpec\varray(TypeSpec\int()), 'varray'), + tuple(TypeSpec\of>(), vec::class.''), + tuple(TypeSpec\of>(), vec::class.''), ]; } } From fc0bdc2d1f466333143c4703c7dd4655810bb8d0 Mon Sep 17 00:00:00 2001 From: Lexidor Digital <31805625+lexidor@users.noreply.github.com> Date: Wed, 24 May 2023 19:50:26 +0200 Subject: [PATCH 2/7] Update tests to reflect removal of varray, darray, and varray_or_darray --- tests/ConstMapSpecTest.hack | 4 +-- tests/ConstVectorSpecTest.hack | 4 +-- tests/DArraySpecTest.hack | 31 +++++++++++--------- tests/DictSpecTest.hack | 4 +-- tests/ReifiedGenericsTest.hack | 7 +---- tests/ShapeSpecTest.hack | 10 +++---- tests/TypeStructureTest.hack | 48 ++++++++++++------------------- tests/VArraySpecTest.hack | 31 +++++++++++--------- tests/VecSpecTest.hack | 6 ++-- tests/fixtures/TypeConstants.hack | 7 ++--- 10 files changed, 70 insertions(+), 82 deletions(-) diff --git a/tests/ConstMapSpecTest.hack b/tests/ConstMapSpecTest.hack index 99e0244..ba5f7bd 100644 --- a/tests/ConstMapSpecTest.hack +++ b/tests/ConstMapSpecTest.hack @@ -29,8 +29,8 @@ final class ConstMapSpecTest extends TypeSpecTest<\ConstMap> { tuple(vec[123], ImmMap {0 => 123}), tuple(vec['123'], ImmMap {0 => 123}), tuple(keyset['123'], ImmMap {'123' => 123}), - tuple(varray[123], ImmMap {0 => 123}), - tuple(darray['123' => 123], ImmMap {'123' => 123}), + tuple(vec[123], ImmMap {0 => 123}), + tuple(dict['123' => 123], ImmMap {'123' => 123}), ]; } diff --git a/tests/ConstVectorSpecTest.hack b/tests/ConstVectorSpecTest.hack index 2148f14..b50999e 100644 --- a/tests/ConstVectorSpecTest.hack +++ b/tests/ConstVectorSpecTest.hack @@ -24,8 +24,8 @@ final class ConstVectorSpecTest extends TypeSpecTest<\ConstVector> { return vec[ tuple(vec[], ImmVector {}), tuple(vec['123'], ImmVector {123}), - tuple(varray['123'], ImmVector {123}), - tuple(darray[123 => '123'], ImmVector {123}), + tuple(vec['123'], ImmVector {123}), + tuple(dict[123 => '123'], ImmVector {123}), tuple(ImmVector {'123'}, ImmVector {123}), tuple(Vector {'123'}, ImmVector {123}), tuple(Vector {123}, Vector {123}), diff --git a/tests/DArraySpecTest.hack b/tests/DArraySpecTest.hack index b3afee0..8511f33 100644 --- a/tests/DArraySpecTest.hack +++ b/tests/DArraySpecTest.hack @@ -13,25 +13,28 @@ namespace Facebook\TypeAssert; use namespace Facebook\TypeSpec; use type Facebook\TypeSpec\TypeSpec; -final class DArraySpecTest extends TypeSpecTest> { +// This type is a misnomer (now). +// DarraySpecTest tests the behavior of `TypeSpec\of>()`, +// but the type under test is `DictSpec<_, _>`. +final class DArraySpecTest extends TypeSpecTest> { <<__Override>> - public function getTypeSpec(): TypeSpec> { + public function getTypeSpec(): TypeSpec> { return TypeSpec\of>(); } <<__Override>> - public function getValidCoercions(): vec<(mixed, darray)> { + public function getValidCoercions(): vec<(mixed, dict)> { return vec[ - tuple(Map {'foo' => 123}, darray['foo' => 123]), - tuple(ImmMap {'foo' => 123}, darray['foo' => 123]), - tuple(dict['foo' => 123], darray['foo' => 123]), - tuple(dict[], darray[]), - tuple(vec[123], darray[0 => 123]), - tuple(vec['123'], darray[0 => 123]), - tuple(keyset['123'], darray['123' => 123]), - tuple(keyset[123], darray[123 => 123]), - tuple(varray[123], darray[0 => 123]), - tuple(darray['123' => 123], darray['123' => 123]), + tuple(Map {'foo' => 123}, dict['foo' => 123]), + tuple(ImmMap {'foo' => 123}, dict['foo' => 123]), + tuple(dict['foo' => 123], dict['foo' => 123]), + tuple(dict[], dict[]), + tuple(vec[123], dict[0 => 123]), + tuple(vec['123'], dict[0 => 123]), + tuple(keyset['123'], dict['123' => 123]), + tuple(keyset[123], dict[123 => 123]), + tuple(vec[123], dict[0 => 123]), + tuple(dict['123' => 123], dict['123' => 123]), ]; } @@ -46,7 +49,7 @@ final class DArraySpecTest extends TypeSpecTest> { <<__Override>> public function getToStringExamples( - ): vec<(TypeSpec>, string)> { + ): vec<(TypeSpec>, string)> { return vec[ tuple(TypeSpec\of>(), dict::class.''), tuple(TypeSpec\of>(), dict::class.''), diff --git a/tests/DictSpecTest.hack b/tests/DictSpecTest.hack index 793fd08..5a4a70d 100644 --- a/tests/DictSpecTest.hack +++ b/tests/DictSpecTest.hack @@ -30,8 +30,8 @@ final class DictSpecTest extends TypeSpecTest> { tuple(vec['123'], dict[0 => 123]), tuple(keyset['123'], dict['123' => 123]), tuple(keyset[123], dict[123 => 123]), - tuple(varray[123], dict[0 => 123]), - tuple(darray['123' => 123], dict['123' => 123]), + tuple(vec[123], dict[0 => 123]), + tuple(dict['123' => 123], dict['123' => 123]), ]; } diff --git a/tests/ReifiedGenericsTest.hack b/tests/ReifiedGenericsTest.hack index 2d10afb..0c7d005 100644 --- a/tests/ReifiedGenericsTest.hack +++ b/tests/ReifiedGenericsTest.hack @@ -39,14 +39,9 @@ final class ReifiedGenericsTest extends \Facebook\HackTest\HackTest { public function testShapeOfVecAndDicts(): void { $valid = shape('vec' => vec['foo', 'bar'], 'dict' => dict[1 => 2]); - $coercable = dict['vec' => vec['foo', 'bar'], 'dict' => darray[1 => 2]]; + $coercable = dict['vec' => vec['foo', 'bar'], 'dict' => dict[1 => 2]]; expect(matches($valid))->toBeSame($valid); - if (darray[] !== dict[]) { - // They are equivalent as of v4.103 - expect(() ==> matches($coercable)) - ->toThrow(IncorrectTypeException::class); - } expect(TypeCoerce\match($coercable)) ->toBeSame($valid); expect(() ==> TypeCoerce\match('hello')) diff --git a/tests/ShapeSpecTest.hack b/tests/ShapeSpecTest.hack index a8c49c2..51955ec 100644 --- a/tests/ShapeSpecTest.hack +++ b/tests/ShapeSpecTest.hack @@ -46,7 +46,7 @@ final class ShapeSpecTest extends TypeSpecTest { public function getValidCoercions(): vec<(mixed, shape(...))> { return vec[ tuple( - darray['string_field' => 'foo', 'nullable_string_field' => null], + dict['string_field' => 'foo', 'nullable_string_field' => null], shape('string_field' => 'foo', 'nullable_string_field' => null), ), tuple( @@ -54,11 +54,11 @@ final class ShapeSpecTest extends TypeSpecTest { shape('string_field' => 'foo', 'nullable_string_field' => null), ), tuple( - darray['string_field' => 123, 'nullable_string_field' => 'bar'], + dict['string_field' => 123, 'nullable_string_field' => 'bar'], shape('string_field' => '123', 'nullable_string_field' => 'bar'), ), tuple( - darray[ + dict[ 'string_field' => 123, 'nullable_string_field' => 'bar', 'optional_string_field' => 123, @@ -70,7 +70,7 @@ final class ShapeSpecTest extends TypeSpecTest { ), ), tuple( - darray[ + dict[ 'string_field' => 123, 'nullable_string_field' => 'bar', 'optional_nullable_string_field' => 123, @@ -82,7 +82,7 @@ final class ShapeSpecTest extends TypeSpecTest { ), ), tuple( - darray[ + dict[ 'string_field' => 123, 'nullable_string_field' => 'bar', 'optional_nullable_string_field' => null, diff --git a/tests/TypeStructureTest.hack b/tests/TypeStructureTest.hack index 37eb413..4bf2458 100644 --- a/tests/TypeStructureTest.hack +++ b/tests/TypeStructureTest.hack @@ -36,13 +36,13 @@ final class TypeStructureTest extends \Facebook\HackTest\HackTest { type_structure(TypeConstants::class, 'TTuple'), tuple('foo', 123), ), - 'varray' => tuple( - type_structure(TypeConstants::class, 'TStringVArray'), - varray['123', '456'], + 'vec' => tuple( + type_structure(TypeConstants::class, 'TStringVec'), + vec['123', '456'], ), - 'darray' => tuple( - type_structure(TypeConstants::class, 'TStringStringDArray'), - darray['foo' => 'bar', 'herp' => 'derp'], + 'dict' => tuple( + type_structure(TypeConstants::class, 'TStringStringDict'), + dict['foo' => 'bar', 'herp' => 'derp'], ), 'string as ?string' => tuple( type_structure(TypeConstants::class, 'TNullableString'), @@ -66,7 +66,7 @@ final class TypeStructureTest extends \Facebook\HackTest\HackTest { ), 'array as Container' => tuple( type_structure(TypeConstants::class, 'TIntContainer'), - varray[123, 456], + vec[123, 456], ), 'Container' => tuple( type_structure(TypeConstants::class, 'TIntContainer'), @@ -80,9 +80,9 @@ final class TypeStructureTest extends \Facebook\HackTest\HackTest { type_structure(TypeConstants::class, 'TStringIntKeyedContainer'), Map {'foo' => 123}, ), - 'darray as KeyedContainer' => tuple( + 'dict as KeyedContainer' => tuple( type_structure(TypeConstants::class, 'TStringIntKeyedContainer'), - darray['foo' => 123], + dict['foo' => 123], ), 'empty Map' => tuple(type_structure(TypeConstants::class, 'TStringStringMap'), Map {}), @@ -142,11 +142,11 @@ final class TypeStructureTest extends \Facebook\HackTest\HackTest { ), 'shape with empty container' => tuple( type_structure(TypeConstants::class, 'TShapeWithContainer'), - darray['container' => Vector {}], + dict['container' => Vector {}], ), 'shape with non-empty container' => tuple( type_structure(TypeConstants::class, 'TShapeWithContainer'), - darray['container' => Vector {'foo'}], + dict['container' => Vector {'foo'}], ), 'enum' => tuple(type_structure(TypeConstants::class, 'TEnum'), ExampleEnum::DERP), @@ -178,14 +178,6 @@ final class TypeStructureTest extends \Facebook\HackTest\HackTest { type_structure(TypeConstants::class, 'TStringKeyset'), keyset['foo', 'bar', 'baz', 'herp', 'derp'], ), - 'varray as varray_or_darray' => tuple( - type_structure(TypeConstants::class, 'TVArrayOrDArray'), - varray[123], - ), - 'darray as varray_or_darray' => tuple( - type_structure(TypeConstants::class, 'TVArrayOrDArray'), - darray['foo' => 123], - ), ]; } @@ -196,8 +188,6 @@ final class TypeStructureTest extends \Facebook\HackTest\HackTest { } public function testAssertedShapeAsShape(): void { - // most of the runtime is fine with actual PHP arrays, but we need a real - // darray for the `as` operator $x = shape('someString' => 'foo'); $y = TypeAssert\matches_type_structure( type_structure(TypeConstants::class, 'TShapeWithOneField'), @@ -356,12 +346,12 @@ final class TypeStructureTest extends \Facebook\HackTest\HackTest { ), 'shape with missing container field' => tuple( type_structure(TypeConstants::class, 'TShapeWithContainer'), - darray[], + dict[], vec['shape[container]'], ), 'shape with container of wrong kind' => tuple( type_structure(TypeConstants::class, 'TShapeWithContainer'), - darray['container' => Vector {123}], + dict['container' => Vector {123}], vec['shape[container]', 'HH\\Vector'], ), 'enum' => tuple( @@ -448,9 +438,9 @@ final class TypeStructureTest extends \Facebook\HackTest\HackTest { vec[new TestStringable('foo')], keyset['foo'], ), - 'varray to vec' => tuple( + 'vec to vec' => tuple( type_structure(TypeConstants::class, 'TIntVec'), - varray['123'], + vec['123'], vec[123], ), 'array to keyset' => tuple( @@ -458,9 +448,9 @@ final class TypeStructureTest extends \Facebook\HackTest\HackTest { vec['123'], keyset['123'], ), - 'darray to dict' => tuple( + 'dict to dict' => tuple( type_structure(TypeConstants::class, 'TStringStringDict'), - darray['foo' => 123, 'bar' => 456], + dict['foo' => 123, 'bar' => 456], dict['foo' => '123', 'bar' => '456'], ), 'shape with implicit subtyping and extra fields' => tuple( @@ -523,7 +513,7 @@ final class TypeStructureTest extends \Facebook\HackTest\HackTest { // - likely future changes to the implementation of shapes $shape = shape('someString' => 'foobar', 'someNullable' => null); $dict = dict['someString' => 'foobar', 'someNullable' => null]; - $array = darray['someString' => 'foobar', 'someNullable' => null]; + $array = dict['someString' => 'foobar', 'someNullable' => null]; $ts = type_structure(TypeConstants::class, 'TFlatShape'); expect(TypeAssert\matches_type_structure($ts, $dict))->toBeSame($shape); @@ -536,7 +526,7 @@ final class TypeStructureTest extends \Facebook\HackTest\HackTest { // - likely future changes to the implementation of tuples $tuple = tuple('foo', 123); $vec = vec['foo', 123]; - $array = varray['foo', 123]; + $array = vec['foo', 123]; $ts = type_structure(TypeConstants::class, 'TTuple'); expect(TypeAssert\matches_type_structure($ts, $vec))->toBeSame($tuple); diff --git a/tests/VArraySpecTest.hack b/tests/VArraySpecTest.hack index b663750..cf924bf 100644 --- a/tests/VArraySpecTest.hack +++ b/tests/VArraySpecTest.hack @@ -13,23 +13,26 @@ namespace Facebook\TypeAssert; use namespace Facebook\TypeSpec; use type Facebook\TypeSpec\TypeSpec; -final class VArraySpecTest extends TypeSpecTest> { +// This type is a misnomer (now). +// VArraySpecTest tests the behavior of `TypeSpec\of>()`, +// but the type under test is `VecSpec<_>`. +final class VArraySpecTest extends TypeSpecTest> { <<__Override>> - public function getTypeSpec(): TypeSpec> { - return TypeSpec\of>(); + public function getTypeSpec(): TypeSpec> { + return TypeSpec\of>(); } <<__Override>> - public function getValidCoercions(): vec<(mixed, varray)> { + public function getValidCoercions(): vec<(mixed, vec)> { return vec[ - tuple(vec[], varray[]), - tuple(vec['123'], varray[123]), - tuple(varray['123'], varray[123]), - tuple(varray[123], varray[123]), - tuple(dict['foo' => '456'], varray[456]), - tuple(Vector {123}, varray[123]), - tuple(darray['foo' => 123], varray[123]), - tuple(keyset['123'], varray[123]), + tuple(vec[], vec[]), + tuple(vec['123'], vec[123]), + tuple(vec['123'], vec[123]), + tuple(vec[123], vec[123]), + tuple(dict['foo' => '456'], vec[456]), + tuple(Vector {123}, vec[123]), + tuple(dict['foo' => 123], vec[123]), + tuple(keyset['123'], vec[123]), ]; } @@ -38,7 +41,7 @@ final class VArraySpecTest extends TypeSpecTest> { return vec[ tuple(false), tuple(123), - tuple(varray['foo']), + tuple(vec['foo']), tuple(vec['foo']), tuple(keyset['foo']), ]; @@ -46,7 +49,7 @@ final class VArraySpecTest extends TypeSpecTest> { <<__Override>> public function getToStringExamples( - ): vec<(TypeSpec>, string)> { + ): vec<(TypeSpec>, string)> { return vec[ tuple(TypeSpec\of>(), vec::class.''), tuple(TypeSpec\of>(), vec::class.''), diff --git a/tests/VecSpecTest.hack b/tests/VecSpecTest.hack index 075b2e5..f838377 100644 --- a/tests/VecSpecTest.hack +++ b/tests/VecSpecTest.hack @@ -24,11 +24,11 @@ final class VecSpecTest extends TypeSpecTest> { return vec[ tuple(vec[], vec[]), tuple(vec['123'], vec[123]), - tuple(varray['123'], vec[123]), - tuple(varray[123], vec[123]), + tuple(vec['123'], vec[123]), + tuple(vec[123], vec[123]), tuple(dict['foo' => '456'], vec[456]), tuple(Vector {123}, vec[123]), - tuple(darray['foo' => 123], vec[123]), + tuple(dict['foo' => 123], vec[123]), tuple(keyset['123'], vec[123]), ]; } diff --git a/tests/fixtures/TypeConstants.hack b/tests/fixtures/TypeConstants.hack index 41fa5a3..8cbe29e 100644 --- a/tests/fixtures/TypeConstants.hack +++ b/tests/fixtures/TypeConstants.hack @@ -18,8 +18,8 @@ final class TypeConstants { const type TNum = num; const type TArrayKey = arraykey; const type TTuple = (string, int); - const type TStringVArray = varray; - const type TStringStringDArray = darray; + const type TStringVec = vec; + const type TStringStringDict = dict; const type TNullableString = ?string; @@ -60,9 +60,6 @@ final class TypeConstants { const type TIntVec = vec; const type TIntVecVec = vec>; - const type TStringStringDict = dict; const type TStringStringVecDict = dict>; const type TStringKeyset = keyset; - - const type TVArrayOrDArray = varray_or_darray; } From bf50df33a2b6b20269d11be2cbeeadd7b1b010f0 Mon Sep 17 00:00:00 2001 From: Lexidor Digital <31805625+lexidor@users.noreply.github.com> Date: Wed, 24 May 2023 19:50:47 +0200 Subject: [PATCH 3/7] Formatting the affected files --- .../__Private/from_type_structure.hack | 357 +++++++++--------- tests/ShapeSpecTest.hack | 9 +- 2 files changed, 189 insertions(+), 177 deletions(-) diff --git a/src/TypeSpec/__Private/from_type_structure.hack b/src/TypeSpec/__Private/from_type_structure.hack index 8e1717f..c58857b 100644 --- a/src/TypeSpec/__Private/from_type_structure.hack +++ b/src/TypeSpec/__Private/from_type_structure.hack @@ -18,192 +18,205 @@ use namespace Facebook\{TypeAssert, TypeSpec}; function from_type_structure(TypeStructure $ts): TypeSpec { if ($ts['optional_shape_field'] ?? false) { $ts['optional_shape_field'] = false; - /* HH_IGNORE_ERROR[4110] */ - return new OptionalSpec(from_type_structure($ts)); + return new OptionalSpec(from_type_structure(as_type_structure_UNSAFE($ts))); } if ($ts['nullable'] ?? false) { $ts['nullable'] = false; - /* HH_IGNORE_ERROR[4110] */ - return new NullableSpec(from_type_structure($ts)); + return new NullableSpec(from_type_structure(as_type_structure_UNSAFE($ts))) + |> as_type_spec_UNSAFE($$); } - /* HH_IGNORE_ERROR[4022] exhaustive + default */ - switch ($ts['kind']) { - case TypeStructureKind::OF_VOID: - throw new UnsupportedTypeException('OF_VOID'); - case TypeStructureKind::OF_INT: - /* HH_IGNORE_ERROR[4110] */ - return TypeSpec\int(); - case TypeStructureKind::OF_BOOL: - /* HH_IGNORE_ERROR[4110] */ - return TypeSpec\bool(); - case TypeStructureKind::OF_FLOAT: - /* HH_IGNORE_ERROR[4110] */ - return TypeSpec\float(); - case TypeStructureKind::OF_STRING: - /* HH_IGNORE_ERROR[4110] */ - return TypeSpec\string(); - case TypeStructureKind::OF_RESOURCE: - /* HH_IGNORE_ERROR[4110] */ - return TypeSpec\resource(); - case TypeStructureKind::OF_NUM: - /* HH_IGNORE_ERROR[4110] */ - return TypeSpec\num(); - case TypeStructureKind::OF_ARRAYKEY: - /* HH_IGNORE_ERROR[4110] */ - return TypeSpec\arraykey(); - case TypeStructureKind::OF_NORETURN: - throw new UnsupportedTypeException('OF_NORETURN'); - case TypeStructureKind::OF_DYNAMIC: - case TypeStructureKind::OF_MIXED: - /* HH_IGNORE_ERROR[4110] */ - return new MixedSpec(); - case TypeStructureKind::OF_TUPLE: - /* HH_IGNORE_ERROR[4110] */ - return new TupleSpec( - Vec\map( - TypeAssert\not_null($ts['elem_types']), - from_type_structure<>, - ), - ); - case TypeStructureKind::OF_FUNCTION: - throw new UnsupportedTypeException('OF_FUNCTION'); - case TypeStructureKind::OF_ARRAY: - throw new UnsupportedTypeException('OF_ARRAY'); - case TypeStructureKind::OF_VARRAY: - $generics = $ts['generic_types'] as nonnull; - invariant(C\count($generics) === 1, 'got varray with multiple generics'); - // When given a legacy varray<_> type, we can return a vec<_> spec instead. - /* HH_IGNORE_ERROR[4110] */ - return TypeSpec\vec(from_type_structure($generics[0])); - case TypeStructureKind::OF_DARRAY: - $generics = $ts['generic_types'] as nonnull; - invariant( - C\count($generics) === 2, - 'darrays must have exactly 2 generics', - ); - // When given a legacy darray<_, _> type, we can return a dict<_, _> spec instead. - /* HH_IGNORE_ERROR[4110] */ - return TypeSpec\dict(from_type_structure($generics[0]), from_type_structure($generics[1])); - case TypeStructureKind::OF_VARRAY_OR_DARRAY: - throw new UnsupportedTypeException('OF_VARRAY_OR_DARRAY'); - case TypeStructureKind::OF_DICT: - $generics = TypeAssert\not_null($ts['generic_types']); - invariant(C\count($generics) === 2, 'dicts must have 2 generics'); - /* HH_IGNORE_ERROR[4110] */ - return TypeSpec\dict( - from_type_structure($generics[0]), - from_type_structure($generics[1]), - ); - case TypeStructureKind::OF_KEYSET: - $generics = TypeAssert\not_null($ts['generic_types']); - invariant(C\count($generics) === 1, 'keysets must have 1 generic'); - /* HH_IGNORE_ERROR[4110] */ - return TypeSpec\keyset(from_type_structure($generics[0])); - case TypeStructureKind::OF_VEC: - $generics = TypeAssert\not_null($ts['generic_types']); - invariant(C\count($generics) === 1, 'vecs must have 1 generic'); - /* HH_IGNORE_ERROR[4110] */ - return TypeSpec\vec(from_type_structure($generics[0])); - case TypeStructureKind::OF_GENERIC: - throw new UnsupportedTypeException('OF_GENERIC'); - case TypeStructureKind::OF_SHAPE: - $fields = TypeAssert\not_null($ts['fields']); - /* HH_IGNORE_ERROR[4110] */ - return new ShapeSpec( - Dict\pull_with_key( - $fields, - ($_k, $field_ts) ==> from_type_structure($field_ts), - ($k, $_v) ==> $k, - ), - ($ts['allows_unknown_fields'] ?? false) - ? UnknownFieldsMode::ALLOW - : UnknownFieldsMode::DENY, - ); - case TypeStructureKind::OF_CLASS: - case TypeStructureKind::OF_INTERFACE: - $classname = TypeAssert\not_null($ts['classname']); - switch ($classname) { - case Vector::class: - case ImmVector::class: - case \ConstVector::class: - return new VectorSpec( - /* HH_IGNORE_ERROR[4323] unsafe generics */ - $classname, - from_type_structure( - TypeAssert\not_null($ts['generic_types'] ?? null)[0], - ), - ); - case Map::class: - case ImmMap::class: - case \ConstMap::class: - return new MapSpec( - /* HH_IGNORE_ERROR[4323] unsafe generics */ - $classname, - from_type_structure( - TypeAssert\not_null($ts['generic_types'] ?? null)[0], - ), - from_type_structure( - TypeAssert\not_null($ts['generic_types'] ?? null)[1], - ), - ); - case Set::class: - case ImmSet::class: - case \ConstSet::class: - return new SetSpec( - /* HH_IGNORE_ERROR[4323] unsafe generics */ - $classname, - from_type_structure( - TypeAssert\not_null($ts['generic_types'] ?? null)[0], - ), - ); - default: - if ( - \is_a( - $classname, - KeyedTraversable::class, - /* strings = */ true, + try { + switch ($ts['kind']) { + case TypeStructureKind::OF_NOTHING: + throw new UnsupportedTypeException('OF_NOTHING'); + case TypeStructureKind::OF_VEC_OR_DICT: + throw new UnsupportedTypeException('OF_VEC_OR_DICT'); + case TypeStructureKind::OF_XHP: + throw new UnsupportedTypeException('OF_XHP'); + case TypeStructureKind::OF_VOID: + throw new UnsupportedTypeException('OF_VOID'); + case TypeStructureKind::OF_INT: + return TypeSpec\int() |> as_type_spec_UNSAFE($$); + case TypeStructureKind::OF_BOOL: + return TypeSpec\bool() |> as_type_spec_UNSAFE($$); + case TypeStructureKind::OF_FLOAT: + return TypeSpec\float() |> as_type_spec_UNSAFE($$); + case TypeStructureKind::OF_STRING: + return TypeSpec\string() |> as_type_spec_UNSAFE($$); + case TypeStructureKind::OF_RESOURCE: + return TypeSpec\resource() |> as_type_spec_UNSAFE($$); + case TypeStructureKind::OF_NUM: + return TypeSpec\num() |> as_type_spec_UNSAFE($$); + case TypeStructureKind::OF_ARRAYKEY: + return TypeSpec\arraykey() |> as_type_spec_UNSAFE($$); + case TypeStructureKind::OF_NORETURN: + throw new UnsupportedTypeException('OF_NORETURN'); + case TypeStructureKind::OF_DYNAMIC: + case TypeStructureKind::OF_MIXED: + return new MixedSpec() |> as_type_spec_UNSAFE($$); + case TypeStructureKind::OF_TUPLE: + return new TupleSpec( + Vec\map( + TypeAssert\not_null($ts['elem_types']), + from_type_structure<>, + ), + ) + |> as_type_spec_UNSAFE($$); + case TypeStructureKind::OF_FUNCTION: + throw new UnsupportedTypeException('OF_FUNCTION'); + case TypeStructureKind::OF_ARRAY: + throw new UnsupportedTypeException('OF_ARRAY'); + case TypeStructureKind::OF_VARRAY: + $generics = $ts['generic_types'] as nonnull; + invariant( + C\count($generics) === 1, + 'got varray with multiple generics', + ); + // When given a legacy varray<_> type, we can return a vec<_> spec instead. + return TypeSpec\vec(from_type_structure($generics[0])) + |> as_type_spec_UNSAFE($$); + case TypeStructureKind::OF_DARRAY: + $generics = $ts['generic_types'] as nonnull; + invariant( + C\count($generics) === 2, + 'darrays must have exactly 2 generics', + ); + // When given a legacy darray<_, _> type, we can return a dict<_, _> spec instead. + return TypeSpec\dict( + from_type_structure($generics[0]), + from_type_structure($generics[1]), + ) + |> as_type_spec_UNSAFE($$); + case TypeStructureKind::OF_VARRAY_OR_DARRAY: + throw new UnsupportedTypeException('OF_VARRAY_OR_DARRAY'); + case TypeStructureKind::OF_DICT: + $generics = TypeAssert\not_null($ts['generic_types']); + invariant(C\count($generics) === 2, 'dicts must have 2 generics'); + return TypeSpec\dict( + from_type_structure($generics[0]), + from_type_structure($generics[1]), + ) + |> as_type_spec_UNSAFE($$); + case TypeStructureKind::OF_KEYSET: + $generics = TypeAssert\not_null($ts['generic_types']); + invariant(C\count($generics) === 1, 'keysets must have 1 generic'); + return TypeSpec\keyset(from_type_structure($generics[0])) + |> as_type_spec_UNSAFE($$); + case TypeStructureKind::OF_VEC: + $generics = TypeAssert\not_null($ts['generic_types']); + invariant(C\count($generics) === 1, 'vecs must have 1 generic'); + return TypeSpec\vec(from_type_structure($generics[0])) + |> as_type_spec_UNSAFE($$); + case TypeStructureKind::OF_GENERIC: + throw new UnsupportedTypeException('OF_GENERIC'); + case TypeStructureKind::OF_SHAPE: + $fields = TypeAssert\not_null($ts['fields']); + return new ShapeSpec( + Dict\pull_with_key( + $fields, + ($_k, $field_ts) ==> from_type_structure($field_ts), + ($k, $_v) ==> $k, + ), + ($ts['allows_unknown_fields'] ?? false) + ? UnknownFieldsMode::ALLOW + : UnknownFieldsMode::DENY, + ) + |> as_type_spec_UNSAFE($$); + case TypeStructureKind::OF_CLASS: + case TypeStructureKind::OF_INTERFACE: + $classname = TypeAssert\not_null($ts['classname']); + switch ($classname) { + case Vector::class: + case ImmVector::class: + case \ConstVector::class: + return new VectorSpec( + \HH\FIXME\UNSAFE_CAST, classname<\ConstVector<_>>>( + $classname, + ), + from_type_structure( + TypeAssert\not_null($ts['generic_types'] ?? null)[0], + ), ) - ) { - return new KeyedTraversableSpec( - /* HH_IGNORE_ERROR[4323] unsafe generics */ - $classname, + |> as_type_spec_UNSAFE($$); + case Map::class: + case ImmMap::class: + case \ConstMap::class: + return new MapSpec( + \HH\FIXME\UNSAFE_CAST, classname<\ConstMap<_, _>>>( + $classname, + ), from_type_structure( TypeAssert\not_null($ts['generic_types'] ?? null)[0], ), from_type_structure( TypeAssert\not_null($ts['generic_types'] ?? null)[1], ), - ); - } - if (\is_a($classname, Traversable::class, /* strings = */ true)) { - return new TraversableSpec( - /* HH_IGNORE_ERROR[4323] unsafe generics */ - $classname, + ) + |> as_type_spec_UNSAFE($$); + case Set::class: + case ImmSet::class: + case \ConstSet::class: + return new SetSpec( + \HH\FIXME\UNSAFE_CAST, classname<\ConstSet<_>>>( + $classname, + ), from_type_structure( TypeAssert\not_null($ts['generic_types'] ?? null)[0], ), - ); - } - return new InstanceOfSpec($classname); - } - case TypeStructureKind::OF_TRAIT: - throw new UnsupportedTypeException('OF_TRAIT'); - case TypeStructureKind::OF_ENUM: - $enum = TypeAssert\not_null($ts['classname']); - /* HH_IGNORE_ERROR[4110] */ - return new EnumSpec($enum); - case TypeStructureKind::OF_NULL: - /* HH_IGNORE_ERROR[4110] unsafe generics */ - return new NullSpec(); - case TypeStructureKind::OF_NONNULL: - /* HH_IGNORE_ERROR[4110] unsafe generics */ - return new NonNullSpec(); - case TypeStructureKind::OF_UNRESOLVED: - throw new UnsupportedTypeException('OF_UNRESOLVED'); - default: - $name = TypeStructureKind::getNames()[$ts['kind']] ?? - \var_export($ts['kind'], true); - throw new UnsupportedTypeException($name); + ) + |> as_type_spec_UNSAFE($$); + default: + if ( + \is_a( + $classname, + KeyedTraversable::class, + /* strings = */ true, + ) + ) { + return new KeyedTraversableSpec( + \HH\FIXME\UNSAFE_CAST< + classname, + classname>, + >($classname), + from_type_structure( + TypeAssert\not_null($ts['generic_types'] ?? null)[0], + ), + from_type_structure( + TypeAssert\not_null($ts['generic_types'] ?? null)[1], + ), + ) + |> as_type_spec_UNSAFE($$); + } + if (\is_a($classname, Traversable::class, /* strings = */ true)) { + return new TraversableSpec( + \HH\FIXME\UNSAFE_CAST, classname>>( + $classname, + ), + from_type_structure( + TypeAssert\not_null($ts['generic_types'] ?? null)[0], + ), + ) + |> as_type_spec_UNSAFE($$); + } + return new InstanceOfSpec($classname); + } + case TypeStructureKind::OF_TRAIT: + throw new UnsupportedTypeException('OF_TRAIT'); + case TypeStructureKind::OF_ENUM: + $enum = TypeAssert\not_null($ts['classname']) + |> \HH\FIXME\UNSAFE_CAST, \HH\enumname>($$); + return new EnumSpec($enum) |> as_type_spec_UNSAFE($$); + case TypeStructureKind::OF_NULL: + return new NullSpec() |> as_type_spec_UNSAFE($$); + case TypeStructureKind::OF_NONNULL: + return new NonNullSpec() |> as_type_spec_UNSAFE($$); + case TypeStructureKind::OF_UNRESOLVED: + throw new UnsupportedTypeException('OF_UNRESOLVED'); + } + } catch (\RuntimeException $_switch_statement_was_not_exhaustive) { + $name = TypeStructureKind::getNames()[$ts['kind']] ?? + \var_export($ts['kind'], true); + throw new UnsupportedTypeException($name); } } diff --git a/tests/ShapeSpecTest.hack b/tests/ShapeSpecTest.hack index 51955ec..42cdd31 100644 --- a/tests/ShapeSpecTest.hack +++ b/tests/ShapeSpecTest.hack @@ -29,10 +29,8 @@ final class ShapeSpecTest extends TypeSpecTest { <<__Override>> public function getInvalidValues(): vec<(mixed)> { - return Vec\filter( - parent::getInvalidValues(), - $it ==> !$it[0] is dict<_, _>, - ); + return + Vec\filter(parent::getInvalidValues(), $it ==> !$it[0] is dict<_, _>); } <<__Override>> @@ -109,7 +107,8 @@ final class ShapeSpecTest extends TypeSpecTest { <<__Override>> public function getToStringExamples(): vec<(TypeSpec, string)> { return vec[tuple( - $this->getTypeSpec(), <<getTypeSpec(), + << string, ?'optional_string_field' => string, From d6c66ceb0a31281328937be9fb5faee6c859df74 Mon Sep 17 00:00:00 2001 From: Lexidor Digital <31805625+lexidor@users.noreply.github.com> Date: Wed, 24 May 2023 20:05:13 +0200 Subject: [PATCH 4/7] Banish HH_FIXME and HH_IGNORE_ERROR directives Begone you imprecise blunt hammer! --- src/TypeSpec/__Private/ClassnameSpec.hack | 8 +++-- src/TypeSpec/__Private/InstanceOfSpec.hack | 6 ++-- .../__Private/KeyedTraversableSpec.hack | 2 +- src/TypeSpec/__Private/MapSpec.hack | 31 ++++++++++--------- src/TypeSpec/__Private/SetSpec.hack | 30 +++++++++--------- src/TypeSpec/__Private/ShapeSpec.hack | 22 +++++++------ src/TypeSpec/__Private/TraversableSpec.hack | 2 +- src/TypeSpec/__Private/TupleSpec.hack | 9 ++---- src/TypeSpec/__Private/VectorSpec.hack | 27 +++++++++------- .../__Private/as_type_spec_UNSAFE.hack | 27 ++++++++++++++++ .../__Private/from_type_structure.hack | 8 +++-- src/TypeSpec/__Private/stringish_cast.hack | 1 - tests/fixtures/TypeConstants.hack | 2 -- 13 files changed, 107 insertions(+), 68 deletions(-) create mode 100644 src/TypeSpec/__Private/as_type_spec_UNSAFE.hack diff --git a/src/TypeSpec/__Private/ClassnameSpec.hack b/src/TypeSpec/__Private/ClassnameSpec.hack index bc5d5f1..b8c3bb5 100644 --- a/src/TypeSpec/__Private/ClassnameSpec.hack +++ b/src/TypeSpec/__Private/ClassnameSpec.hack @@ -21,9 +21,11 @@ final class ClassnameSpec> extends TypeSpec { <<__Override>> public function assertType(mixed $value): T { - if (($value is string) && \is_a($value, $this->what, /* strings = */ true)) { - /* HH_IGNORE_ERROR[4110] is_a is not understood by Hack */ - return $value; + if ($value is string && \is_a($value, $this->what, /* strings = */ true)) { + return \HH\FIXME\UNSAFE_CAST( + $value, + 'is_a is not understood by Hack', + ); } throw IncorrectTypeException::withValue($this->getTrace(), $this->what, $value); diff --git a/src/TypeSpec/__Private/InstanceOfSpec.hack b/src/TypeSpec/__Private/InstanceOfSpec.hack index 0ff3dc8..b63e57f 100644 --- a/src/TypeSpec/__Private/InstanceOfSpec.hack +++ b/src/TypeSpec/__Private/InstanceOfSpec.hack @@ -22,8 +22,10 @@ final class InstanceOfSpec extends TypeSpec { <<__Override>> public function assertType(mixed $value): T { if (\is_a($value, $this->what)) { - /* HH_IGNORE_ERROR[4110] unsafe for generics */ - return $value; + return \HH\FIXME\UNSAFE_CAST( + $value, + 'unsafe for classname>; the `<_>` goes unchecked.', + ); } throw IncorrectTypeException::withValue($this->getTrace(), $this->what, $value); diff --git a/src/TypeSpec/__Private/KeyedTraversableSpec.hack b/src/TypeSpec/__Private/KeyedTraversableSpec.hack index 97707a2..cf95bb2 100644 --- a/src/TypeSpec/__Private/KeyedTraversableSpec.hack +++ b/src/TypeSpec/__Private/KeyedTraversableSpec.hack @@ -71,7 +71,7 @@ final class KeyedTraversableSpec> $tsk->assertType($k); $tsv->assertType($v); } - return /* HH_IGNORE_ERROR[4110] */ $value; + return \HH\FIXME\UNSAFE_CAST, T>($value); } <<__Override>> diff --git a/src/TypeSpec/__Private/MapSpec.hack b/src/TypeSpec/__Private/MapSpec.hack index 16cc481..343797d 100644 --- a/src/TypeSpec/__Private/MapSpec.hack +++ b/src/TypeSpec/__Private/MapSpec.hack @@ -58,17 +58,14 @@ final class MapSpec> $changed === false && \is_a($value, $this->what, /* allow_string = */ true) ) { - /* HH_IGNORE_ERROR[4110] */ - return $value; + return \HH\FIXME\UNSAFE_CAST, T>($value); } if ($this->what === Map::class) { - /* HH_IGNORE_ERROR[4110] */ - return $out; + return \HH\FIXME\UNSAFE_CAST, T>($out); } - /* HH_IGNORE_ERROR[4110] */ - return $out->immutable(); + return \HH\FIXME\UNSAFE_CAST, T>($out->immutable()); } <<__Override>> @@ -99,19 +96,25 @@ final class MapSpec> } if (!$changed) { - /* HH_IGNORE_ERROR[4110] is_a() ensure the collection type - and $spec->assertType() ensures the inner type. */ - return $value; + // $value has an undenotable type Tk#1, so mixed. + return \HH\FIXME\UNSAFE_CAST( + $value, + 'is_a() ensures the collection type and $spec->assertType() ensures the inner type.', + ); } if ($this->what === Map::class) { - /* HH_IGNORE_ERROR[4110] $out is a Map and $this->what is also Map. */ - return $out; + return \HH\FIXME\UNSAFE_CAST, T>( + $out, + '$out is a Map and $this->what is also Map.', + ); } - /* HH_IGNORE_ERROR[4110] Return ImmMap when the user asks for ConstMap or ImmMap. - This immutability for ConstMap is not needed, but kept for backwards compatibility. */ - return $out->immutable(); + return \HH\FIXME\UNSAFE_CAST, T>( + $out->immutable(), + 'Return ImmMap when the user asks for ConstMap or ImmMap. + This immutability for ConstMap is not needed, but kept for backwards compatibility.', + ); } <<__Override>> diff --git a/src/TypeSpec/__Private/SetSpec.hack b/src/TypeSpec/__Private/SetSpec.hack index 96038c4..938dbed 100644 --- a/src/TypeSpec/__Private/SetSpec.hack +++ b/src/TypeSpec/__Private/SetSpec.hack @@ -47,16 +47,13 @@ final class SetSpec> extends TypeSpec { } if ($changed === false && \is_a($value, $this->what)) { - /* HH_IGNORE_ERROR[4110] */ - return $value; + return \HH\FIXME\UNSAFE_CAST, T>($value); } if ($this->what === Set::class) { - /* HH_IGNORE_ERROR[4110] */ - return $out; + return \HH\FIXME\UNSAFE_CAST, T>($out); } - /* HH_IGNORE_ERROR[4110] */ - return $out->immutable(); + return \HH\FIXME\UNSAFE_CAST, T>($out->immutable()); } <<__Override>> @@ -85,19 +82,24 @@ final class SetSpec> extends TypeSpec { } if (!$changed) { - /* HH_IGNORE_ERROR[4110] is_a() ensures the collection type - and $spec->assertType() ensures the inner type. */ - return $value; + return \HH\FIXME\UNSAFE_CAST<\ConstSet, T>( + $value, + 'is_a() ensures the collection type and $spec->assertType() ensures the inner type.', + ); } if ($this->what === Set::class) { - /* HH_IGNORE_ERROR[4110] $out is a Set and $this->what is also Set. */ - return $out; + return \HH\FIXME\UNSAFE_CAST<\ConstSet, T>( + $out, + '$out is a Set and $this->what is also Set.', + ); } - /* HH_IGNORE_ERROR[4110] Return ImmSet when the user asks for ConstSet or ImmSet. - This immutability for ConstSet is not needed, but kept for consistency with MapSpec and VectorSpec. */ - return $out->immutable(); + return \HH\FIXME\UNSAFE_CAST, T>( + $out->immutable(), + 'Return ImmSet when the user asks for ConstSet or ImmSet. + This immutability for ConstSet is not needed, but kept for consistency with MapSpec and VectorSpec.', + ); } <<__Override>> diff --git a/src/TypeSpec/__Private/ShapeSpec.hack b/src/TypeSpec/__Private/ShapeSpec.hack index a93d46f..f84adf6 100644 --- a/src/TypeSpec/__Private/ShapeSpec.hack +++ b/src/TypeSpec/__Private/ShapeSpec.hack @@ -35,7 +35,10 @@ final class ShapeSpec extends TypeSpec { TypeCoercionException::withValue($this->getTrace(), 'shape', $value); } - $value = dict(/* HH_IGNORE_ERROR[4323] */$value); + $value = dict(\HH\FIXME\UNSAFE_CAST< + KeyedTraversable, + KeyedTraversable, + >($value)); $out = dict[]; foreach ($this->inners as $key => $spec) { $trace = $this->getTrace()->withFrame('shape['.$key.']'); @@ -59,7 +62,10 @@ final class ShapeSpec extends TypeSpec { } } - return self::dictToShapeUNSAFE($out); + return \HH\FIXME\UNSAFE_CAST, shape()>( + $out, + 'Can not use $out as shape(...), because the generic is closed.', + ); } <<__Override>> @@ -101,14 +107,10 @@ final class ShapeSpec extends TypeSpec { } } - return self::dictToShapeUNSAFE($out); - } - - private static function dictToShapeUNSAFE( - dict $shape, - ): shape() { - /* HH_IGNORE_ERROR[4110] */ - return $shape; + return \HH\FIXME\UNSAFE_CAST, shape()>( + $out, + 'Can not use $out as shape(...), because the generic is closed.', + ); } <<__Override>> diff --git a/src/TypeSpec/__Private/TraversableSpec.hack b/src/TypeSpec/__Private/TraversableSpec.hack index 8ca39fb..696d11e 100644 --- a/src/TypeSpec/__Private/TraversableSpec.hack +++ b/src/TypeSpec/__Private/TraversableSpec.hack @@ -67,7 +67,7 @@ final class TraversableSpec> foreach ($value as $v) { $this->inner->withTrace($trace)->assertType($v); } - return /* HH_IGNORE_ERROR[4110] */ $value; + return \HH\FIXME\UNSAFE_CAST, T>($value); } <<__Override>> diff --git a/src/TypeSpec/__Private/TupleSpec.hack b/src/TypeSpec/__Private/TupleSpec.hack index e9f4b10..f19267c 100644 --- a/src/TypeSpec/__Private/TupleSpec.hack +++ b/src/TypeSpec/__Private/TupleSpec.hack @@ -43,7 +43,7 @@ final class TupleSpec extends TypeSpec { ->withTrace($this->getTrace()->withFrame('tuple['.$i.']')) ->coerceType($values[$i]); } - return self::vecToTuple($out); + return $out as BogusTuple; } <<__Override>> @@ -68,12 +68,7 @@ final class TupleSpec extends TypeSpec { ->withTrace($this->getTrace()->withFrame('tuple['.$i.']')) ->assertType($value[$i]); } - return self::vecToTuple($out); - } - - private static function vecToTuple(vec $tuple): BogusTuple { - /* HH_IGNORE_ERROR[4110] */ - return $tuple; + return $out as BogusTuple; } <<__Override>> diff --git a/src/TypeSpec/__Private/VectorSpec.hack b/src/TypeSpec/__Private/VectorSpec.hack index 35a73aa..ff736db 100644 --- a/src/TypeSpec/__Private/VectorSpec.hack +++ b/src/TypeSpec/__Private/VectorSpec.hack @@ -48,14 +48,14 @@ final class VectorSpec> extends TypeSpec { } if ($changed === false && \is_a($value, $this->what)) { - return /* HH_IGNORE_ERROR[4110] */ $value; + return \HH\FIXME\UNSAFE_CAST, T>($value); } if ($this->what === Vector::class) { - return /* HH_IGNORE_ERROR[4110] */ $out; + return \HH\FIXME\UNSAFE_CAST, T>($out); } - return /* HH_IGNORE_ERROR[4110] */ $out->immutable(); + return \HH\FIXME\UNSAFE_CAST, T>($out->immutable()); } <<__Override>> @@ -84,19 +84,24 @@ final class VectorSpec> extends TypeSpec { } if (!$changed) { - /* HH_IGNORE_ERROR[4110] is_a() ensure the collection type - and $spec->assertType() ensures the inner type. */ - return $value; + return \HH\FIXME\UNSAFE_CAST<\ConstVector, T>( + $value, + 'is_a() ensures the collection type and $spec->assertType() ensures the inner type.', + ); } if ($this->what === Vector::class) { - /* HH_IGNORE_ERROR[4110] $out is a Vector and $this->what is also Vector. */ - return $out; + return \HH\FIXME\UNSAFE_CAST, T>( + $out, + '$out is a Vector and $this->what is also Vector.', + ); } - /* HH_IGNORE_ERROR[4110] Return ImmVector when the user asks for ConstVector or ImmVector. - This immutability for ConstVector is not needed, but kept for backwards compatibility. */ - return $out->immutable(); + return \HH\FIXME\UNSAFE_CAST, T>( + $out->immutable(), + 'Return ImmVector when the user asks for ConstVector or ImmVector. + This immutability for ConstVector is not needed, but kept for backwards compatibility.', + ); } <<__Override>> diff --git a/src/TypeSpec/__Private/as_type_spec_UNSAFE.hack b/src/TypeSpec/__Private/as_type_spec_UNSAFE.hack new file mode 100644 index 0000000..4a27b0e --- /dev/null +++ b/src/TypeSpec/__Private/as_type_spec_UNSAFE.hack @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2016, Fred Emmott + * Copyright (c) 2017-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + */ + +namespace Facebook\TypeSpec\__Private; + +use type Facebook\TypeSpec\TypeSpec; + +/** + * EXTREMELY UNSAFE!!! + * + * Rebinds the `` of `$ts` to `nothing`. + * This means you'll be able to pass this TypeSpec for any other TypeSpec. + * This kind of unsafe trickery is needed when dynamically constructing + * a TypeSpec for a dynamic `T`, such as in `from_type_structure()`. + * There is nothing you can do to prove to Hack that the checks you did + * on the TypeStructure ensure your `T` and its `T` are compatible. + */ +function as_type_spec_UNSAFE(TypeSpec $ts): TypeSpec { + return \HH\FIXME\UNSAFE_CAST, TypeSpec>($ts); +} diff --git a/src/TypeSpec/__Private/from_type_structure.hack b/src/TypeSpec/__Private/from_type_structure.hack index c58857b..c420eae 100644 --- a/src/TypeSpec/__Private/from_type_structure.hack +++ b/src/TypeSpec/__Private/from_type_structure.hack @@ -18,11 +18,15 @@ use namespace Facebook\{TypeAssert, TypeSpec}; function from_type_structure(TypeStructure $ts): TypeSpec { if ($ts['optional_shape_field'] ?? false) { $ts['optional_shape_field'] = false; - return new OptionalSpec(from_type_structure(as_type_structure_UNSAFE($ts))); + return new OptionalSpec(from_type_structure( + \HH\FIXME\UNSAFE_CAST>($ts), + )); } if ($ts['nullable'] ?? false) { $ts['nullable'] = false; - return new NullableSpec(from_type_structure(as_type_structure_UNSAFE($ts))) + return new NullableSpec(from_type_structure( + \HH\FIXME\UNSAFE_CAST>($ts), + )) |> as_type_spec_UNSAFE($$); } diff --git a/src/TypeSpec/__Private/stringish_cast.hack b/src/TypeSpec/__Private/stringish_cast.hack index 1a8c60e..0165e88 100644 --- a/src/TypeSpec/__Private/stringish_cast.hack +++ b/src/TypeSpec/__Private/stringish_cast.hack @@ -26,7 +26,6 @@ function stringish_cast(\Stringish $stringish, string $caller): string { '() may not work in a future release.', \E_USER_DEPRECATED, ); - /*HH_FIXME[4128] stringish_cast() can't be used in the future.*/ return $stringish->__toString(); } } diff --git a/tests/fixtures/TypeConstants.hack b/tests/fixtures/TypeConstants.hack index 8cbe29e..72008fc 100644 --- a/tests/fixtures/TypeConstants.hack +++ b/tests/fixtures/TypeConstants.hack @@ -32,7 +32,6 @@ final class TypeConstants { const type TStringIntKeyedTraversable = KeyedTraversable; const type TStringIntKeyedContainer = KeyedContainer; - /* HH_FIXME[3033] no optional shape fields in 3.21 or 3.22 */ const type TFlatShape = shape( 'someString' => string, 'someNullable' => ?string, @@ -44,7 +43,6 @@ final class TypeConstants { 'someString' => string, ); - /* HH_FIXME[0003] no unknown shape fields in 3.21 */ const type TShapeWithOneFieldAndImplicitSubtypes = shape( 'someString' => string, ... From 00ef52329114ad65850945c815fe38a63b16f216 Mon Sep 17 00:00:00 2001 From: Lexidor Digital <31805625+lexidor@users.noreply.github.com> Date: Wed, 24 May 2023 18:13:49 +0000 Subject: [PATCH 5/7] Test on ubuntu 20.04, where apt install succeeds --- .github/workflows/build-and-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 64c995f..78a702f 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -11,7 +11,7 @@ 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 From 5423f3d6cab4867e17f7ab5360c392d258a06c16 Mon Sep 17 00:00:00 2001 From: Lexidor Digital <31805625+lexidor@users.noreply.github.com> Date: Wed, 24 May 2023 18:37:10 +0000 Subject: [PATCH 6/7] Don't look for ubuntu-20.04-latest There is no such runner, so the task waits forever to start. Whoops! --- .github/workflows/build-and-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 78a702f..f697514 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -16,7 +16,7 @@ jobs: - "4.128" - latest - nightly - runs-on: ${{matrix.os}}-latest + runs-on: ${{matrix.os}} steps: - uses: actions/checkout@v2 - uses: hhvm/actions/hack-lint-test@master From 348a748ce316df021e5fc402801bc268cbaa5b63 Mon Sep 17 00:00:00 2001 From: Lexidor Digital <31805625+lexidor@users.noreply.github.com> Date: Wed, 24 May 2023 20:57:14 +0200 Subject: [PATCH 7/7] Bump minimum hhvm version to 4.153 Hhvm 4.128 does not support unfixme'd UNSAFE_CAST() calls. Bumping to 4.153 to see if that version does. If it doesn't, will bump to 4.168. --- .github/workflows/build-and-test.yml | 4 +--- composer.json | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index f697514..de6d226 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -13,9 +13,7 @@ jobs: matrix: os: [ ubuntu-20.04 ] hhvm: - - "4.128" - - latest - - nightly + - "4.153" runs-on: ${{matrix.os}} steps: - uses: actions/checkout@v2 diff --git a/composer.json b/composer.json index 6d7021f..336642d 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,7 @@ "license": "MIT", "keywords": ["hack", "TypeAssert"], "require": { - "hhvm": "^4.128" + "hhvm": "^4.153" }, "extra": { "branch-alias": {