Skip to content

[CodeQuality] Respect typeGuardedClasses and skip closure-only returns in ResponseReturnTypeControllerActionRector#964

Merged
TomasVotruba merged 2 commits into
mainfrom
response-return-type-guard-and-closure-fix
Jul 21, 2026
Merged

[CodeQuality] Respect typeGuardedClasses and skip closure-only returns in ResponseReturnTypeControllerActionRector#964
TomasVotruba merged 2 commits into
mainfrom
response-return-type-guard-and-closure-fix

Conversation

@TomasVotruba

@TomasVotruba TomasVotruba commented Jul 21, 2026

Copy link
Copy Markdown
Member

Two fixes for ResponseReturnTypeControllerActionRector.

1. Respect typeGuardedClasses

Follow-up to rectorphp/rector-src#8135. The rule wrote return types even into classes the user guarded via withTypeGuardedClasses(), which is a breaking change for their child classes. Now it bails out through ParentClassMethodTypeOverrideGuard::isTypeGuardedClass().

With ->withTypeGuardedClasses([AbstractCustomController::class]):

-class SomeController extends AbstractCustomController
+class SomeController extends AbstractCustomController // non-final = guarded, left alone
 {
     #[Route]
     public function detail()
     {
         return $this->render('some_template');
     }
 }

Final classes cannot be extended, so they are still refactored:

 final class SomeController extends AbstractCustomController
 {
     #[Route]
-    public function detail()
+    public function detail(): \Symfony\Component\HttpFoundation\Response
     {
         return $this->render('some_template');
     }
 }

Controllers outside the guarded tree are untouched by the option:

 class SomeController extends AbstractController
 {
     #[Route]
-    public function detail()
+    public function detail(): \Symfony\Component\HttpFoundation\Response
     {
         return $this->render('some_template');
     }
 }

2. Skip methods whose only return lives in a closure

hasReturn() used the unscoped hasInstancesOf(), so a closure body counted as the method's return. isResponseReturnMethod() then saw zero scoped returns and vacuously returned true, producing a bogus type on a method that returns nothing:

 final class SomeController extends AbstractController
 {
     #[Route]
-    public function detail()
+    public function detail(): \Symfony\Component\HttpFoundation\RedirectResponse
     {
         $callback = function () {
             return 'hey';
         };

         $callback();
     }
 }

Switched to hasInstancesOfInFunctionLikeScoped().

@TomasVotruba
TomasVotruba merged commit 2bbb3b5 into main Jul 21, 2026
8 checks passed
@TomasVotruba
TomasVotruba deleted the response-return-type-guard-and-closure-fix branch July 21, 2026 09:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant