From b96374daf7f3a0333283a2643eab6094d094197d Mon Sep 17 00:00:00 2001 From: Martin Herndl Date: Thu, 1 Sep 2022 09:46:49 +0200 Subject: [PATCH 1/2] Add array_fill_keys tests for union and optional constant array keys --- .../PHPStan/Analyser/data/array-fill-keys.php | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/PHPStan/Analyser/data/array-fill-keys.php b/tests/PHPStan/Analyser/data/array-fill-keys.php index 739c7e8deb..fcf82d1094 100644 --- a/tests/PHPStan/Analyser/data/array-fill-keys.php +++ b/tests/PHPStan/Analyser/data/array-fill-keys.php @@ -51,6 +51,28 @@ function withObjectKey() : array assertType("*NEVER*", array_fill_keys([new Baz()], 'b')); } +function withUnionKeys(): void +{ + $arr1 = ['foo', rand(0, 1) ? 'bar1' : 'bar2', 'baz']; + assertType("non-empty-array<'bar1'|'bar2'|'baz'|'foo', 'b'>", array_fill_keys($arr1, 'b')); + + $arr2 = ['foo']; + if (rand(0, 1)) { + $arr2[] = 'bar'; + } + $arr2[] = 'baz'; + assertType("non-empty-array<'bar'|'baz'|'foo', 'b'>", array_fill_keys($arr2, 'b')); +} + +function withOptionalKeys(): void +{ + $arr1 = ['foo', 'bar']; + if (rand(0, 1)) { + $arr1[] = 'baz'; + } + assertType("array{foo: 'b', bar: 'b', baz?: 'b'}", array_fill_keys($arr1, 'b')); +} + /** * @param Bar[] $foo * @param int[] $bar From a39e5701ad8c6e54713b9f0b18fb7528b07eba21 Mon Sep 17 00:00:00 2001 From: Martin Herndl Date: Thu, 1 Sep 2022 09:47:25 +0200 Subject: [PATCH 2/2] Use TypeUtils::getOldConstantArrays in array_fill_keys extension --- src/Type/Php/ArrayFillKeysFunctionReturnTypeExtension.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Type/Php/ArrayFillKeysFunctionReturnTypeExtension.php b/src/Type/Php/ArrayFillKeysFunctionReturnTypeExtension.php index b1863e1db4..e912e4b828 100644 --- a/src/Type/Php/ArrayFillKeysFunctionReturnTypeExtension.php +++ b/src/Type/Php/ArrayFillKeysFunctionReturnTypeExtension.php @@ -33,7 +33,7 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection, $valueType = $scope->getType($functionCall->getArgs()[1]->value); $keysType = $scope->getType($functionCall->getArgs()[0]->value); - $constantArrays = TypeUtils::getConstantArrays($keysType); + $constantArrays = TypeUtils::getOldConstantArrays($keysType); if (count($constantArrays) === 0) { if ($keysType->isArray()->yes()) { $itemType = $keysType->getIterableValueType(); @@ -53,15 +53,15 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection, $arrayTypes = []; foreach ($constantArrays as $constantArray) { $arrayBuilder = ConstantArrayTypeBuilder::createEmpty(); - foreach ($constantArray->getValueTypes() as $keyType) { + foreach ($constantArray->getValueTypes() as $i => $keyType) { if ((new IntegerType())->isSuperTypeOf($keyType)->no()) { if ($keyType->toString() instanceof ErrorType) { return new NeverType(); } - $arrayBuilder->setOffsetValueType($keyType->toString(), $valueType); + $arrayBuilder->setOffsetValueType($keyType->toString(), $valueType, $constantArray->isOptionalKey($i)); } else { - $arrayBuilder->setOffsetValueType($keyType, $valueType); + $arrayBuilder->setOffsetValueType($keyType, $valueType, $constantArray->isOptionalKey($i)); } } $arrayTypes[] = $arrayBuilder->getArray();