diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 3b47d2e5fc..d3e9a54a9d 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -1422,7 +1422,7 @@ parameters: - message: '#^Doing instanceof PHPStan\\Type\\ObjectType is error\-prone and deprecated\. Use Type\:\:isObject\(\) or Type\:\:getObjectClassNames\(\) instead\.$#' identifier: phpstanApi.instanceofType - count: 6 + count: 3 path: src/Type/ObjectType.php - diff --git a/src/Type/ObjectType.php b/src/Type/ObjectType.php index a51539df05..3290fb97c7 100644 --- a/src/Type/ObjectType.php +++ b/src/Type/ObjectType.php @@ -5,11 +5,6 @@ use ArrayAccess; use Closure; use Countable; -use DateTime; -use DateTimeImmutable; -use DateTimeInterface; -use Error; -use Exception; use Iterator; use IteratorAggregate; use PHPStan\Analyser\OutOfClassScope; @@ -46,7 +41,6 @@ use PHPStan\Type\Traits\NonGeneralizableTypeTrait; use PHPStan\Type\Traits\NonGenericTypeTrait; use PHPStan\Type\Traits\UndecidedComparisonTypeTrait; -use Throwable; use Traversable; use function array_key_exists; use function array_map; @@ -1560,23 +1554,21 @@ private function getInterfaces(): array public function tryRemove(Type $typeToRemove): ?Type { - if ($this->getClassName() === DateTimeInterface::class) { - if ($typeToRemove instanceof ObjectType && $typeToRemove->getClassName() === DateTimeImmutable::class) { - return new ObjectType(DateTime::class); - } - - if ($typeToRemove instanceof ObjectType && $typeToRemove->getClassName() === DateTime::class) { - return new ObjectType(DateTimeImmutable::class); - } - } + if ($typeToRemove instanceof ObjectType) { + foreach (UnionType::EQUAL_UNION_CLASSES as $baseClass => $classes) { + if ($this->getClassName() !== $baseClass) { + continue; + } - if ($this->getClassName() === Throwable::class) { - if ($typeToRemove instanceof ObjectType && $typeToRemove->getClassName() === Error::class) { - return new ObjectType(Exception::class); // phpcs:ignore SlevomatCodingStandard.Exceptions.ReferenceThrowableOnly.ReferencedGeneralException - } + foreach ($classes as $index => $class) { + if ($typeToRemove->getClassName() === $class) { + unset($classes[$index]); - if ($typeToRemove instanceof ObjectType && $typeToRemove->getClassName() === Exception::class) { // phpcs:ignore SlevomatCodingStandard.Exceptions.ReferenceThrowableOnly.ReferencedGeneralException - return new ObjectType(Error::class); + return TypeCombinator::union( + ...array_map(static fn (string $objectClass): Type => new ObjectType($objectClass), $classes), + ); + } + } } } diff --git a/src/Type/UnionType.php b/src/Type/UnionType.php index 1505fa1bb2..5d7627b306 100644 --- a/src/Type/UnionType.php +++ b/src/Type/UnionType.php @@ -5,6 +5,8 @@ use DateTime; use DateTimeImmutable; use DateTimeInterface; +use Error; +use Exception; use PHPStan\Php\PhpVersion; use PHPStan\PhpDocParser\Ast\Type\TypeNode; use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode; @@ -26,6 +28,7 @@ use PHPStan\Type\Generic\TemplateTypeVariance; use PHPStan\Type\Generic\TemplateUnionType; use PHPStan\Type\Traits\NonGeneralizableTypeTrait; +use Throwable; use function array_diff_assoc; use function array_fill_keys; use function array_map; @@ -45,6 +48,11 @@ class UnionType implements CompoundType use NonGeneralizableTypeTrait; + public const EQUAL_UNION_CLASSES = [ + DateTimeInterface::class => [DateTimeImmutable::class, DateTime::class], + Throwable::class => [Error::class, Exception::class], // phpcs:ignore SlevomatCodingStandard.Exceptions.ReferenceThrowableOnly.ReferencedGeneralException + ]; + private bool $sortedTypes = false; /** @var array */ @@ -183,14 +191,18 @@ public function getConstantStrings(): array public function accepts(Type $type, bool $strictTypes): AcceptsResult { - if ( - $type->equals(new ObjectType(DateTimeInterface::class)) - && $this->accepts( - new UnionType([new ObjectType(DateTime::class), new ObjectType(DateTimeImmutable::class)]), - $strictTypes, - )->yes() - ) { - return AcceptsResult::createYes(); + foreach (self::EQUAL_UNION_CLASSES as $baseClass => $classes) { + if (!$type->equals(new ObjectType($baseClass))) { + continue; + } + + $union = TypeCombinator::union( + ...array_map(static fn (string $objectClass): Type => new ObjectType($objectClass), $classes), + ); + if ($this->accepts($union, $strictTypes)->yes()) { + return AcceptsResult::createYes(); + } + break; } $result = AcceptsResult::createNo();