From 8460c80323b490989b781cc8042e2a5d57f90d46 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Thu, 7 Aug 2025 10:16:10 +0200 Subject: [PATCH] add list<...> support to AddArrayFunctionClosureParamTypeRector --- .../array_filter_with_docblock_list.php.inc | 33 +++++++++ ...AddArrayFunctionClosureParamTypeRector.php | 68 ++++++++++++++----- ...wFunctionParamArrayWhereDimFetchRector.php | 4 +- 3 files changed, 86 insertions(+), 19 deletions(-) create mode 100644 rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayFunctionClosureParamTypeRector/Fixture/array_filter_with_docblock_list.php.inc diff --git a/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayFunctionClosureParamTypeRector/Fixture/array_filter_with_docblock_list.php.inc b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayFunctionClosureParamTypeRector/Fixture/array_filter_with_docblock_list.php.inc new file mode 100644 index 00000000000..0a5fd7765ba --- /dev/null +++ b/rules-tests/TypeDeclaration/Rector/FuncCall/AddArrayFunctionClosureParamTypeRector/Fixture/array_filter_with_docblock_list.php.inc @@ -0,0 +1,33 @@ + $items + */ + public function run(array $items) + { + $result = array_filter($items, fn ($item) => $item * 2); + } +} + +?> +----- + $items + */ + public function run(array $items) + { + $result = array_filter($items, fn (int $item) => $item * 2); + } +} + +?> diff --git a/rules/TypeDeclaration/Rector/FuncCall/AddArrayFunctionClosureParamTypeRector.php b/rules/TypeDeclaration/Rector/FuncCall/AddArrayFunctionClosureParamTypeRector.php index f3fccaefdb4..14bc2b3b8c7 100644 --- a/rules/TypeDeclaration/Rector/FuncCall/AddArrayFunctionClosureParamTypeRector.php +++ b/rules/TypeDeclaration/Rector/FuncCall/AddArrayFunctionClosureParamTypeRector.php @@ -8,9 +8,12 @@ use PhpParser\Node\Expr\ArrowFunction; use PhpParser\Node\Expr\Closure; use PhpParser\Node\Expr\FuncCall; +use PHPStan\Type\Accessory\AccessoryArrayListType; use PHPStan\Type\ArrayType; use PHPStan\Type\Constant\ConstantArrayType; +use PHPStan\Type\IntersectionType; use PHPStan\Type\MixedType; +use PHPStan\Type\Type; use Rector\PHPStanStaticTypeMapper\Enum\TypeKind; use Rector\Rector\AbstractRector; use Rector\StaticTypeMapper\StaticTypeMapper; @@ -26,7 +29,7 @@ final class AddArrayFunctionClosureParamTypeRector extends AbstractRector implements MinPhpVersionInterface { public function __construct( - private readonly StaticTypeMapper $staticTypeMapper + private readonly StaticTypeMapper $staticTypeMapper, ) { } @@ -73,6 +76,8 @@ public function refactor(Node $node): ?Node return null; } + $hasChanged = false; + foreach (NativeFuncCallPositions::ARRAY_AND_CALLBACK_POSITIONS as $functionName => $positions) { if (! $this->isName($node, $functionName)) { continue; @@ -96,27 +101,29 @@ public function refactor(Node $node): ?Node } $passedExprType = $this->getType($node->getArgs()[$arrayPosition]->value); - if ($passedExprType instanceof ConstantArrayType || $passedExprType instanceof ArrayType) { - $singlePassedExprType = $passedExprType->getItemType(); - - if ($singlePassedExprType instanceof MixedType) { - continue; - } - $paramType = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode( - $singlePassedExprType, - TypeKind::PARAM - ); - - if (! $paramType instanceof Node) { - continue; - } + $singlePassedExprType = $this->resolveArrayItemType($passedExprType); + if (! $singlePassedExprType instanceof Type) { + continue; + } + if ($singlePassedExprType instanceof MixedType) { + continue; + } - $arrowFunctionParam->type = $paramType; + $paramType = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode( + $singlePassedExprType, + TypeKind::PARAM + ); - return $node; + if (! $paramType instanceof Node) { + continue; } + $hasChanged = true; + $arrowFunctionParam->type = $paramType; + } + + if ($hasChanged === false) { return null; } @@ -127,4 +134,31 @@ public function provideMinPhpVersion(): int { return PhpVersionFeature::SCALAR_TYPES; } + + private function resolveArrayItemType(Type $mainType): ?Type + { + if ($mainType instanceof ConstantArrayType || $mainType instanceof ArrayType) { + return $mainType->getItemType(); + } + + if ($mainType instanceof IntersectionType) { + foreach ($mainType->getTypes() as $subType) { + if ($subType instanceof AccessoryArrayListType) { + continue; + } + + if (! $subType->isArray()->yes()) { + continue; + } + + if (! $subType instanceof ArrayType) { + continue; + } + + return $subType->getItemType(); + } + } + + return null; + } } diff --git a/rules/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector.php b/rules/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector.php index 9f8ae4c813f..f61a054ce52 100644 --- a/rules/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector.php +++ b/rules/TypeDeclaration/Rector/FuncCall/AddArrowFunctionParamArrayWhereDimFetchRector.php @@ -4,13 +4,13 @@ namespace Rector\TypeDeclaration\Rector\FuncCall; -use PhpParser\Node\Expr\Variable; -use PhpParser\Node\Expr\Instanceof_; use PhpParser\Node; use PhpParser\Node\Expr\ArrayDimFetch; use PhpParser\Node\Expr\ArrowFunction; use PhpParser\Node\Expr\Closure; use PhpParser\Node\Expr\FuncCall; +use PhpParser\Node\Expr\Instanceof_; +use PhpParser\Node\Expr\Variable; use PhpParser\Node\Identifier; use Rector\PhpParser\Node\BetterNodeFinder; use Rector\Rector\AbstractRector;