diff --git a/src/Type/Constant/ConstantArrayType.php b/src/Type/Constant/ConstantArrayType.php index 259952b41b..c70240e699 100644 --- a/src/Type/Constant/ConstantArrayType.php +++ b/src/Type/Constant/ConstantArrayType.php @@ -50,7 +50,6 @@ use function array_merge; use function array_pop; use function array_push; -use function array_reverse; use function array_slice; use function array_unique; use function array_values; @@ -886,14 +885,16 @@ public function popArray(): Type public function reverseArray(TrinaryLogic $preserveKeys): Type { - $keyTypesReversed = array_reverse($this->keyTypes, true); - $keyTypes = array_values($keyTypesReversed); - $keyTypesReversedKeys = array_keys($keyTypesReversed); - $optionalKeys = array_map(static fn (int $optionalKey): int => $keyTypesReversedKeys[$optionalKey], $this->optionalKeys); + $builder = ConstantArrayTypeBuilder::createEmpty(); - $reversed = new self($keyTypes, array_reverse($this->valueTypes), $this->nextAutoIndexes, $optionalKeys, TrinaryLogic::createNo()); + for ($i = count($this->keyTypes) - 1; $i >= 0; $i--) { + $offsetType = $preserveKeys->yes() || $this->keyTypes[$i]->isInteger()->no() + ? $this->keyTypes[$i] + : null; + $builder->setOffsetValueType($offsetType, $this->valueTypes[$i], $this->isOptionalKey($i)); + } - return $preserveKeys->yes() ? $reversed : $reversed->reindex(); + return $builder->getArray(); } public function searchArray(Type $needleType): Type diff --git a/tests/PHPStan/Analyser/nsrt/array-reverse.php b/tests/PHPStan/Analyser/nsrt/array-reverse.php index 413e1d5f2a..28dbd009c4 100644 --- a/tests/PHPStan/Analyser/nsrt/array-reverse.php +++ b/tests/PHPStan/Analyser/nsrt/array-reverse.php @@ -22,8 +22,9 @@ public function normalArrays(array $a, array $b): void /** * @param array{a: 'foo', b: 'bar', c?: 'baz'} $a * @param array{17: 'foo', 19: 'bar'}|array{foo: 17, bar: 19} $b + * @param array{0: 'A', 1?: 'B', 2?: 'C'} $c */ - public function constantArrays(array $a, array $b): void + public function constantArrays(array $a, array $b, array $c): void { assertType('array{}', array_reverse([])); assertType('array{}', array_reverse([], true)); @@ -45,6 +46,9 @@ public function constantArrays(array $a, array $b): void assertType('array{\'bar\', \'foo\'}|array{bar: 19, foo: 17}', array_reverse($b)); assertType('array{19: \'bar\', 17: \'foo\'}|array{bar: 19, foo: 17}', array_reverse($b, true)); + + assertType("array{0: 'A'|'B'|'C', 1?: 'A'|'B', 2?: 'A'}", array_reverse($c)); + assertType("array{2?: 'C', 1?: 'B', 0: 'A'}", array_reverse($c, true)); } /** diff --git a/tests/PHPStan/Rules/Functions/ReturnTypeRuleTest.php b/tests/PHPStan/Rules/Functions/ReturnTypeRuleTest.php index f16d288869..28d766512f 100644 --- a/tests/PHPStan/Rules/Functions/ReturnTypeRuleTest.php +++ b/tests/PHPStan/Rules/Functions/ReturnTypeRuleTest.php @@ -297,4 +297,11 @@ public function testBug8881(): void $this->analyse([__DIR__ . '/data/bug-8881.php'], []); } + public function testBug11549(): void + { + $this->checkExplicitMixed = true; + $this->checkNullables = true; + $this->analyse([__DIR__ . '/data/bug-11549.php'], []); + } + } diff --git a/tests/PHPStan/Rules/Functions/data/bug-11549.php b/tests/PHPStan/Rules/Functions/data/bug-11549.php new file mode 100644 index 0000000000..afc7c1b6eb --- /dev/null +++ b/tests/PHPStan/Rules/Functions/data/bug-11549.php @@ -0,0 +1,13 @@ +