From 2c7a29b2d5444cfece7a6298cba83f1f61de5228 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Mon, 26 Oct 2026 00:11:12 +0100 Subject: [PATCH] [TypeDeclaration] Retarget ClosureReturnTypeFromAssertInstanceOfRector to Class_ node --- ...turnTypeFromAssertInstanceOfRectorTest.php | 2 +- ...assert_instance_of_narrowed_return.php.inc | 4 +- .../skip_no_assert_instance_of.php.inc | 2 +- .../Fixture/skip_outside_test_case.php.inc | 2 +- .../config/configured_rule.php | 2 +- ...reReturnTypeFromAssertInstanceOfRector.php | 74 ++++++++++--------- src/Config/Level/TypeDeclarationLevel.php | 2 +- 7 files changed, 46 insertions(+), 42 deletions(-) rename rules-tests/TypeDeclaration/Rector/{Closure => Class_}/ClosureReturnTypeFromAssertInstanceOfRector/ClosureReturnTypeFromAssertInstanceOfRectorTest.php (86%) rename rules-tests/TypeDeclaration/Rector/{Closure => Class_}/ClosureReturnTypeFromAssertInstanceOfRector/Fixture/assert_instance_of_narrowed_return.php.inc (74%) rename rules-tests/TypeDeclaration/Rector/{Closure => Class_}/ClosureReturnTypeFromAssertInstanceOfRector/Fixture/skip_no_assert_instance_of.php.inc (67%) rename rules-tests/TypeDeclaration/Rector/{Closure => Class_}/ClosureReturnTypeFromAssertInstanceOfRector/Fixture/skip_outside_test_case.php.inc (60%) rename rules-tests/TypeDeclaration/Rector/{Closure => Class_}/ClosureReturnTypeFromAssertInstanceOfRector/config/configured_rule.php (77%) rename rules/TypeDeclaration/Rector/{Closure => Class_}/ClosureReturnTypeFromAssertInstanceOfRector.php (64%) diff --git a/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeFromAssertInstanceOfRector/ClosureReturnTypeFromAssertInstanceOfRectorTest.php b/rules-tests/TypeDeclaration/Rector/Class_/ClosureReturnTypeFromAssertInstanceOfRector/ClosureReturnTypeFromAssertInstanceOfRectorTest.php similarity index 86% rename from rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeFromAssertInstanceOfRector/ClosureReturnTypeFromAssertInstanceOfRectorTest.php rename to rules-tests/TypeDeclaration/Rector/Class_/ClosureReturnTypeFromAssertInstanceOfRector/ClosureReturnTypeFromAssertInstanceOfRectorTest.php index d4c301d5713..42028b94c0f 100644 --- a/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeFromAssertInstanceOfRector/ClosureReturnTypeFromAssertInstanceOfRectorTest.php +++ b/rules-tests/TypeDeclaration/Rector/Class_/ClosureReturnTypeFromAssertInstanceOfRector/ClosureReturnTypeFromAssertInstanceOfRectorTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\TypeDeclaration\Rector\Closure\ClosureReturnTypeFromAssertInstanceOfRector; +namespace Rector\Tests\TypeDeclaration\Rector\Class_\ClosureReturnTypeFromAssertInstanceOfRector; use Iterator; use PHPUnit\Framework\Attributes\DataProvider; diff --git a/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeFromAssertInstanceOfRector/Fixture/assert_instance_of_narrowed_return.php.inc b/rules-tests/TypeDeclaration/Rector/Class_/ClosureReturnTypeFromAssertInstanceOfRector/Fixture/assert_instance_of_narrowed_return.php.inc similarity index 74% rename from rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeFromAssertInstanceOfRector/Fixture/assert_instance_of_narrowed_return.php.inc rename to rules-tests/TypeDeclaration/Rector/Class_/ClosureReturnTypeFromAssertInstanceOfRector/Fixture/assert_instance_of_narrowed_return.php.inc index ec6922f0f4c..64ab0d1f9cf 100644 --- a/rules-tests/TypeDeclaration/Rector/Closure/ClosureReturnTypeFromAssertInstanceOfRector/Fixture/assert_instance_of_narrowed_return.php.inc +++ b/rules-tests/TypeDeclaration/Rector/Class_/ClosureReturnTypeFromAssertInstanceOfRector/Fixture/assert_instance_of_narrowed_return.php.inc @@ -1,6 +1,6 @@ returnType instanceof Node) { + $classReflection = $this->fetchClassLikeReflection($node); + if (! $classReflection instanceof ClassReflection) { return null; } - // only handle closures narrowed by assertInstanceOf(); plain returns are handled by ClosureReturnTypeRector - if (! $this->hasAssertInstanceOf($node)) { + // is phpunit test case? + if (! $classReflection->is(ClassName::TEST_CASE_CLASS)) { return null; } - $scope = ScopeFetcher::fetch($node); - if (! $this->isInsideTestCaseClass($scope)) { - return null; - } + $hasChanged = false; - $closureReturnType = $this->returnTypeInferer->inferFunctionLike($node); + /** @var Closure[] $closures */ + $closures = $this->betterNodeFinder->findInstancesOf($node, [Closure::class]); + foreach ($closures as $closure) { + // type is already set + if ($closure->returnType instanceof Node) { + continue; + } - // handled by other rules - if ($closureReturnType instanceof NeverType) { - return null; - } + // only handle closures narrowed by assertInstanceOf(); plain returns are handled by ClosureReturnTypeRector + if (! $this->hasAssertInstanceOf($closure)) { + continue; + } - $returnTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($closureReturnType, TypeKind::RETURN); - if (! $returnTypeNode instanceof Node) { - return null; + $closureReturnType = $this->returnTypeInferer->inferFunctionLike($closure); + + // handled by other rules + if ($closureReturnType instanceof NeverType) { + continue; + } + + $returnTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($closureReturnType, TypeKind::RETURN); + if (! $returnTypeNode instanceof Node) { + continue; + } + + $closure->returnType = $returnTypeNode; + $hasChanged = true; } - $node->returnType = $returnTypeNode; + if ($hasChanged) { + return $node; + } - return $node; + return null; } public function provideMinPhpVersion(): int @@ -135,15 +150,4 @@ private function hasAssertInstanceOf(Closure $closure): bool && $this->isName($node->name, 'assertInstanceOf') ); } - - private function isInsideTestCaseClass(Scope $scope): bool - { - $classReflection = $scope->getClassReflection(); - if (! $classReflection instanceof ClassReflection) { - return false; - } - - // is phpunit test case? - return $classReflection->is(ClassName::TEST_CASE_CLASS); - } } diff --git a/src/Config/Level/TypeDeclarationLevel.php b/src/Config/Level/TypeDeclarationLevel.php index f4c421a7ea7..2194ee4471a 100644 --- a/src/Config/Level/TypeDeclarationLevel.php +++ b/src/Config/Level/TypeDeclarationLevel.php @@ -10,6 +10,7 @@ use Rector\TypeDeclaration\Rector\ArrowFunction\AddArrowFunctionReturnTypeRector; use Rector\TypeDeclaration\Rector\Class_\AddTestsVoidReturnTypeWhereNoReturnRector; use Rector\TypeDeclaration\Rector\Class_\ChildDoctrineRepositoryClassTypeRector; +use Rector\TypeDeclaration\Rector\Class_\ClosureReturnTypeFromAssertInstanceOfRector; use Rector\TypeDeclaration\Rector\Class_\MergeDateTimePropertyTypeDeclarationRector; use Rector\TypeDeclaration\Rector\Class_\ObjectTypedPropertyFromJMSSerializerAttributeTypeRector; use Rector\TypeDeclaration\Rector\Class_\PropertyTypeFromStrictSetterGetterRector; @@ -56,7 +57,6 @@ use Rector\TypeDeclaration\Rector\ClassMethod\StringReturnTypeFromStrictStringReturnsRector; use Rector\TypeDeclaration\Rector\Closure\AddClosureNeverReturnTypeRector; use Rector\TypeDeclaration\Rector\Closure\AddClosureVoidReturnTypeWhereNoReturnRector; -use Rector\TypeDeclaration\Rector\Closure\ClosureReturnTypeFromAssertInstanceOfRector; use Rector\TypeDeclaration\Rector\Closure\ClosureReturnTypeRector; use Rector\TypeDeclaration\Rector\FuncCall\AddArrayAnyAllClosureParamTypeRector; use Rector\TypeDeclaration\Rector\FuncCall\AddArrayFunctionClosureParamTypeRector;