From deef91983766dd61c18d4f9d819ffc94fb701cd5 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Sat, 26 Oct 2024 15:31:26 +0200 Subject: [PATCH] Avoid new HasOffsetValueType being intersected with oversized array --- phpstan-baseline.neon | 10 ++++++++ .../Constant/ConstantArrayTypeBuilder.php | 2 ++ src/Type/IntersectionType.php | 25 +++++++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 1bdc95b954..1521bbb1a0 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1170,6 +1170,11 @@ parameters: count: 3 path: src/Type/IntersectionType.php + - + message: "#^Doing instanceof PHPStan\\\\Type\\\\ArrayType is error\\-prone and deprecated\\. Use Type\\:\\:isArray\\(\\) or Type\\:\\:getArrays\\(\\) instead\\.$#" + count: 1 + path: src/Type/IntersectionType.php + - message: "#^Doing instanceof PHPStan\\\\Type\\\\BooleanType is error\\-prone and deprecated\\. Use Type\\:\\:isBoolean\\(\\) instead\\.$#" count: 1 @@ -1180,6 +1185,11 @@ parameters: count: 2 path: src/Type/IntersectionType.php + - + message: "#^Doing instanceof PHPStan\\\\Type\\\\Constant\\\\ConstantStringType is error\\-prone and deprecated\\. Use Type\\:\\:getConstantStrings\\(\\) instead\\.$#" + count: 1 + path: src/Type/IntersectionType.php + - message: "#^Doing instanceof PHPStan\\\\Type\\\\IntersectionType is error\\-prone and deprecated\\.$#" count: 4 diff --git a/src/Type/Constant/ConstantArrayTypeBuilder.php b/src/Type/Constant/ConstantArrayTypeBuilder.php index a306833e8d..e5caa1117a 100644 --- a/src/Type/Constant/ConstantArrayTypeBuilder.php +++ b/src/Type/Constant/ConstantArrayTypeBuilder.php @@ -128,6 +128,7 @@ public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $opt if (count($this->keyTypes) > self::ARRAY_COUNT_LIMIT) { $this->degradeToGeneralArray = true; + $this->oversized = true; } return; @@ -194,6 +195,7 @@ public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $opt if (count($this->keyTypes) > self::ARRAY_COUNT_LIMIT) { $this->degradeToGeneralArray = true; + $this->oversized = true; } return; diff --git a/src/Type/IntersectionType.php b/src/Type/IntersectionType.php index 140e45a8ed..6a8d2ab329 100644 --- a/src/Type/IntersectionType.php +++ b/src/Type/IntersectionType.php @@ -28,6 +28,7 @@ use PHPStan\Type\Accessory\AccessoryType; use PHPStan\Type\Accessory\NonEmptyArrayType; use PHPStan\Type\Constant\ConstantIntegerType; +use PHPStan\Type\Constant\ConstantStringType; use PHPStan\Type\Generic\TemplateType; use PHPStan\Type\Generic\TemplateTypeMap; use PHPStan\Type\Generic\TemplateTypeVariance; @@ -722,6 +723,30 @@ public function getOffsetValueType(Type $offsetType): Type public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $unionValues = true): Type { + if ($this->isOversizedArray()->yes()) { + return $this->intersectTypes(static function (Type $type) use ($offsetType, $valueType, $unionValues): Type { + // avoid new HasOffsetValueType being intersected with oversized array + if (!$type instanceof ArrayType) { + return $type->setOffsetValueType($offsetType, $valueType, $unionValues); + } + + if (!$offsetType instanceof ConstantStringType && !$offsetType instanceof ConstantIntegerType) { + return $type->setOffsetValueType($offsetType, $valueType, $unionValues); + } + + if (!$offsetType->isSuperTypeOf($type->getKeyType())->yes()) { + return $type->setOffsetValueType($offsetType, $valueType, $unionValues); + } + + return TypeCombinator::intersect( + new ArrayType( + TypeCombinator::union($type->getKeyType(), $offsetType), + TypeCombinator::union($type->getItemType(), $valueType), + ), + new NonEmptyArrayType(), + ); + }); + } return $this->intersectTypes(static fn (Type $type): Type => $type->setOffsetValueType($offsetType, $valueType, $unionValues)); }