diff --git a/src/Type/Constant/ConstantArrayTypeBuilder.php b/src/Type/Constant/ConstantArrayTypeBuilder.php index 190b95a8aba..5a61feea503 100644 --- a/src/Type/Constant/ConstantArrayTypeBuilder.php +++ b/src/Type/Constant/ConstantArrayTypeBuilder.php @@ -5,6 +5,7 @@ use PHPStan\ShouldNotHappenException; use PHPStan\Type\Accessory\NonEmptyArrayType; use PHPStan\Type\ArrayType; +use PHPStan\Type\IntegerRangeType; use PHPStan\Type\Type; use PHPStan\Type\TypeCombinator; use PHPStan\Type\TypeUtils; @@ -101,37 +102,59 @@ public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $opt $scalarTypes = TypeUtils::getConstantScalars($offsetType); if (count($scalarTypes) === 0) { - $integerRanges = TypeUtils::getIntegerRanges($offsetType); + $integerRangesBackup = TypeUtils::getIntegerRanges($offsetType); + $integerRanges = $integerRangesBackup; if (count($integerRanges) > 0) { foreach ($integerRanges as $integerRange) { $minRange = $integerRange->getMin(); - if ($minRange === null || $minRange <= -self::ARRAY_COUNT_LIMIT) { + if ($minRange === null) { break; } $maxRange = $integerRange->getMax(); - if ($maxRange === null || $maxRange >= self::ARRAY_COUNT_LIMIT) { + if ($maxRange === null) { break; } - foreach (range($minRange, $maxRange) as $rangeValue) { + $rangeValue = $minRange; + $rangeCount = 0; + do { + $rangeCount++; + if ($rangeCount > self::ARRAY_COUNT_LIMIT) { + $scalarTypes = $integerRangesBackup; + + break; + } + $scalarTypes[] = new ConstantIntegerType($rangeValue); - } + $rangeValue++; + } while ($rangeValue <= $maxRange); } } } + if (count($scalarTypes) > 0 && count($scalarTypes) < self::ARRAY_COUNT_LIMIT) { $match = true; $valueTypes = $this->valueTypes; foreach ($scalarTypes as $scalarType) { $scalarOffsetType = ArrayType::castToArrayKeyType($scalarType); - if (!$scalarOffsetType instanceof ConstantIntegerType && !$scalarOffsetType instanceof ConstantStringType) { + if ( + !$scalarOffsetType instanceof ConstantIntegerType + && !$scalarOffsetType instanceof ConstantStringType + && !$scalarOffsetType instanceof IntegerRangeType + ) { throw new ShouldNotHappenException(); } $offsetMatch = false; - /** @var ConstantIntegerType|ConstantStringType $keyType */ + /** @var ConstantIntegerType|ConstantStringType|IntegerRangeType $keyType */ foreach ($this->keyTypes as $i => $keyType) { + if ($scalarOffsetType instanceof IntegerRangeType) { + $match = false; + + break; + } + if ($keyType->getValue() !== $scalarOffsetType->getValue()) { continue; } diff --git a/tests/PHPStan/PhpDoc/TypeDescriptionTest.php b/tests/PHPStan/PhpDoc/TypeDescriptionTest.php index c6fc0d9ab27..575e4db23bd 100644 --- a/tests/PHPStan/PhpDoc/TypeDescriptionTest.php +++ b/tests/PHPStan/PhpDoc/TypeDescriptionTest.php @@ -79,6 +79,15 @@ public function dataTest(): iterable ); $builder->setOffsetValueType(IntegerRangeType::fromInterval(-2147483648, 2147483647), new StringType()); yield ['non-empty-array, string>', $builder->getArray()]; + + $builder = ConstantArrayTypeBuilder::createFromConstantArray( + new ConstantArrayType( + [new ConstantIntegerType(0), new ConstantIntegerType(20)], + [new StringType(), new StringType()], + ), + ); + $builder->setOffsetValueType(IntegerRangeType::fromInterval(1, 19), new StringType()); + yield ['non-empty-array, string>', $builder->getArray()]; } /**