From c15804c1471a827bfc130024df91cbf7f6789f14 Mon Sep 17 00:00:00 2001 From: Martin Herndl Date: Thu, 16 Jun 2022 21:49:14 +0200 Subject: [PATCH] Array after `array_push` / `array_unshift` call can still be empty --- src/Analyser/NodeScopeResolver.php | 14 +++++--- tests/PHPStan/Analyser/data/array-push.php | 10 ++++-- tests/PHPStan/Analyser/data/array-unshift.php | 10 ++++-- .../PHPStan/Rules/Variables/EmptyRuleTest.php | 8 +++++ .../PHPStan/Rules/Variables/data/bug-7424.php | 35 +++++++++++++++++++ 5 files changed, 67 insertions(+), 10 deletions(-) create mode 100644 tests/PHPStan/Rules/Variables/data/bug-7424.php diff --git a/src/Analyser/NodeScopeResolver.php b/src/Analyser/NodeScopeResolver.php index 5507652bdf1..5b8670d3ddc 100644 --- a/src/Analyser/NodeScopeResolver.php +++ b/src/Analyser/NodeScopeResolver.php @@ -1825,7 +1825,7 @@ function (MutatingScope $scope) use ($expr, $nodeCallback, $context): Expression /** * @param Arg[] $callArgs - * @param callable(?Type, Type, bool=): void $setOffsetValueType + * @param callable(?Type, Type, bool): void $setOffsetValueType */ $setOffsetValueTypes = static function (Scope $scope, array $callArgs, callable $setOffsetValueType, ?bool &$nonConstantArrayWasUnpacked = null): void { foreach ($callArgs as $callArg) { @@ -1850,7 +1850,7 @@ function (MutatingScope $scope) use ($expr, $nodeCallback, $context): Expression } continue; } - $setOffsetValueType(null, $callArgType); + $setOffsetValueType(null, $callArgType, false); } }; @@ -1861,7 +1861,7 @@ function (MutatingScope $scope) use ($expr, $nodeCallback, $context): Expression $setOffsetValueTypes( $scope, $callArgs, - static function (?Type $offsetType, Type $valueType, bool $optional = false) use (&$arrayTypeBuilder): void { + static function (?Type $offsetType, Type $valueType, bool $optional) use (&$arrayTypeBuilder): void { $arrayTypeBuilder->setOffsetValueType($offsetType, $valueType, $optional); }, $nonConstantArrayWasUnpacked, @@ -1890,8 +1890,14 @@ static function (?Type $offsetType, Type $valueType, bool $optional = false) use $setOffsetValueTypes( $scope, $callArgs, - static function (?Type $offsetType, Type $valueType) use (&$arrayType): void { + static function (?Type $offsetType, Type $valueType, bool $optional) use (&$arrayType): void { + $isIterableAtLeastOnce = $arrayType->isIterableAtLeastOnce()->yes() || !$optional; $arrayType = $arrayType->setOffsetValueType($offsetType, $valueType); + if ($isIterableAtLeastOnce) { + return; + } + + $arrayType = TypeCombinator::union(...TypeUtils::getArrays($arrayType)); }, ); } diff --git a/tests/PHPStan/Analyser/data/array-push.php b/tests/PHPStan/Analyser/data/array-push.php index e4a42922c55..8aaaf3326db 100644 --- a/tests/PHPStan/Analyser/data/array-push.php +++ b/tests/PHPStan/Analyser/data/array-push.php @@ -13,10 +13,14 @@ * @param non-empty-array $c * @param array $d */ -function arrayPush(array $a, array $b, array $c, array $d): void +function arrayPush(array $a, array $b, array $c, array $d, array $arr): void { array_push($a, ...$b); - assertType('non-empty-array', $a); + assertType('array', $a); + + /** @var non-empty-array $arr */ + array_push($arr, ...$b); + assertType('non-empty-array', $arr); array_push($b, ...[]); assertType('array', $b); @@ -27,7 +31,7 @@ function arrayPush(array $a, array $b, array $c, array $d): void /** @var array $d1 */ $d1 = []; array_push($d, ...$d1); - assertType('non-empty-array', $d); + assertType('array', $d); } function arrayPushConstantArray(): void diff --git a/tests/PHPStan/Analyser/data/array-unshift.php b/tests/PHPStan/Analyser/data/array-unshift.php index 3b8d6484552..1483b1497b1 100644 --- a/tests/PHPStan/Analyser/data/array-unshift.php +++ b/tests/PHPStan/Analyser/data/array-unshift.php @@ -13,10 +13,14 @@ * @param non-empty-array $c * @param array $d */ -function arrayUnshift(array $a, array $b, array $c, array $d): void +function arrayUnshift(array $a, array $b, array $c, array $d, array $arr): void { array_unshift($a, ...$b); - assertType('non-empty-array', $a); + assertType('array', $a); + + /** @var non-empty-array $arr */ + array_push($arr, ...$b); + assertType('non-empty-array', $arr); array_unshift($b, ...[]); assertType('array', $b); @@ -27,7 +31,7 @@ function arrayUnshift(array $a, array $b, array $c, array $d): void /** @var array $d1 */ $d1 = []; array_unshift($d, ...$d1); - assertType('non-empty-array', $d); + assertType('array', $d); } function arrayUnshiftConstantArray(): void diff --git a/tests/PHPStan/Rules/Variables/EmptyRuleTest.php b/tests/PHPStan/Rules/Variables/EmptyRuleTest.php index 1d457ba1c32..e0e62375942 100644 --- a/tests/PHPStan/Rules/Variables/EmptyRuleTest.php +++ b/tests/PHPStan/Rules/Variables/EmptyRuleTest.php @@ -169,4 +169,12 @@ public function testBug7318(): void $this->analyse([__DIR__ . '/../Properties/data/bug-7318.php'], []); } + public function testBug7424(): void + { + $this->treatPhpDocTypesAsCertain = true; + $this->strictUnnecessaryNullsafePropertyFetch = false; + + $this->analyse([__DIR__ . '/data/bug-7424.php'], []); + } + } diff --git a/tests/PHPStan/Rules/Variables/data/bug-7424.php b/tests/PHPStan/Rules/Variables/data/bug-7424.php new file mode 100644 index 00000000000..ac685e0b195 --- /dev/null +++ b/tests/PHPStan/Rules/Variables/data/bug-7424.php @@ -0,0 +1,35 @@ +getInitData(); + + array_push($data, ...$this->getExtra()); + + if (empty($data)) { + return; + } + + echo 'Proceeding to process data'; + } + + /** + * @return string[] + */ + protected function getInitData(): array + { + return []; + } + + /** + * @return string[] + */ + protected function getExtra(): array + { + return []; + } +}