From d73c27d678d9e178adf7046e1d1bb2a206158fa9 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Tue, 21 Jul 2026 11:16:22 +0200 Subject: [PATCH 1/2] [CodeQuality] Respect typeGuardedClasses and skip closure-only returns in ResponseReturnTypeControllerActionRector --- .../skip_return_in_closure_only.php.inc | 19 ++++++++++ .../final_controller.php.inc | 35 +++++++++++++++++++ .../skip_type_guarded_controller.php.inc | 15 ++++++++ ...eControllerActionRectorTypeGuardedTest.php | 28 +++++++++++++++ .../config/type_guarded_rule.php | 11 ++++++ ...sponseReturnTypeControllerActionRector.php | 11 ++++-- 6 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 rules-tests/CodeQuality/Rector/ClassMethod/ResponseReturnTypeControllerActionRector/Fixture/skip_return_in_closure_only.php.inc create mode 100644 rules-tests/CodeQuality/Rector/ClassMethod/ResponseReturnTypeControllerActionRector/FixtureTypeGuarded/final_controller.php.inc create mode 100644 rules-tests/CodeQuality/Rector/ClassMethod/ResponseReturnTypeControllerActionRector/FixtureTypeGuarded/skip_type_guarded_controller.php.inc create mode 100644 rules-tests/CodeQuality/Rector/ClassMethod/ResponseReturnTypeControllerActionRector/ResponseReturnTypeControllerActionRectorTypeGuardedTest.php create mode 100644 rules-tests/CodeQuality/Rector/ClassMethod/ResponseReturnTypeControllerActionRector/config/type_guarded_rule.php diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/ResponseReturnTypeControllerActionRector/Fixture/skip_return_in_closure_only.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/ResponseReturnTypeControllerActionRector/Fixture/skip_return_in_closure_only.php.inc new file mode 100644 index 000000000..0a7cf3ff7 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/ResponseReturnTypeControllerActionRector/Fixture/skip_return_in_closure_only.php.inc @@ -0,0 +1,19 @@ +render('some_template'); + } +} + +?> +----- +render('some_template'); + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/ResponseReturnTypeControllerActionRector/FixtureTypeGuarded/skip_type_guarded_controller.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/ResponseReturnTypeControllerActionRector/FixtureTypeGuarded/skip_type_guarded_controller.php.inc new file mode 100644 index 000000000..cfe880fa4 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/ResponseReturnTypeControllerActionRector/FixtureTypeGuarded/skip_type_guarded_controller.php.inc @@ -0,0 +1,15 @@ +render('some_template'); + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/ResponseReturnTypeControllerActionRector/ResponseReturnTypeControllerActionRectorTypeGuardedTest.php b/rules-tests/CodeQuality/Rector/ClassMethod/ResponseReturnTypeControllerActionRector/ResponseReturnTypeControllerActionRectorTypeGuardedTest.php new file mode 100644 index 000000000..a55e9fbaf --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/ResponseReturnTypeControllerActionRector/ResponseReturnTypeControllerActionRectorTypeGuardedTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/FixtureTypeGuarded'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/type_guarded_rule.php'; + } +} diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/ResponseReturnTypeControllerActionRector/config/type_guarded_rule.php b/rules-tests/CodeQuality/Rector/ClassMethod/ResponseReturnTypeControllerActionRector/config/type_guarded_rule.php new file mode 100644 index 000000000..9685356a6 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/ResponseReturnTypeControllerActionRector/config/type_guarded_rule.php @@ -0,0 +1,11 @@ +withRules([ResponseReturnTypeControllerActionRector::class]) + ->withTypeGuardedClasses([AbstractController::class]); diff --git a/rules/CodeQuality/Rector/ClassMethod/ResponseReturnTypeControllerActionRector.php b/rules/CodeQuality/Rector/ClassMethod/ResponseReturnTypeControllerActionRector.php index f7c14f2cc..8d8a9067d 100644 --- a/rules/CodeQuality/Rector/ClassMethod/ResponseReturnTypeControllerActionRector.php +++ b/rules/CodeQuality/Rector/ClassMethod/ResponseReturnTypeControllerActionRector.php @@ -30,6 +30,7 @@ use Rector\TypeDeclaration\NodeAnalyzer\ReturnAnalyzer; use Rector\TypeDeclaration\TypeInferer\ReturnTypeInferer; use Rector\ValueObject\PhpVersionFeature; +use Rector\VendorLocker\ParentClassMethodTypeOverrideGuard; use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -45,7 +46,8 @@ public function __construct( private readonly BetterNodeFinder $betterNodeFinder, private readonly ReturnAnalyzer $returnAnalyzer, private readonly StaticTypeMapper $staticTypeMapper, - private readonly ReturnTypeInferer $returnTypeInferer + private readonly ReturnTypeInferer $returnTypeInferer, + private readonly ParentClassMethodTypeOverrideGuard $parentClassMethodTypeOverrideGuard ) { } @@ -112,6 +114,11 @@ public function refactor(Node $node): ?Node return null; } + // adding a return type would break child classes of user-guarded classes + if ($this->parentClassMethodTypeOverrideGuard->isTypeGuardedClass($node)) { + return null; + } + if (! $this->isActionClassMethod($node)) { return null; } @@ -188,7 +195,7 @@ private function isActionClassMethod(ClassMethod $classMethod): bool private function hasReturn(ClassMethod $classMethod): bool { - return $this->betterNodeFinder->hasInstancesOf($classMethod, [Return_::class]); + return $this->betterNodeFinder->hasInstancesOfInFunctionLikeScoped($classMethod, Return_::class); } private function refactorResponse(ClassMethod $classMethod): ?ClassMethod From a024593e420e6f6082257546c09b54a487d6973d Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Tue, 21 Jul 2026 11:18:32 +0200 Subject: [PATCH 2/2] Use custom controller class in typeGuardedClasses test fixtures --- .../final_controller.php.inc | 8 ++--- .../non_guarded_controller.php.inc | 35 +++++++++++++++++++ .../skip_type_guarded_controller.php.inc | 4 +-- .../Source/AbstractCustomController.php | 11 ++++++ .../config/type_guarded_rule.php | 4 +-- 5 files changed, 54 insertions(+), 8 deletions(-) create mode 100644 rules-tests/CodeQuality/Rector/ClassMethod/ResponseReturnTypeControllerActionRector/FixtureTypeGuarded/non_guarded_controller.php.inc create mode 100644 rules-tests/CodeQuality/Rector/ClassMethod/ResponseReturnTypeControllerActionRector/Source/AbstractCustomController.php diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/ResponseReturnTypeControllerActionRector/FixtureTypeGuarded/final_controller.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/ResponseReturnTypeControllerActionRector/FixtureTypeGuarded/final_controller.php.inc index c2e7d34f7..f48f84d1c 100644 --- a/rules-tests/CodeQuality/Rector/ClassMethod/ResponseReturnTypeControllerActionRector/FixtureTypeGuarded/final_controller.php.inc +++ b/rules-tests/CodeQuality/Rector/ClassMethod/ResponseReturnTypeControllerActionRector/FixtureTypeGuarded/final_controller.php.inc @@ -2,10 +2,10 @@ namespace Rector\Symfony\Tests\CodeQuality\Rector\ClassMethod\ResponseReturnTypeControllerActionRector\FixtureTypeGuarded; -use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Rector\Symfony\Tests\CodeQuality\Rector\ClassMethod\ResponseReturnTypeControllerActionRector\Source\AbstractCustomController; use Symfony\Component\Routing\Annotation\Route; -final class FinalController extends AbstractController +final class FinalController extends AbstractCustomController { #[Route] public function detail() @@ -20,10 +20,10 @@ final class FinalController extends AbstractController namespace Rector\Symfony\Tests\CodeQuality\Rector\ClassMethod\ResponseReturnTypeControllerActionRector\FixtureTypeGuarded; -use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Rector\Symfony\Tests\CodeQuality\Rector\ClassMethod\ResponseReturnTypeControllerActionRector\Source\AbstractCustomController; use Symfony\Component\Routing\Annotation\Route; -final class FinalController extends AbstractController +final class FinalController extends AbstractCustomController { #[Route] public function detail(): \Symfony\Component\HttpFoundation\Response diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/ResponseReturnTypeControllerActionRector/FixtureTypeGuarded/non_guarded_controller.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/ResponseReturnTypeControllerActionRector/FixtureTypeGuarded/non_guarded_controller.php.inc new file mode 100644 index 000000000..6fca15351 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/ResponseReturnTypeControllerActionRector/FixtureTypeGuarded/non_guarded_controller.php.inc @@ -0,0 +1,35 @@ +render('some_template'); + } +} + +?> +----- +render('some_template'); + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/ResponseReturnTypeControllerActionRector/FixtureTypeGuarded/skip_type_guarded_controller.php.inc b/rules-tests/CodeQuality/Rector/ClassMethod/ResponseReturnTypeControllerActionRector/FixtureTypeGuarded/skip_type_guarded_controller.php.inc index cfe880fa4..0fd8e7fb0 100644 --- a/rules-tests/CodeQuality/Rector/ClassMethod/ResponseReturnTypeControllerActionRector/FixtureTypeGuarded/skip_type_guarded_controller.php.inc +++ b/rules-tests/CodeQuality/Rector/ClassMethod/ResponseReturnTypeControllerActionRector/FixtureTypeGuarded/skip_type_guarded_controller.php.inc @@ -2,10 +2,10 @@ namespace Rector\Symfony\Tests\CodeQuality\Rector\ClassMethod\ResponseReturnTypeControllerActionRector\FixtureTypeGuarded; -use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Rector\Symfony\Tests\CodeQuality\Rector\ClassMethod\ResponseReturnTypeControllerActionRector\Source\AbstractCustomController; use Symfony\Component\Routing\Annotation\Route; -class SkipTypeGuardedController extends AbstractController +class SkipTypeGuardedController extends AbstractCustomController { #[Route] public function detail() diff --git a/rules-tests/CodeQuality/Rector/ClassMethod/ResponseReturnTypeControllerActionRector/Source/AbstractCustomController.php b/rules-tests/CodeQuality/Rector/ClassMethod/ResponseReturnTypeControllerActionRector/Source/AbstractCustomController.php new file mode 100644 index 000000000..64d193175 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/ClassMethod/ResponseReturnTypeControllerActionRector/Source/AbstractCustomController.php @@ -0,0 +1,11 @@ +withRules([ResponseReturnTypeControllerActionRector::class]) - ->withTypeGuardedClasses([AbstractController::class]); + ->withTypeGuardedClasses([AbstractCustomController::class]);