Updated Rector to commit 3ed476b9ab65958d85416e48a810b11dbaf4283a

3ed476b9ab [TypeDeclaration] Add AddReturnArrayDocblockBasedOnArrayMapRector (#6235)
This commit is contained in:
Tomas Votruba 2024-08-14 22:29:38 +00:00
parent 86cae8066f
commit c5919c93d6
4 changed files with 175 additions and 2 deletions

View File

@ -0,0 +1,171 @@
<?php
declare (strict_types=1);
namespace Rector\TypeDeclaration\Rector\ClassMethod;
use PhpParser\Node;
use PhpParser\Node\Expr\ArrowFunction;
use PhpParser\Node\Expr\Closure;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\FunctionLike;
use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Function_;
use PHPStan\Type\ArrayType;
use PHPStan\Type\MixedType;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger;
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
use Rector\PhpParser\Node\BetterNodeFinder;
use Rector\Rector\AbstractRector;
use Rector\StaticTypeMapper\StaticTypeMapper;
use Rector\TypeDeclaration\NodeAnalyzer\ReturnAnalyzer;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* @see \Rector\Tests\TypeDeclaration\Rector\ClassMethod\AddReturnArrayDocblockBasedOnArrayMapRector\AddReturnArrayDocblockBasedOnArrayMapRectorTest
*/
final class AddReturnArrayDocblockBasedOnArrayMapRector extends AbstractRector
{
/**
* @readonly
* @var \Rector\PhpParser\Node\BetterNodeFinder
*/
private $betterNodeFinder;
/**
* @readonly
* @var \Rector\TypeDeclaration\NodeAnalyzer\ReturnAnalyzer
*/
private $returnAnalyzer;
/**
* @readonly
* @var \Rector\StaticTypeMapper\StaticTypeMapper
*/
private $staticTypeMapper;
/**
* @readonly
* @var \Rector\NodeTypeResolver\PHPStan\Type\TypeFactory
*/
private $typeFactory;
/**
* @readonly
* @var \Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger
*/
private $phpDocTypeChanger;
/**
* @readonly
* @var \Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory
*/
private $phpDocInfoFactory;
public function __construct(BetterNodeFinder $betterNodeFinder, ReturnAnalyzer $returnAnalyzer, StaticTypeMapper $staticTypeMapper, TypeFactory $typeFactory, PhpDocTypeChanger $phpDocTypeChanger, PhpDocInfoFactory $phpDocInfoFactory)
{
$this->betterNodeFinder = $betterNodeFinder;
$this->returnAnalyzer = $returnAnalyzer;
$this->staticTypeMapper = $staticTypeMapper;
$this->typeFactory = $typeFactory;
$this->phpDocTypeChanger = $phpDocTypeChanger;
$this->phpDocInfoFactory = $phpDocInfoFactory;
}
public function getRuleDefinition() : RuleDefinition
{
return new RuleDefinition('Add @return array docblock based on array_map() return strict type', [new CodeSample(<<<'CODE_SAMPLE'
class SomeClass
{
public function getItems(array $items)
{
return array_map(function ($item): int {
return $item->id;
}, $items);
}
}
CODE_SAMPLE
, <<<'CODE_SAMPLE'
class SomeClass
{
/**
* @return int[]
*/
public function getItems(array $items)
{
return array_map(function ($item): int {
return $item->id;
}, $items);
}
}
CODE_SAMPLE
)]);
}
public function getNodeTypes() : array
{
return [ClassMethod::class, Function_::class];
}
/**
* @param ClassMethod|Function_ $node
* @return null|\PhpParser\Node\Stmt\Function_|\PhpParser\Node\Stmt\ClassMethod
*/
public function refactor(Node $node)
{
$returnsScoped = $this->betterNodeFinder->findReturnsScoped($node);
if ($this->hasNonArrayReturnType($node)) {
return null;
}
// nothing to return? skip it
if ($returnsScoped === []) {
return null;
}
// only returns with expr and no void
if (!$this->returnAnalyzer->hasOnlyReturnWithExpr($node, $returnsScoped)) {
return null;
}
$closureReturnTypes = [];
foreach ($returnsScoped as $returnScoped) {
if (!$returnScoped->expr instanceof FuncCall) {
return null;
}
$arrayMapClosure = $this->matchArrayMapClosure($returnScoped->expr);
if (!$arrayMapClosure instanceof FunctionLike) {
return null;
}
if (!$arrayMapClosure->returnType instanceof Node) {
return null;
}
$closureReturnTypes[] = $this->staticTypeMapper->mapPhpParserNodePHPStanType($arrayMapClosure->returnType);
}
$returnType = $this->typeFactory->createMixedPassedOrUnionType($closureReturnTypes);
$arrayType = new ArrayType(new MixedType(), $returnType);
$functionLikePhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node);
$hasChanged = $this->phpDocTypeChanger->changeReturnType($node, $functionLikePhpDocInfo, $arrayType);
if ($hasChanged) {
return null;
}
return $node;
}
/**
* @param \PhpParser\Node\Stmt\ClassMethod|\PhpParser\Node\Stmt\Function_ $functionLike
*/
private function hasNonArrayReturnType($functionLike) : bool
{
if (!$functionLike->returnType instanceof Identifier) {
return \false;
}
return $functionLike->returnType->toLowerString() !== 'array';
}
/**
* @return \PhpParser\Node\Expr\Closure|\PhpParser\Node\Expr\ArrowFunction|null
*/
private function matchArrayMapClosure(FuncCall $funcCall)
{
if (!$this->isName($funcCall, 'array_map')) {
return null;
}
if ($funcCall->isFirstClassCallable()) {
return null;
}
// lets infer strict array_map() type
$firstArg = $funcCall->getArgs()[0];
if (!$firstArg->value instanceof Closure && !$firstArg->value instanceof ArrowFunction) {
return null;
}
return $firstArg->value;
}
}

View File

@ -19,12 +19,12 @@ final class VersionResolver
* @api
* @var string
*/
public const PACKAGE_VERSION = 'fdd7f638a6e1f4d7ebf244f5827acdb687f14b3c';
public const PACKAGE_VERSION = '3ed476b9ab65958d85416e48a810b11dbaf4283a';
/**
* @api
* @var string
*/
public const RELEASE_DATE = '2024-08-14 20:21:02';
public const RELEASE_DATE = '2024-08-15 00:26:49';
/**
* @var int
*/

View File

@ -2444,6 +2444,7 @@ return array(
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\AddParamTypeBasedOnPHPUnitDataProviderRector' => $baseDir . '/rules/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector.php',
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\AddParamTypeDeclarationRector' => $baseDir . '/rules/TypeDeclaration/Rector/ClassMethod/AddParamTypeDeclarationRector.php',
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\AddParamTypeFromPropertyTypeRector' => $baseDir . '/rules/TypeDeclaration/Rector/ClassMethod/AddParamTypeFromPropertyTypeRector.php',
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\AddReturnArrayDocblockBasedOnArrayMapRector' => $baseDir . '/rules/TypeDeclaration/Rector/ClassMethod/AddReturnArrayDocblockBasedOnArrayMapRector.php',
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\AddReturnTypeDeclarationBasedOnParentClassMethodRector' => $baseDir . '/rules/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationBasedOnParentClassMethodRector.php',
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\AddReturnTypeDeclarationRector' => $baseDir . '/rules/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationRector.php',
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\AddTypeFromResourceDocblockRector' => $baseDir . '/rules/TypeDeclaration/Rector/ClassMethod/AddTypeFromResourceDocblockRector.php',

View File

@ -2663,6 +2663,7 @@ class ComposerStaticInit969cf4fb8686733ba64d08544d342ca0
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\AddParamTypeBasedOnPHPUnitDataProviderRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/ClassMethod/AddParamTypeBasedOnPHPUnitDataProviderRector.php',
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\AddParamTypeDeclarationRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/ClassMethod/AddParamTypeDeclarationRector.php',
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\AddParamTypeFromPropertyTypeRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/ClassMethod/AddParamTypeFromPropertyTypeRector.php',
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\AddReturnArrayDocblockBasedOnArrayMapRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/ClassMethod/AddReturnArrayDocblockBasedOnArrayMapRector.php',
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\AddReturnTypeDeclarationBasedOnParentClassMethodRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationBasedOnParentClassMethodRector.php',
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\AddReturnTypeDeclarationRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/ClassMethod/AddReturnTypeDeclarationRector.php',
'Rector\\TypeDeclaration\\Rector\\ClassMethod\\AddTypeFromResourceDocblockRector' => __DIR__ . '/../..' . '/rules/TypeDeclaration/Rector/ClassMethod/AddTypeFromResourceDocblockRector.php',