diff --git a/src/Type/Php/ArrayColumnFunctionReturnTypeExtension.php b/src/Type/Php/ArrayColumnFunctionReturnTypeExtension.php index 627b2c21d0..43da514a62 100644 --- a/src/Type/Php/ArrayColumnFunctionReturnTypeExtension.php +++ b/src/Type/Php/ArrayColumnFunctionReturnTypeExtension.php @@ -90,7 +90,7 @@ private function handleAnyArray(Type $arrayType, Type $columnType, ?Type $indexT $returnKeyType = new IntegerType(); } - $returnType = new ArrayType($returnKeyType, $returnValueType); + $returnType = new ArrayType($this->castToArrayKeyType($returnKeyType), $returnValueType); if ($iterableAtLeastOnce->yes()) { $returnType = TypeCombinator::intersect($returnType, new NonEmptyArrayType()); @@ -125,6 +125,9 @@ private function handleConstantArray(ConstantArrayType $arrayType, Type $columnT $keyType = null; } + if ($keyType !== null) { + $keyType = $this->castToArrayKeyType($keyType); + } $builder->setOffsetValueType($keyType, $valueType); } @@ -180,4 +183,19 @@ private function getOffsetOrProperty(Type $type, Type $offsetOrProperty, Scope $ return TypeCombinator::union(...$returnTypes); } + private function castToArrayKeyType(Type $type): Type + { + $isArray = $type->isArray(); + if ($isArray->yes()) { + return new IntegerType(); + } + if ($isArray->no()) { + return ArrayType::castToArrayKeyType($type); + } + return TypeCombinator::union( + ArrayType::castToArrayKeyType(TypeCombinator::remove($type, new ArrayType(new MixedType(), new MixedType()))), + new IntegerType(), + ); + } + } diff --git a/tests/PHPStan/Analyser/NodeScopeResolverTest.php b/tests/PHPStan/Analyser/NodeScopeResolverTest.php index b9b0fdf782..2392bc3322 100644 --- a/tests/PHPStan/Analyser/NodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/NodeScopeResolverTest.php @@ -750,6 +750,7 @@ public function dataFileAsserts(): iterable yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-6699.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-6715.php'); + yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-6682.php'); } /** diff --git a/tests/PHPStan/Analyser/data/array-column.php b/tests/PHPStan/Analyser/data/array-column.php index 36430a1b9d..8a48236ad4 100644 --- a/tests/PHPStan/Analyser/data/array-column.php +++ b/tests/PHPStan/Analyser/data/array-column.php @@ -20,6 +20,17 @@ function testArrays(array $array): void assertType('array{}', array_column($array, 'column')); assertType('array{}', array_column($array, 'column', 'key')); assertType('array{}', array_column($array, null, 'key')); + + /** @var array> $array */ + assertType('array', array_column($array, 'column', 'key')); + /** @var array> $array */ + assertType('array', array_column($array, 'column', 'key')); + /** @var array> $array */ + assertType('array', array_column($array, 'column', 'key')); + /** @var array> $array */ + assertType('array<\'\'|int, null>', array_column($array, 'column', 'key')); + /** @var array> $array */ + assertType('array', array_column($array, 'column', 'key')); } function testConstantArrays(array $array): void @@ -54,6 +65,17 @@ function testConstantArrays(array $array): void assertType('non-empty-array', array_column($array, 'column')); assertType('non-empty-array', array_column($array, 'column', 'key')); assertType('non-empty-array', array_column($array, null, 'key')); + + /** @var array $array */ + assertType('array', array_column($array, 'column', 'key')); + /** @var array $array */ + assertType('array<0|1, string>', array_column($array, 'column', 'key')); + /** @var array $array */ + assertType('array<1, string>', array_column($array, 'column', 'key')); + /** @var array $array */ + assertType('array<\'\', string>', array_column($array, 'column', 'key')); + /** @var array $array */ + assertType('array', array_column($array, 'column', 'key')); } function testImprecise(array $array): void { @@ -84,8 +106,8 @@ function testObjects(array $array): void { assertType('array', array_column($array, null, 'tagName')); assertType('array', array_column($array, 'foo')); assertType('array', array_column($array, 'foo', 'tagName')); - assertType('array', array_column($array, 'nodeName', 'foo')); - assertType('array', array_column($array, null, 'foo')); + assertType('array', array_column($array, 'nodeName', 'foo')); + assertType('array', array_column($array, null, 'foo')); /** @var non-empty-array $array */ assertType('non-empty-array', array_column($array, 'nodeName')); @@ -93,8 +115,8 @@ function testObjects(array $array): void { assertType('non-empty-array', array_column($array, null, 'tagName')); assertType('array', array_column($array, 'foo')); assertType('array', array_column($array, 'foo', 'tagName')); - assertType('non-empty-array', array_column($array, 'nodeName', 'foo')); - assertType('non-empty-array', array_column($array, null, 'foo')); + assertType('non-empty-array', array_column($array, 'nodeName', 'foo')); + assertType('non-empty-array', array_column($array, null, 'foo')); /** @var array{DOMElement} $array */ assertType('array{string}', array_column($array, 'nodeName')); diff --git a/tests/PHPStan/Analyser/data/bug-6682.php b/tests/PHPStan/Analyser/data/bug-6682.php new file mode 100644 index 0000000000..cdb2738ceb --- /dev/null +++ b/tests/PHPStan/Analyser/data/bug-6682.php @@ -0,0 +1,19 @@ +|null>> $data + */ + public function __construct(array $data) + { + $x = array_column($data, null, 'type'); + assertType('array|string|null>>', $x); + } +}