diff --git a/src/Parser/TypeTraverserInstanceofVisitor.php b/src/Parser/TypeTraverserInstanceofVisitor.php index 3ad2fa0f11..e0a6459a8f 100644 --- a/src/Parser/TypeTraverserInstanceofVisitor.php +++ b/src/Parser/TypeTraverserInstanceofVisitor.php @@ -6,6 +6,7 @@ use PhpParser\Node; use PhpParser\NodeVisitorAbstract; use PHPStan\DependencyInjection\AutowiredService; +use function in_array; #[AutowiredService] final class TypeTraverserInstanceofVisitor extends NodeVisitorAbstract @@ -13,6 +14,11 @@ final class TypeTraverserInstanceofVisitor extends NodeVisitorAbstract public const ATTRIBUTE_NAME = 'insideTypeTraverserMap'; + private const TYPE_TRAVERSER_CLASSES = [ + 'phpstan\\type\\typetraverser', + 'phpstan\\type\\simultaneoustypetraverser', + ]; + private int $depth = 0; #[Override] @@ -33,7 +39,7 @@ public function enterNode(Node $node): ?Node if ( $node instanceof Node\Expr\StaticCall && $node->class instanceof Node\Name - && $node->class->toLowerString() === 'phpstan\\type\\typetraverser' + && in_array($node->class->toLowerString(), self::TYPE_TRAVERSER_CLASSES, true) && $node->name instanceof Node\Identifier && $node->name->toLowerString() === 'map' ) { @@ -49,7 +55,7 @@ public function leaveNode(Node $node): ?Node if ( $node instanceof Node\Expr\StaticCall && $node->class instanceof Node\Name - && $node->class->toLowerString() === 'phpstan\\type\\typetraverser' + && in_array($node->class->toLowerString(), self::TYPE_TRAVERSER_CLASSES, true) && $node->name instanceof Node\Identifier && $node->name->toLowerString() === 'map' ) { diff --git a/tests/PHPStan/Rules/Api/ApiInstanceofTypeRuleTest.php b/tests/PHPStan/Rules/Api/ApiInstanceofTypeRuleTest.php index 08ceeff3fd..d5e9b165df 100644 --- a/tests/PHPStan/Rules/Api/ApiInstanceofTypeRuleTest.php +++ b/tests/PHPStan/Rules/Api/ApiInstanceofTypeRuleTest.php @@ -22,22 +22,22 @@ public function testRule(): void $this->analyse([__DIR__ . '/data/instanceof-type.php'], [ [ 'Doing instanceof PHPStan\Type\TypeWithClassName is error-prone and deprecated. Use Type::getObjectClassNames() or Type::getObjectClassReflections() instead.', - 20, + 21, $tipText, ], [ 'Doing instanceof phpstan\type\typewithclassname is error-prone and deprecated. Use Type::getObjectClassNames() or Type::getObjectClassReflections() instead.', - 24, + 25, $tipText, ], [ 'Doing instanceof PHPStan\Type\TypeWithClassName is error-prone and deprecated. Use Type::getObjectClassNames() or Type::getObjectClassReflections() instead.', - 36, + 37, $tipText, ], [ 'Doing instanceof PHPStan\Type\Generic\GenericObjectType is error-prone and deprecated.', - 40, + 41, $tipText, ], ]); diff --git a/tests/PHPStan/Rules/Api/data/instanceof-type.php b/tests/PHPStan/Rules/Api/data/instanceof-type.php index eabc5f6c5e..39ca3faf9a 100644 --- a/tests/PHPStan/Rules/Api/data/instanceof-type.php +++ b/tests/PHPStan/Rules/Api/data/instanceof-type.php @@ -3,6 +3,7 @@ namespace ApiInstanceofType; use PHPStan\Type\Generic\GenericObjectType; +use PHPStan\Type\SimultaneousTypeTraverser; use PHPStan\Type\Type; use PHPStan\Type\TypeTraverser; use PHPStan\Type\TypeWithClassName; @@ -40,6 +41,15 @@ public function doFoo($a, Type $type) if ($a instanceof GenericObjectType) { } + + $type = SimultaneousTypeTraverser::map($type, $type, function (Type $left, Type $right, callable $traverse): Type { + if ($left instanceof TypeWithClassName) { + return $left; + } + + return $traverse($left, $right); + }); + } }