diff --git a/src/Type/Constant/ConstantArrayType.php b/src/Type/Constant/ConstantArrayType.php index a7c4d14510..29e384eda8 100644 --- a/src/Type/Constant/ConstantArrayType.php +++ b/src/Type/Constant/ConstantArrayType.php @@ -9,6 +9,7 @@ use PHPStan\Reflection\TrivialParametersAcceptor; use PHPStan\ShouldNotHappenException; use PHPStan\TrinaryLogic; +use PHPStan\Type\Accessory\HasOffsetValueType; use PHPStan\Type\Accessory\NonEmptyArrayType; use PHPStan\Type\ArrayType; use PHPStan\Type\BooleanType; @@ -952,7 +953,23 @@ public function generalize(GeneralizePrecision $precision): Type $this->getItemType()->generalize($precision), ); - if (count($this->keyTypes) > count($this->optionalKeys)) { + $keyTypesCount = count($this->keyTypes); + $optionalKeysCount = count($this->optionalKeys); + + if ($precision->isMoreSpecific() && ($keyTypesCount - $optionalKeysCount) < 32) { + $accessoryTypes = []; + foreach ($this->keyTypes as $i => $keyType) { + if ($this->isOptionalKey($i)) { + continue; + } + + $accessoryTypes[] = new HasOffsetValueType($keyType, $this->valueTypes[$i]); + } + + return TypeCombinator::intersect($arrayType, ...$accessoryTypes); + } + + if ($keyTypesCount > $optionalKeysCount) { return TypeCombinator::intersect($arrayType, new NonEmptyArrayType()); } diff --git a/src/Type/TypeCombinator.php b/src/Type/TypeCombinator.php index 7e4f8ee93c..e7ec286826 100644 --- a/src/Type/TypeCombinator.php +++ b/src/Type/TypeCombinator.php @@ -597,7 +597,7 @@ private static function processArrayTypes(array $arrayTypes, array $accessoryTyp $keyTypesForGeneralArray = []; $valueTypesForGeneralArray = []; - $generalArrayOccurred = self::shouldDegradeConstantArrays($arrayTypes); + $generalArrayOccurred = false; $constantKeyTypesNumbered = []; /** @var int|float $nextConstantKeyTypeIndex */ @@ -638,25 +638,29 @@ private static function processArrayTypes(array $arrayTypes, array $accessoryTyp ]; } + $reducedArrayTypes = self::reduceArrays($arrayTypes); + return array_map( static fn (Type $arrayType) => self::intersect($arrayType, ...$accessoryTypes), - self::reduceArrays($arrayTypes), + self::optimizeConstantArrays($reducedArrayTypes), ); } /** - * @param ArrayType[] $arrayTypes + * @param Type[] $types + * @return Type[] */ - private static function shouldDegradeConstantArrays(array $arrayTypes): bool + private static function optimizeConstantArrays(array $types): array { + $results = []; $constantArrayValuesCount = 0; - foreach ($arrayTypes as $arrayType) { - TypeTraverser::map($arrayType, static function (Type $type, callable $traverse) use (&$constantArrayValuesCount): Type { + foreach ($types as $type) { + $results[] = TypeTraverser::map($type, static function (Type $type, callable $traverse) use (&$constantArrayValuesCount): Type { if ($type instanceof ConstantArrayType) { - $constantArrayValuesCount += count($type->getValueTypes()); if ($constantArrayValuesCount > ConstantArrayTypeBuilder::ARRAY_COUNT_LIMIT) { - return $type; + return $type->generalize(GeneralizePrecision::moreSpecific()); } + $constantArrayValuesCount += count($type->getValueTypes()); return $traverse($type); } @@ -669,7 +673,7 @@ private static function shouldDegradeConstantArrays(array $arrayTypes): bool }); } - return $constantArrayValuesCount > ConstantArrayTypeBuilder::ARRAY_COUNT_LIMIT; + return $results; } /** diff --git a/tests/PHPStan/Analyser/AnalyserIntegrationTest.php b/tests/PHPStan/Analyser/AnalyserIntegrationTest.php index bfa2972513..f7c69e802a 100644 --- a/tests/PHPStan/Analyser/AnalyserIntegrationTest.php +++ b/tests/PHPStan/Analyser/AnalyserIntegrationTest.php @@ -931,7 +931,7 @@ public function testBug7581(): void public function testBug7903(): void { $errors = $this->runAnalyse(__DIR__ . '/data/bug-7903.php'); - $this->assertCount(7, $errors); + $this->assertNoErrors($errors); } public function testBug7901(): void