diff --git a/src/Type/Constant/ConstantArrayTypeBuilder.php b/src/Type/Constant/ConstantArrayTypeBuilder.php index b1a68ac083..fcefe266fb 100644 --- a/src/Type/Constant/ConstantArrayTypeBuilder.php +++ b/src/Type/Constant/ConstantArrayTypeBuilder.php @@ -8,6 +8,8 @@ use PHPStan\Type\Accessory\NonEmptyArrayType; use PHPStan\Type\Accessory\OversizedArrayType; use PHPStan\Type\ArrayType; +use PHPStan\Type\CallableType; +use PHPStan\Type\ClosureType; use PHPStan\Type\IntersectionType; use PHPStan\Type\Type; use PHPStan\Type\TypeCombinator; @@ -29,9 +31,12 @@ final class ConstantArrayTypeBuilder { public const ARRAY_COUNT_LIMIT = 256; + private const CLOSURES_COUNT_LIMIT = 16; private bool $degradeToGeneralArray = false; + private bool $degradeClosures = false; + private bool $oversized = false; /** @@ -79,6 +84,23 @@ public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $opt } if (!$this->degradeToGeneralArray) { + if ($valueType instanceof ClosureType) { + $numClosures = 1; + foreach ($this->valueTypes as $innerType) { + if (!($innerType instanceof ClosureType)) { + continue; + } + + $numClosures++; + } + + if ($numClosures >= self::CLOSURES_COUNT_LIMIT) { + $this->degradeClosures = true; + $this->degradeToGeneralArray = true; + $this->oversized = true; + } + } + if ($offsetType === null) { $newAutoIndexes = $optional ? $this->nextAutoIndexes : []; $hasOptional = false; @@ -291,9 +313,22 @@ public function getArray(): Type return new ConstantArrayType($keyTypes, $this->valueTypes, $this->nextAutoIndexes, $this->optionalKeys, $this->isList); } + if ($this->degradeClosures) { + $itemTypes = []; + $itemTypes[] = new CallableType(); + foreach ($this->valueTypes as $valueType) { + if ($valueType instanceof ClosureType) { + continue; + } + $itemTypes[] = $valueType; + } + } else { + $itemTypes = $this->valueTypes; + } + $array = new ArrayType( TypeCombinator::union(...$this->keyTypes), - TypeCombinator::union(...$this->valueTypes), + TypeCombinator::union(...$itemTypes), ); $types = []; diff --git a/tests/PHPStan/Analyser/AnalyserIntegrationTest.php b/tests/PHPStan/Analyser/AnalyserIntegrationTest.php index a3e9095a89..fed5cb2c75 100644 --- a/tests/PHPStan/Analyser/AnalyserIntegrationTest.php +++ b/tests/PHPStan/Analyser/AnalyserIntegrationTest.php @@ -1570,6 +1570,12 @@ public function testBug13714(): void $this->assertSame('Function Bug13714\array_find invoked with 2 parameters, 0 required.', $errors[6]->getMessage()); } + public function testBug13933(): void + { + $errors = $this->runAnalyse(__DIR__ . '/data/bug-13933.php'); + $this->assertNoErrors($errors); + } + /** * @param string[]|null $allAnalysedFiles * @return list diff --git a/tests/PHPStan/Analyser/data/bug-13933.php b/tests/PHPStan/Analyser/data/bug-13933.php new file mode 100644 index 0000000000..711ce8cbee --- /dev/null +++ b/tests/PHPStan/Analyser/data/bug-13933.php @@ -0,0 +1,311 @@ +&oversized-array', $arr);