From 681f6f47b9af11760457cc028c47587d85256b9c Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Mon, 13 Oct 2025 13:31:26 +0200 Subject: [PATCH 1/4] Add closure, function and arrow function support to ParamTypeByMethodCallTypeRector --- .../Fixture/also_closure.php.inc | 45 +++++++++++++++ .../VariableInSprintfMaskMatcher.php | 7 ++- .../ParamTypeByMethodCallTypeRector.php | 55 ++++++++++--------- 3 files changed, 79 insertions(+), 28 deletions(-) create mode 100644 rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/also_closure.php.inc diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/also_closure.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/also_closure.php.inc new file mode 100644 index 00000000000..bd270ca8e1d --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/also_closure.php.inc @@ -0,0 +1,45 @@ +someTypedService->run($value); + }; + } +} + +?> +----- +someTypedService->run($value); + }; + } +} + +?> diff --git a/rules/TypeDeclaration/NodeAnalyzer/VariableInSprintfMaskMatcher.php b/rules/TypeDeclaration/NodeAnalyzer/VariableInSprintfMaskMatcher.php index 998e9109751..5c6a54cdbf8 100644 --- a/rules/TypeDeclaration/NodeAnalyzer/VariableInSprintfMaskMatcher.php +++ b/rules/TypeDeclaration/NodeAnalyzer/VariableInSprintfMaskMatcher.php @@ -31,7 +31,12 @@ public function __construct( public function matchMask(ClassMethod|Function_ $functionLike, string $variableName, string $mask): bool { $funcCalls = $this->betterNodeFinder->findInstancesOfScoped((array) $functionLike->stmts, FuncCall::class); - $funcCalls = array_values(array_filter($funcCalls, fn(FuncCall $funcCall): bool => $this->nodeNameResolver->isName($funcCall->name, 'sprintf'))); + $funcCalls = array_values( + array_filter($funcCalls, fn (FuncCall $funcCall): bool => $this->nodeNameResolver->isName( + $funcCall->name, + 'sprintf' + )) + ); if (count($funcCalls) !== 1) { return false; diff --git a/rules/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector.php b/rules/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector.php index 2988d97b9ed..1cc419da8e6 100644 --- a/rules/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector.php +++ b/rules/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector.php @@ -4,12 +4,13 @@ namespace Rector\TypeDeclaration\Rector\ClassMethod; +use PhpParser\Node\Stmt\Function_; +use PhpParser\Node\Expr\Closure; use PhpParser\Node; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Param; -use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory; @@ -96,33 +97,25 @@ public function go(string $value) */ public function getNodeTypes(): array { - return [Class_::class]; + return [ClassMethod::class, Function_::class, Closure::class]; } /** - * @param Class_ $node + * @param ClassMethod|Function_|Closure $node */ public function refactor(Node $node): ?Node { - $hasChanged = false; - - foreach ($node->getMethods() as $classMethod) { - if ($this->shouldSkipClassMethod($classMethod)) { - continue; - } - - /** @var array $callers */ - $callers = $this->betterNodeFinder->findInstancesOf( - $classMethod, - [StaticCall::class, MethodCall::class, FuncCall::class] - ); - - $hasClassMethodChanged = $this->refactorClassMethod($classMethod, $callers); - if ($hasClassMethodChanged) { - $hasChanged = true; - } + if ($node instanceof ClassMethod && $this->shouldSkipClassMethod($node)) { + return null; } + /** @var array $callers */ + $callers = $this->betterNodeFinder->findInstancesOf( + $node, + [StaticCall::class, MethodCall::class, FuncCall::class] + ); + + $hasChanged = $this->refactorFunctionLike($node, $callers); if ($hasChanged) { return $node; } @@ -139,8 +132,10 @@ private function shouldSkipClassMethod(ClassMethod $classMethod): bool return $this->parentClassMethodTypeOverrideGuard->hasParentClassMethod($classMethod); } - private function shouldSkipParam(Param $param, ClassMethod $classMethod): bool - { + private function shouldSkipParam( + Param $param, + ClassMethod|Function_|Closure $functionLike + ): bool { // already has type, skip if ($param->type instanceof Node) { return true; @@ -150,18 +145,24 @@ private function shouldSkipParam(Param $param, ClassMethod $classMethod): bool return true; } - return ! $this->paramTypeAddGuard->isLegal($param, $classMethod); + if (! $functionLike instanceof ClassMethod) { + return false; + } + + return ! $this->paramTypeAddGuard->isLegal($param, $functionLike); } /** * @param array $callers */ - private function refactorClassMethod(ClassMethod $classMethod, array $callers): bool - { + private function refactorFunctionLike( + ClassMethod|Closure|Function_ $functionLike, + array $callers + ): bool { $hasChanged = false; - foreach ($classMethod->params as $param) { - if ($this->shouldSkipParam($param, $classMethod)) { + foreach ($functionLike->params as $param) { + if ($this->shouldSkipParam($param, $functionLike)) { continue; } From bc4ac033358cbe24340535f5e1f70d810956a7e9 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Mon, 13 Oct 2025 13:38:38 +0200 Subject: [PATCH 2/4] add arrow function node --- .../Fixture/also_arrow_function.php.inc | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/also_arrow_function.php.inc diff --git a/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/also_arrow_function.php.inc b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/also_arrow_function.php.inc new file mode 100644 index 00000000000..f5604405706 --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector/Fixture/also_arrow_function.php.inc @@ -0,0 +1,41 @@ + $this->someTypedService->run($value); + } +} + +?> +----- + $this->someTypedService->run($value); + } +} + +?> From 3ba8cb98638555a3f0caa361a89e166431820c92 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Mon, 13 Oct 2025 13:39:37 +0200 Subject: [PATCH 3/4] add arrow function support --- .../ClassMethod/ParamTypeByMethodCallTypeRector.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/rules/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector.php b/rules/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector.php index 1cc419da8e6..63a5121d382 100644 --- a/rules/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector.php +++ b/rules/TypeDeclaration/Rector/ClassMethod/ParamTypeByMethodCallTypeRector.php @@ -4,14 +4,15 @@ namespace Rector\TypeDeclaration\Rector\ClassMethod; -use PhpParser\Node\Stmt\Function_; -use PhpParser\Node\Expr\Closure; use PhpParser\Node; +use PhpParser\Node\Expr\ArrowFunction; +use PhpParser\Node\Expr\Closure; use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Param; use PhpParser\Node\Stmt\ClassMethod; +use PhpParser\Node\Stmt\Function_; use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory; use Rector\PhpParser\Node\BetterNodeFinder; @@ -97,7 +98,7 @@ public function go(string $value) */ public function getNodeTypes(): array { - return [ClassMethod::class, Function_::class, Closure::class]; + return [ClassMethod::class, Function_::class, Closure::class, ArrowFunction::class]; } /** @@ -134,7 +135,7 @@ private function shouldSkipClassMethod(ClassMethod $classMethod): bool private function shouldSkipParam( Param $param, - ClassMethod|Function_|Closure $functionLike + ClassMethod|Function_|Closure|ArrowFunction $functionLike ): bool { // already has type, skip if ($param->type instanceof Node) { @@ -156,7 +157,7 @@ private function shouldSkipParam( * @param array $callers */ private function refactorFunctionLike( - ClassMethod|Closure|Function_ $functionLike, + ClassMethod|Closure|Function_|ArrowFunction $functionLike, array $callers ): bool { $hasChanged = false; From a44678a2ae70ffbbdc7ce5a517e6a82f48a908b7 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 13 Oct 2025 11:42:17 +0000 Subject: [PATCH 4/4] [ci-review] Rector Rectify --- src/ChangesReporting/Output/GitHubOutputFormatter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ChangesReporting/Output/GitHubOutputFormatter.php b/src/ChangesReporting/Output/GitHubOutputFormatter.php index 8516157d619..56633532276 100644 --- a/src/ChangesReporting/Output/GitHubOutputFormatter.php +++ b/src/ChangesReporting/Output/GitHubOutputFormatter.php @@ -131,7 +131,7 @@ private function sanitizeAnnotationProperties(array $annotationProperties): stri ); $sanitizedProperties = array_map( - fn (string $key, $value): string => sprintf('%s=%s', $key, $this->sanitizeAnnotationProperty($value)), + fn (string $key, string|int|null $value): string => sprintf('%s=%s', $key, $this->sanitizeAnnotationProperty($value)), array_keys($nonNullProperties), $nonNullProperties );