diff --git a/src/Type/Constant/ConstantArrayTypeBuilder.php b/src/Type/Constant/ConstantArrayTypeBuilder.php index db110bfa26..d7718f9014 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; @@ -13,7 +14,6 @@ use function count; use function is_float; use function max; -use function range; /** @api */ class ConstantArrayTypeBuilder @@ -101,34 +101,57 @@ 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) { - if ($integerRange->getMin() === null) { + $minRange = $integerRange->getMin(); + if ($minRange === null) { break; } - if ($integerRange->getMax() === null) { + + $maxRange = $integerRange->getMax(); + if ($maxRange === null) { break; } - foreach (range($integerRange->getMin(), $integerRange->getMax()) 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 IntegerRangeType) { + $match = false; + + break; + } + if (!$scalarOffsetType instanceof ConstantIntegerType && !$scalarOffsetType instanceof ConstantStringType) { throw new ShouldNotHappenException(); } - $offsetMatch = false; + $offsetMatch = false; /** @var ConstantIntegerType|ConstantStringType $keyType */ foreach ($this->keyTypes as $i => $keyType) { + if ($keyType->getValue() !== $scalarOffsetType->getValue()) { continue; } diff --git a/tests/PHPStan/PhpDoc/TypeDescriptionTest.php b/tests/PHPStan/PhpDoc/TypeDescriptionTest.php index 29c3a35231..575e4db23b 100644 --- a/tests/PHPStan/PhpDoc/TypeDescriptionTest.php +++ b/tests/PHPStan/PhpDoc/TypeDescriptionTest.php @@ -9,9 +9,12 @@ use PHPStan\Type\Accessory\NonEmptyArrayType; use PHPStan\Type\ArrayType; use PHPStan\Type\ClassStringType; +use PHPStan\Type\Constant\ConstantArrayType; use PHPStan\Type\Constant\ConstantArrayTypeBuilder; +use PHPStan\Type\Constant\ConstantIntegerType; use PHPStan\Type\Constant\ConstantStringType; use PHPStan\Type\Generic\GenericClassStringType; +use PHPStan\Type\IntegerRangeType; use PHPStan\Type\IntegerType; use PHPStan\Type\IntersectionType; use PHPStan\Type\MixedType; @@ -67,6 +70,24 @@ public function dataTest(): iterable $builder = ConstantArrayTypeBuilder::createEmpty(); $builder->setOffsetValueType(new ConstantStringType('"foo"'), new IntegerType()); yield ['array{\'"foo"\': int}', $builder->getArray()]; + + $builder = ConstantArrayTypeBuilder::createFromConstantArray( + new ConstantArrayType( + [new ConstantIntegerType(0), new ConstantIntegerType(1)], + [new StringType(), new StringType()], + ), + ); + $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()]; } /**