diff --git a/src/Type/Constant/ConstantArrayType.php b/src/Type/Constant/ConstantArrayType.php index fbb1217057..5c867649f6 100644 --- a/src/Type/Constant/ConstantArrayType.php +++ b/src/Type/Constant/ConstantArrayType.php @@ -1416,7 +1416,6 @@ private function getKeysOrValuesArray(array $types): self { $count = count($types); $autoIndexes = range($count - count($this->optionalKeys), $count); - assert($autoIndexes !== []); if ($this->isList->yes()) { // Optimized version for lists: Assume that if a later key exists, then earlier keys also exist. diff --git a/src/Type/Php/RangeFunctionReturnTypeExtension.php b/src/Type/Php/RangeFunctionReturnTypeExtension.php index ce5116e7fc..45f6fffc9e 100644 --- a/src/Type/Php/RangeFunctionReturnTypeExtension.php +++ b/src/Type/Php/RangeFunctionReturnTypeExtension.php @@ -92,39 +92,25 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection, $startConstant = $endConstant; $endConstant = $tmp; } - return new IntersectionType([ - new ArrayType( - IntegerRangeType::createAllGreaterThanOrEqualTo(0), - IntegerRangeType::fromInterval($startConstant->getValue(), $endConstant->getValue()), + return self::getNonEmptyListOfType( + IntegerRangeType::fromInterval( + $startConstant->getValue(), + $endConstant->getValue(), ), - new NonEmptyArrayType(), - new AccessoryArrayListType(), - ]); + ); } if ($stepType->isFloat()->yes()) { - return new IntersectionType([ - new ArrayType( - IntegerRangeType::createAllGreaterThanOrEqualTo(0), - new FloatType(), - ), - new NonEmptyArrayType(), - new AccessoryArrayListType(), - ]); + return self::getNonEmptyListOfType(new FloatType()); } - return new IntersectionType([ - new ArrayType( - IntegerRangeType::createAllGreaterThanOrEqualTo(0), - TypeCombinator::union( - $startConstant->generalize(GeneralizePrecision::moreSpecific()), - $endConstant->generalize(GeneralizePrecision::moreSpecific()), - $stepType->generalize(GeneralizePrecision::moreSpecific()), - ), + return self::getNonEmptyListOfType( + TypeCombinator::union( + $startConstant->generalize(GeneralizePrecision::moreSpecific()), + $endConstant->generalize(GeneralizePrecision::moreSpecific()), + $stepType->generalize(GeneralizePrecision::moreSpecific()), ), - new NonEmptyArrayType(), - new AccessoryArrayListType(), - ]); + ); } $arrayBuilder = ConstantArrayTypeBuilder::createEmpty(); foreach ($rangeValues as $value) { @@ -146,30 +132,45 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection, if ($isInteger && $isStepInteger) { if ($argType instanceof IntegerRangeType) { - return new IntersectionType([new ArrayType(IntegerRangeType::createAllGreaterThanOrEqualTo(0), $argType), new AccessoryArrayListType()]); + return self::getNonEmptyListOfType($argType); } - return new IntersectionType([new ArrayType(IntegerRangeType::createAllGreaterThanOrEqualTo(0), new IntegerType()), new AccessoryArrayListType()]); + return self::getNonEmptyListOfType(new IntegerType()); } if ($argType->isFloat()->yes()) { - return new IntersectionType([new ArrayType(IntegerRangeType::createAllGreaterThanOrEqualTo(0), new FloatType()), new AccessoryArrayListType()]); + return self::getNonEmptyListOfType(new FloatType()); } $numberType = new UnionType([new IntegerType(), new FloatType()]); $isNumber = $numberType->isSuperTypeOf($argType)->yes(); $isNumericString = $argType->isNumericString()->yes(); if ($isNumber || $isNumericString) { - return new IntersectionType([new ArrayType(IntegerRangeType::createAllGreaterThanOrEqualTo(0), $numberType), new AccessoryArrayListType()]); + return self::getNonEmptyListOfType($numberType); } if ($argType->isString()->yes()) { - return new IntersectionType([new ArrayType(IntegerRangeType::createAllGreaterThanOrEqualTo(0), new StringType()), new AccessoryArrayListType()]); + return self::getNonEmptyListOfType(new StringType()); } - return new IntersectionType([new ArrayType( - IntegerRangeType::createAllGreaterThanOrEqualTo(0), - new BenevolentUnionType([new IntegerType(), new FloatType(), new StringType()]), - ), new AccessoryArrayListType()]); + return self::getNonEmptyListOfType( + new BenevolentUnionType([ + new IntegerType(), + new FloatType(), + new StringType(), + ]), + ); + } + + private static function getNonEmptyListOfType(Type $type): IntersectionType + { + return new IntersectionType([ + new ArrayType( + IntegerRangeType::createAllGreaterThanOrEqualTo(0), + $type, + ), + new NonEmptyArrayType(), + new AccessoryArrayListType(), + ]); } } diff --git a/tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php b/tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php index 55b868f5c1..535d7f740d 100644 --- a/tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php @@ -5594,19 +5594,19 @@ public static function dataRangeFunction(): array 'range(2.1, 5)', ], [ - 'list', + 'non-empty-list', 'range(2, 5, $integer)', ], [ - 'list', + 'non-empty-list', 'range($float, 5, $integer)', ], [ - 'list<(float|int|string)>', + 'non-empty-list<(float|int|string)>', 'range($float, $mixed, $integer)', ], [ - 'list<(float|int|string)>', + 'non-empty-list<(float|int|string)>', 'range($integer, $mixed)', ], [ diff --git a/tests/PHPStan/Analyser/nsrt/bug-11692.php b/tests/PHPStan/Analyser/nsrt/bug-11692.php index c1edbba1fd..628346f085 100644 --- a/tests/PHPStan/Analyser/nsrt/bug-11692.php +++ b/tests/PHPStan/Analyser/nsrt/bug-11692.php @@ -12,13 +12,12 @@ function doFoo(int $i, float $f, $floatOrInt): void { assertType('non-empty-list', range(1, 9999, .01)); assertType('non-empty-list>', range(1, 9999, 3)); - assertType('list', range(1, 9999, $floatOrInt)); - assertType('list', range(1, 9999, $floatOrInt)); + assertType('non-empty-list', range(1, 9999, $floatOrInt)); - assertType('list', range(1, 3, $i)); - assertType('list', range(1, 3, $f)); + assertType('non-empty-list', range(1, 3, $i)); + assertType('non-empty-list', range(1, 3, $f)); - assertType('list', range(1, 9999, $i)); - assertType('list', range(1, 9999, $f)); + assertType('non-empty-list', range(1, 9999, $i)); + assertType('non-empty-list', range(1, 9999, $f)); } diff --git a/tests/PHPStan/Analyser/nsrt/bug-2378.php b/tests/PHPStan/Analyser/nsrt/bug-2378.php index a05de0f302..7d0e032f82 100644 --- a/tests/PHPStan/Analyser/nsrt/bug-2378.php +++ b/tests/PHPStan/Analyser/nsrt/bug-2378.php @@ -17,7 +17,7 @@ public function doFoo( assertType('array{\'a\', \'b\', \'c\', \'d\'}', range('a', 'd')); assertType('array{\'a\', \'c\', \'e\', \'g\', \'i\'}', range('a', 'i', 2)); - assertType('list', range($s, $s)); + assertType('non-empty-list', range($s, $s)); } } diff --git a/tests/PHPStan/Analyser/nsrt/range-int-range.php b/tests/PHPStan/Analyser/nsrt/range-int-range.php index f1846aad71..032c801d8e 100644 --- a/tests/PHPStan/Analyser/nsrt/range-int-range.php +++ b/tests/PHPStan/Analyser/nsrt/range-int-range.php @@ -16,7 +16,7 @@ public function zeroToMax( int $b ): void { - assertType('list>', range($a, $b)); + assertType('non-empty-list>', range($a, $b)); } /** @@ -28,7 +28,7 @@ public function twoToTwenty( int $b ): void { - assertType('list>', range($a, $b)); + assertType('non-empty-list>', range($a, $b)); } /** @@ -40,7 +40,7 @@ public function fifteenTo5( int $b ): void { - assertType('list>', range($a, $b)); + assertType('non-empty-list>', range($a, $b)); } public function knownRange( diff --git a/tests/PHPStan/Analyser/nsrt/range-numeric-string.php b/tests/PHPStan/Analyser/nsrt/range-numeric-string.php index bae424e559..67ecac356b 100644 --- a/tests/PHPStan/Analyser/nsrt/range-numeric-string.php +++ b/tests/PHPStan/Analyser/nsrt/range-numeric-string.php @@ -16,7 +16,7 @@ public function doFoo( string $b ): void { - assertType('list', range($a, $b)); + assertType('non-empty-list', range($a, $b)); } }