diff --git a/src/Analyser/TypeSpecifier.php b/src/Analyser/TypeSpecifier.php index cceef0e7c0..b114ac8028 100644 --- a/src/Analyser/TypeSpecifier.php +++ b/src/Analyser/TypeSpecifier.php @@ -289,9 +289,15 @@ public function specifyTypesInCondition( $sizeType = $leftType; } - $specifiedTypes = $this->specifyTypesForCountFuncCall($expr->right, $argType, $sizeType, $context, $scope, $expr); - if ($specifiedTypes !== null) { - $result = $result->unionWith($specifiedTypes); + // For range-typed left operands in falsey context ($left <= count($right) + // being false means count < $left), specifyTypesForCountFuncCall would + // incorrectly narrow the array because "count not in sizeType" is stricter + // than "count < some value in sizeType range". + if (!($context->falsey() && $leftType instanceof IntegerRangeType)) { + $specifiedTypes = $this->specifyTypesForCountFuncCall($expr->right, $argType, $sizeType, $context, $scope, $expr); + if ($specifiedTypes !== null) { + $result = $result->unionWith($specifiedTypes); + } } if ( diff --git a/tests/PHPStan/Analyser/nsrt/bug-13705.php b/tests/PHPStan/Analyser/nsrt/bug-13705.php new file mode 100644 index 0000000000..f6813d9895 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-13705.php @@ -0,0 +1,40 @@ +', $codes); + $code = random_bytes(16); + if (!in_array($code, $codes, true)) { + $codes[] = $code; + } + } +} + +function doWhileLoop(): void +{ + $quantity = random_int(1, 42); + $codes = []; + do { + $code = random_bytes(16); + if (!in_array($code, $codes, true)) { + $codes[] = $code; + } + } while (count($codes) < $quantity); +} + +function whileLoopSimple(): void +{ + $quantity = random_int(1, 42); + $codes = []; + while (count($codes) < $quantity) { + assertType('list', $codes); + $codes[] = random_bytes(16); + } +} diff --git a/tests/PHPStan/Analyser/nsrt/bug-4700.php b/tests/PHPStan/Analyser/nsrt/bug-4700.php index 24a680e387..49cea6c59d 100644 --- a/tests/PHPStan/Analyser/nsrt/bug-4700.php +++ b/tests/PHPStan/Analyser/nsrt/bug-4700.php @@ -21,8 +21,8 @@ function(array $array, int $count): void { assertType('int<1, 5>', count($a)); assertType('list{0: mixed~null, 1?: mixed~null, 2?: mixed~null, 3?: mixed~null, 4?: mixed~null}', $a); } else { - assertType('0', count($a)); - assertType('array{}', $a); + assertType('int<0, 5>', count($a)); + assertType('array{}|list{0: mixed~null, 1?: mixed~null, 2?: mixed~null, 3?: mixed~null, 4?: mixed~null}', $a); } }; diff --git a/tests/PHPStan/Analyser/nsrt/bug11480.php b/tests/PHPStan/Analyser/nsrt/bug11480.php index 17077d7bfc..f80a7237ff 100644 --- a/tests/PHPStan/Analyser/nsrt/bug11480.php +++ b/tests/PHPStan/Analyser/nsrt/bug11480.php @@ -106,7 +106,7 @@ public function intRangeCount($count): void if (count($x) >= $count) { assertType("array{'xy'}|array{0: 'ab', 1?: 'xy'}", $x); } else { - assertType("array{}", $x); + assertType("array{}|array{'xy'}|array{0: 'ab', 1?: 'xy'}", $x); } assertType("array{}|array{'xy'}|array{0: 'ab', 1?: 'xy'}", $x); }