From d77b63fc2fdb551528b65db4e05f682e5f3bf157 Mon Sep 17 00:00:00 2001 From: "John Paul E. Balandan, CPA" Date: Wed, 20 Nov 2024 20:48:04 +0800 Subject: [PATCH] Fix `iterator_to_array` return type with generators --- ...atorToArrayFunctionReturnTypeExtension.php | 9 +++++- .../Analyser/nsrt/iterator_to_array.php | 30 +++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/Type/Php/IteratorToArrayFunctionReturnTypeExtension.php b/src/Type/Php/IteratorToArrayFunctionReturnTypeExtension.php index 3eba789175..623e3c0bcd 100644 --- a/src/Type/Php/IteratorToArrayFunctionReturnTypeExtension.php +++ b/src/Type/Php/IteratorToArrayFunctionReturnTypeExtension.php @@ -8,7 +8,9 @@ use PHPStan\Type\Accessory\AccessoryArrayListType; use PHPStan\Type\ArrayType; use PHPStan\Type\DynamicFunctionReturnTypeExtension; +use PHPStan\Type\ErrorType; use PHPStan\Type\IntegerType; +use PHPStan\Type\NeverType; use PHPStan\Type\Type; use function strtolower; @@ -29,7 +31,12 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection, } $traversableType = $scope->getType($arguments[0]->value); - $arrayKeyType = $traversableType->getIterableKeyType(); + $arrayKeyType = $traversableType->getIterableKeyType()->toArrayKey(); + + if ($arrayKeyType instanceof ErrorType) { + return new NeverType(true); + } + $isList = false; if (isset($arguments[1])) { diff --git a/tests/PHPStan/Analyser/nsrt/iterator_to_array.php b/tests/PHPStan/Analyser/nsrt/iterator_to_array.php index 64ecbdeb05..4c7ddbc2b0 100644 --- a/tests/PHPStan/Analyser/nsrt/iterator_to_array.php +++ b/tests/PHPStan/Analyser/nsrt/iterator_to_array.php @@ -2,6 +2,7 @@ namespace IteratorToArray; +use stdClass; use Traversable; use function iterator_to_array; use function PHPStan\Testing\assertType; @@ -31,4 +32,33 @@ public function testNotPreservingKeys(Traversable $foo) { assertType('list', iterator_to_array($foo, false)); } + + public function testBehaviorOnGenerators(): void + { + $generator1 = static function (): iterable { + yield 0 => 1; + yield true => 2; + yield 2 => 3; + yield null => 4; + }; + $generator2 = static function (): iterable { + yield 0 => 1; + yield 'a' => 2; + yield null => 3; + yield true => 4; + }; + + assertType('array<0|1|2|\'\', 1|2|3|4>', iterator_to_array($generator1())); + assertType('array<0|1|\'\'|\'a\', 1|2|3|4>', iterator_to_array($generator2())); + } + + public function testOnGeneratorsWithIllegalKeysForArray(): void + { + $illegalGenerator = static function (): iterable { + yield 'a' => 'b'; + yield new stdClass => 'c'; + }; + + assertType('*NEVER*', iterator_to_array($illegalGenerator())); + } }