diff --git a/conf/config.neon b/conf/config.neon index 751c0507a4..528e35e6d6 100644 --- a/conf/config.neon +++ b/conf/config.neon @@ -111,6 +111,7 @@ parameters: reportMagicProperties: false ignoreErrors: [] internalErrorsCountLimit: 50 + constantArrayTypeBuilderArrayCountLimit: 256 cache: nodesByStringCountMax: 256 resolvedPhpDocBlockCacheCountMax: 2048 diff --git a/conf/parametersSchema.neon b/conf/parametersSchema.neon index 55df0639fb..1cc8065cc9 100644 --- a/conf/parametersSchema.neon +++ b/conf/parametersSchema.neon @@ -134,6 +134,7 @@ parametersSchema: ) ) internalErrorsCountLimit: int() + constantArrayTypeBuilderArrayCountLimit: int() cache: structure([ nodesByStringCountMax: int(), resolvedPhpDocBlockCacheCountMax: int(), diff --git a/src/Analyser/TypeSpecifier.php b/src/Analyser/TypeSpecifier.php index 79eb168577..bc74c960f2 100644 --- a/src/Analyser/TypeSpecifier.php +++ b/src/Analyser/TypeSpecifier.php @@ -1151,7 +1151,7 @@ private function specifyTypesForCountFuncCall( if ( $sizeType instanceof ConstantIntegerType - && $sizeType->getValue() < ConstantArrayTypeBuilder::ARRAY_COUNT_LIMIT + && $sizeType->getValue() < ConstantArrayTypeBuilder::getArrayCountLimit() && $isList->yes() && $arrayType->getKeyType()->isSuperTypeOf(IntegerRangeType::fromInterval(0, $sizeType->getValue() - 1))->yes() ) { @@ -1168,7 +1168,7 @@ private function specifyTypesForCountFuncCall( if ( $sizeType instanceof IntegerRangeType && $sizeType->getMin() !== null - && $sizeType->getMin() < ConstantArrayTypeBuilder::ARRAY_COUNT_LIMIT + && $sizeType->getMin() < ConstantArrayTypeBuilder::getArrayCountLimit() && $isList->yes() && $arrayType->getKeyType()->isSuperTypeOf(IntegerRangeType::fromInterval(0, ($sizeType->getMax() ?? $sizeType->getMin()) - 1))->yes() ) { @@ -1179,7 +1179,7 @@ private function specifyTypesForCountFuncCall( $builderData[] = [$offsetType, $arrayType->getOffsetValueType($offsetType), false]; } if ($sizeType->getMax() !== null) { - if ($sizeType->getMax() - $sizeType->getMin() > ConstantArrayTypeBuilder::ARRAY_COUNT_LIMIT) { + if ($sizeType->getMax() - $sizeType->getMin() > ConstantArrayTypeBuilder::getArrayCountLimit()) { $resultTypes[] = $arrayType; continue; } @@ -1201,7 +1201,7 @@ private function specifyTypesForCountFuncCall( continue; } - if (count($builderData) > ConstantArrayTypeBuilder::ARRAY_COUNT_LIMIT) { + if (count($builderData) > ConstantArrayTypeBuilder::getArrayCountLimit()) { $resultTypes[] = $arrayType; continue; } diff --git a/src/DependencyInjection/ConstantArrayTypeLimitAccessor.php b/src/DependencyInjection/ConstantArrayTypeLimitAccessor.php new file mode 100644 index 0000000000..c5a904e88c --- /dev/null +++ b/src/DependencyInjection/ConstantArrayTypeLimitAccessor.php @@ -0,0 +1,20 @@ +getService('typeSpecifier'); BleedingEdgeToggle::setBleedingEdge($container->getParameter('featureToggles')['bleedingEdge']); + ConstantArrayTypeLimitAccessor::setLimit($container->getParameter('constantArrayTypeBuilderArrayCountLimit')); } public function getCurrentWorkingDirectory(): string diff --git a/src/PhpDoc/TypeNodeResolver.php b/src/PhpDoc/TypeNodeResolver.php index 4df978002b..a6d07ee91b 100644 --- a/src/PhpDoc/TypeNodeResolver.php +++ b/src/PhpDoc/TypeNodeResolver.php @@ -1046,7 +1046,7 @@ function (CallableTypeParameterNode $parameterNode) use ($nameScope, &$isVariadi private function resolveArrayShapeNode(ArrayShapeNode $typeNode, NameScope $nameScope): Type { $builder = ConstantArrayTypeBuilder::createEmpty(); - if (count($typeNode->items) > ConstantArrayTypeBuilder::ARRAY_COUNT_LIMIT) { + if (count($typeNode->items) > ConstantArrayTypeBuilder::getArrayCountLimit()) { $builder->degradeToGeneralArray(true); } diff --git a/src/Reflection/InitializerExprTypeResolver.php b/src/Reflection/InitializerExprTypeResolver.php index cf543fa3ae..c1a1866dca 100644 --- a/src/Reflection/InitializerExprTypeResolver.php +++ b/src/Reflection/InitializerExprTypeResolver.php @@ -631,7 +631,7 @@ public function resolveConcatType(Type $left, Type $right): Type */ public function getArrayType(Expr\Array_ $expr, callable $getTypeCallback): Type { - if (count($expr->items) > ConstantArrayTypeBuilder::ARRAY_COUNT_LIMIT) { + if (count($expr->items) > ConstantArrayTypeBuilder::getArrayCountLimit()) { return $this->oversizedArrayBuilder->build($expr, $getTypeCallback); } @@ -1464,7 +1464,7 @@ public function getPlusTypeFromTypes(Expr $left, Expr $right, Type $leftType, Ty $leftCount = count($leftConstantArrays); $rightCount = count($rightConstantArrays); if ($leftCount > 0 && $rightCount > 0 - && ($leftCount + $rightCount < ConstantArrayTypeBuilder::ARRAY_COUNT_LIMIT)) { + && ($leftCount + $rightCount < ConstantArrayTypeBuilder::getArrayCountLimit())) { $resultTypes = []; foreach ($rightConstantArrays as $rightConstantArray) { foreach ($leftConstantArrays as $leftConstantArray) { diff --git a/src/Type/Constant/ConstantArrayTypeBuilder.php b/src/Type/Constant/ConstantArrayTypeBuilder.php index 9396fec39f..cd38765534 100644 --- a/src/Type/Constant/ConstantArrayTypeBuilder.php +++ b/src/Type/Constant/ConstantArrayTypeBuilder.php @@ -2,6 +2,7 @@ namespace PHPStan\Type\Constant; +use PHPStan\DependencyInjection\ConstantArrayTypeLimitAccessor; use PHPStan\ShouldNotHappenException; use PHPStan\TrinaryLogic; use PHPStan\Type\Accessory\AccessoryArrayListType; @@ -30,9 +31,17 @@ final class ConstantArrayTypeBuilder { + /** + * @deprecated Use getArrayCountLimit() instead + */ public const ARRAY_COUNT_LIMIT = 256; private const CLOSURES_COUNT_LIMIT = 16; + public static function getArrayCountLimit(): int + { + return ConstantArrayTypeLimitAccessor::getLimit(); + } + private bool $degradeToGeneralArray = false; private bool $degradeClosures = false; @@ -70,7 +79,7 @@ public static function createFromConstantArray(ConstantArrayType $startArrayType $startArrayType->isList(), ); - if (count($startArrayType->getKeyTypes()) > self::ARRAY_COUNT_LIMIT) { + if (count($startArrayType->getKeyTypes()) > self::getArrayCountLimit()) { $builder->degradeToGeneralArray(true); } @@ -147,7 +156,7 @@ public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $opt $this->optionalKeys[] = count($this->keyTypes) - 1; } - if (count($this->keyTypes) > self::ARRAY_COUNT_LIMIT) { + if (count($this->keyTypes) > self::getArrayCountLimit()) { $this->degradeToGeneralArray = true; $this->oversized = true; } @@ -220,7 +229,7 @@ public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $opt $this->optionalKeys[] = count($this->keyTypes) - 1; } - if (count($this->keyTypes) > self::ARRAY_COUNT_LIMIT) { + if (count($this->keyTypes) > self::getArrayCountLimit()) { $this->degradeToGeneralArray = true; $this->oversized = true; } @@ -244,7 +253,7 @@ public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $opt } } } - if (count($scalarTypes) > 0 && count($scalarTypes) < self::ARRAY_COUNT_LIMIT) { + if (count($scalarTypes) > 0 && count($scalarTypes) < self::getArrayCountLimit()) { $match = true; $valueTypes = $this->valueTypes; foreach ($scalarTypes as $scalarType) { diff --git a/src/Type/ConstantTypeHelper.php b/src/Type/ConstantTypeHelper.php index 49fa3b082f..82e6cf9102 100644 --- a/src/Type/ConstantTypeHelper.php +++ b/src/Type/ConstantTypeHelper.php @@ -42,7 +42,7 @@ public static function getTypeFromValue($value): Type return new ConstantStringType($value); } elseif (is_array($value)) { $arrayBuilder = ConstantArrayTypeBuilder::createEmpty(); - if (count($value) > ConstantArrayTypeBuilder::ARRAY_COUNT_LIMIT) { + if (count($value) > ConstantArrayTypeBuilder::getArrayCountLimit()) { $arrayBuilder->degradeToGeneralArray(true); } foreach ($value as $k => $v) { diff --git a/src/Type/Php/ArrayMapFunctionReturnTypeExtension.php b/src/Type/Php/ArrayMapFunctionReturnTypeExtension.php index 78699643bd..96cf13d64a 100644 --- a/src/Type/Php/ArrayMapFunctionReturnTypeExtension.php +++ b/src/Type/Php/ArrayMapFunctionReturnTypeExtension.php @@ -128,7 +128,7 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection, if (count($constantArrays) > 0) { $arrayTypes = []; $totalCount = TypeCombinator::countConstantArrayValueTypes($constantArrays) * TypeCombinator::countConstantArrayValueTypes([$valueType]); - if ($totalCount < ConstantArrayTypeBuilder::ARRAY_COUNT_LIMIT) { + if ($totalCount < ConstantArrayTypeBuilder::getArrayCountLimit()) { foreach ($constantArrays as $constantArray) { $returnedArrayBuilder = ConstantArrayTypeBuilder::createEmpty(); $valueTypes = $constantArray->getValueTypes(); diff --git a/src/Type/TypeCombinator.php b/src/Type/TypeCombinator.php index aa5f933532..939a665906 100644 --- a/src/Type/TypeCombinator.php +++ b/src/Type/TypeCombinator.php @@ -904,7 +904,7 @@ private static function optimizeConstantArrays(array $types): array { $constantArrayValuesCount = self::countConstantArrayValueTypes($types); - if ($constantArrayValuesCount <= ConstantArrayTypeBuilder::ARRAY_COUNT_LIMIT) { + if ($constantArrayValuesCount <= ConstantArrayTypeBuilder::getArrayCountLimit()) { return $types; } @@ -991,7 +991,7 @@ private static function optimizeConstantArrays(array $types): array $keyType = self::union(...$keyTypes); $valueType = self::union(...$valueTypes); - if ($valueType instanceof UnionType && count($valueType->getTypes()) > ConstantArrayTypeBuilder::ARRAY_COUNT_LIMIT) { + if ($valueType instanceof UnionType && count($valueType->getTypes()) > ConstantArrayTypeBuilder::getArrayCountLimit()) { $valueType = $valueType->generalize(GeneralizePrecision::lessSpecific()); }