mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-27 13:13:31 +00:00
decouple ChildParamPopulator
This commit is contained in:
parent
442e2abac5
commit
5df24b4087
|
@ -0,0 +1,150 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Rector\TypeDeclaration\ChildPopulator;
|
||||||
|
|
||||||
|
use PhpParser\Node;
|
||||||
|
use PhpParser\Node\FunctionLike;
|
||||||
|
use PhpParser\Node\Identifier;
|
||||||
|
use PhpParser\Node\Name;
|
||||||
|
use PhpParser\Node\NullableType;
|
||||||
|
use PhpParser\Node\Stmt\Class_;
|
||||||
|
use PhpParser\Node\Stmt\ClassLike;
|
||||||
|
use PhpParser\Node\Stmt\ClassMethod;
|
||||||
|
use PhpParser\Node\Stmt\Function_;
|
||||||
|
use PhpParser\Node\UnionType;
|
||||||
|
use PHPStan\Type\MixedType;
|
||||||
|
use PHPStan\Type\ObjectType;
|
||||||
|
use PHPStan\Type\StaticType;
|
||||||
|
use PHPStan\Type\Type;
|
||||||
|
use Rector\ChangesReporting\Collector\RectorChangeCollector;
|
||||||
|
use Rector\NodeCollector\NodeFinder\ClassLikeParsedNodesFinder;
|
||||||
|
use Rector\NodeNameResolver\NodeNameResolver;
|
||||||
|
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||||
|
use Rector\PHPStan\Type\SelfObjectType;
|
||||||
|
use Rector\StaticTypeMapper\StaticTypeMapper;
|
||||||
|
use Rector\TypeDeclaration\ValueObject\NewType;
|
||||||
|
|
||||||
|
final class ChildParamPopulator
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var ClassLikeParsedNodesFinder
|
||||||
|
*/
|
||||||
|
private $classLikeParsedNodesFinder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var StaticTypeMapper
|
||||||
|
*/
|
||||||
|
private $staticTypeMapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var NodeNameResolver
|
||||||
|
*/
|
||||||
|
private $nodeNameResolver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var RectorChangeCollector
|
||||||
|
*/
|
||||||
|
private $rectorChangeCollector;
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
ClassLikeParsedNodesFinder $classLikeParsedNodesFinder,
|
||||||
|
StaticTypeMapper $staticTypeMapper,
|
||||||
|
NodeNameResolver $nodeNameResolver,
|
||||||
|
RectorChangeCollector $rectorChangeCollector
|
||||||
|
) {
|
||||||
|
$this->classLikeParsedNodesFinder = $classLikeParsedNodesFinder;
|
||||||
|
$this->staticTypeMapper = $staticTypeMapper;
|
||||||
|
$this->nodeNameResolver = $nodeNameResolver;
|
||||||
|
$this->rectorChangeCollector = $rectorChangeCollector;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add typehint to all children
|
||||||
|
* @param ClassMethod|Function_ $functionLike
|
||||||
|
*/
|
||||||
|
public function populateChildClassMethod(FunctionLike $functionLike, int $position, Type $paramType): void
|
||||||
|
{
|
||||||
|
if (! $functionLike instanceof ClassMethod) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var string|null $className */
|
||||||
|
$className = $functionLike->getAttribute(AttributeKey::CLASS_NAME);
|
||||||
|
// anonymous class
|
||||||
|
if ($className === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$childrenClassLikes = $this->classLikeParsedNodesFinder->findClassesAndInterfacesByType($className);
|
||||||
|
|
||||||
|
// update their methods as well
|
||||||
|
foreach ($childrenClassLikes as $childClassLike) {
|
||||||
|
if ($childClassLike instanceof Class_) {
|
||||||
|
$usedTraits = $this->classLikeParsedNodesFinder->findUsedTraitsInClass($childClassLike);
|
||||||
|
|
||||||
|
foreach ($usedTraits as $trait) {
|
||||||
|
$this->addParamTypeToMethod($trait, $position, $functionLike, $paramType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->addParamTypeToMethod($childClassLike, $position, $functionLike, $paramType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function addParamTypeToMethod(
|
||||||
|
ClassLike $classLike,
|
||||||
|
int $position,
|
||||||
|
ClassMethod $classMethod,
|
||||||
|
Type $paramType
|
||||||
|
): void {
|
||||||
|
$methodName = $this->nodeNameResolver->getName($classMethod);
|
||||||
|
if ($methodName === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$currentClassMethod = $classLike->getMethod($methodName);
|
||||||
|
if ($currentClassMethod === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! isset($currentClassMethod->params[$position])) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$paramNode = $currentClassMethod->params[$position];
|
||||||
|
|
||||||
|
// already has a type
|
||||||
|
if ($paramNode->type !== null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$resolvedChildType = $this->resolveChildTypeNode($paramType);
|
||||||
|
if ($resolvedChildType === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// let the method know it was changed now
|
||||||
|
$paramNode->type = $resolvedChildType;
|
||||||
|
$paramNode->type->setAttribute(NewType::HAS_NEW_INHERITED_TYPE, true);
|
||||||
|
|
||||||
|
$this->rectorChangeCollector->notifyNodeFileInfo($paramNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Name|NullableType|Identifier|UnionType|null
|
||||||
|
*/
|
||||||
|
private function resolveChildTypeNode(Type $type): ?Node
|
||||||
|
{
|
||||||
|
if ($type instanceof MixedType) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($type instanceof SelfObjectType || $type instanceof StaticType) {
|
||||||
|
$type = new ObjectType($type->getClassName());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($type);
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,11 +30,6 @@ use Rector\VendorLocker\VendorLockResolver;
|
||||||
*/
|
*/
|
||||||
abstract class AbstractTypeDeclarationRector extends AbstractRector
|
abstract class AbstractTypeDeclarationRector extends AbstractRector
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
protected const HAS_NEW_INHERITED_TYPE = 'has_new_inherited_return_type';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var DocBlockManipulator
|
* @var DocBlockManipulator
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -7,8 +7,6 @@ namespace Rector\TypeDeclaration\Rector\FunctionLike;
|
||||||
use PhpParser\Node;
|
use PhpParser\Node;
|
||||||
use PhpParser\Node\FunctionLike;
|
use PhpParser\Node\FunctionLike;
|
||||||
use PhpParser\Node\Param;
|
use PhpParser\Node\Param;
|
||||||
use PhpParser\Node\Stmt\Class_;
|
|
||||||
use PhpParser\Node\Stmt\ClassLike;
|
|
||||||
use PhpParser\Node\Stmt\ClassMethod;
|
use PhpParser\Node\Stmt\ClassMethod;
|
||||||
use PhpParser\Node\Stmt\Function_;
|
use PhpParser\Node\Stmt\Function_;
|
||||||
use PHPStan\Type\MixedType;
|
use PHPStan\Type\MixedType;
|
||||||
|
@ -16,9 +14,10 @@ use PHPStan\Type\Type;
|
||||||
use Rector\Core\RectorDefinition\CodeSample;
|
use Rector\Core\RectorDefinition\CodeSample;
|
||||||
use Rector\Core\RectorDefinition\RectorDefinition;
|
use Rector\Core\RectorDefinition\RectorDefinition;
|
||||||
use Rector\Core\ValueObject\PhpVersionFeature;
|
use Rector\Core\ValueObject\PhpVersionFeature;
|
||||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
|
||||||
use Rector\PHPStanStaticTypeMapper\PHPStanStaticTypeMapper;
|
use Rector\PHPStanStaticTypeMapper\PHPStanStaticTypeMapper;
|
||||||
|
use Rector\TypeDeclaration\ChildPopulator\ChildParamPopulator;
|
||||||
use Rector\TypeDeclaration\TypeInferer\ParamTypeInferer;
|
use Rector\TypeDeclaration\TypeInferer\ParamTypeInferer;
|
||||||
|
use Rector\TypeDeclaration\ValueObject\NewType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see \Rector\TypeDeclaration\Tests\Rector\FunctionLike\ParamTypeDeclarationRector\ParamTypeDeclarationRectorTest
|
* @see \Rector\TypeDeclaration\Tests\Rector\FunctionLike\ParamTypeDeclarationRector\ParamTypeDeclarationRectorTest
|
||||||
|
@ -30,9 +29,15 @@ final class ParamTypeDeclarationRector extends AbstractTypeDeclarationRector
|
||||||
*/
|
*/
|
||||||
private $paramTypeInferer;
|
private $paramTypeInferer;
|
||||||
|
|
||||||
public function __construct(ParamTypeInferer $paramTypeInferer)
|
/**
|
||||||
|
* @var ChildParamPopulator
|
||||||
|
*/
|
||||||
|
private $childParamPopulator;
|
||||||
|
|
||||||
|
public function __construct(ParamTypeInferer $paramTypeInferer, ChildParamPopulator $childParamPopulator)
|
||||||
{
|
{
|
||||||
$this->paramTypeInferer = $paramTypeInferer;
|
$this->paramTypeInferer = $paramTypeInferer;
|
||||||
|
$this->childParamPopulator = $childParamPopulator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDefinition(): RectorDefinition
|
public function getDefinition(): RectorDefinition
|
||||||
|
@ -148,76 +153,7 @@ PHP
|
||||||
}
|
}
|
||||||
|
|
||||||
$param->type = $paramTypeNode;
|
$param->type = $paramTypeNode;
|
||||||
$this->populateChildren($functionLike, $position, $inferedType);
|
$this->childParamPopulator->populateChildClassMethod($functionLike, $position, $inferedType);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add typehint to all children
|
|
||||||
* @param ClassMethod|Function_ $functionLike
|
|
||||||
*/
|
|
||||||
private function populateChildren(FunctionLike $functionLike, int $position, Type $paramType): void
|
|
||||||
{
|
|
||||||
if (! $functionLike instanceof ClassMethod) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @var string|null $className */
|
|
||||||
$className = $functionLike->getAttribute(AttributeKey::CLASS_NAME);
|
|
||||||
// anonymous class
|
|
||||||
if ($className === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$childrenClassLikes = $this->classLikeParsedNodesFinder->findClassesAndInterfacesByType($className);
|
|
||||||
|
|
||||||
// update their methods as well
|
|
||||||
foreach ($childrenClassLikes as $childClassLike) {
|
|
||||||
if ($childClassLike instanceof Class_) {
|
|
||||||
$usedTraits = $this->classLikeParsedNodesFinder->findUsedTraitsInClass($childClassLike);
|
|
||||||
|
|
||||||
foreach ($usedTraits as $trait) {
|
|
||||||
$this->addParamTypeToMethod($trait, $position, $functionLike, $paramType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->addParamTypeToMethod($childClassLike, $position, $functionLike, $paramType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function addParamTypeToMethod(
|
|
||||||
ClassLike $classLike,
|
|
||||||
int $position,
|
|
||||||
ClassMethod $classMethod,
|
|
||||||
Type $paramType
|
|
||||||
): void {
|
|
||||||
$methodName = $this->getName($classMethod);
|
|
||||||
|
|
||||||
$currentClassMethod = $classLike->getMethod($methodName);
|
|
||||||
if ($currentClassMethod === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (! isset($currentClassMethod->params[$position])) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$paramNode = $currentClassMethod->params[$position];
|
|
||||||
|
|
||||||
// already has a type
|
|
||||||
if ($paramNode->type !== null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$resolvedChildType = $this->resolveChildTypeNode($paramType);
|
|
||||||
if ($resolvedChildType === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// let the method know it was changed now
|
|
||||||
$paramNode->type = $resolvedChildType;
|
|
||||||
$paramNode->type->setAttribute(self::HAS_NEW_INHERITED_TYPE, true);
|
|
||||||
|
|
||||||
$this->notifyNodeFileInfo($paramNode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function shouldSkipParam(Param $param, FunctionLike $functionLike, int $position): bool
|
private function shouldSkipParam(Param $param, FunctionLike $functionLike, int $position): bool
|
||||||
|
@ -236,6 +172,6 @@ PHP
|
||||||
}
|
}
|
||||||
|
|
||||||
// already set → skip
|
// already set → skip
|
||||||
return ! $param->type->getAttribute(self::HAS_NEW_INHERITED_TYPE, false);
|
return ! $param->type->getAttribute(NewType::HAS_NEW_INHERITED_TYPE, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ namespace Rector\TypeDeclaration\TypeAlreadyAddedChecker;
|
||||||
|
|
||||||
use Iterator;
|
use Iterator;
|
||||||
use PhpParser\Node;
|
use PhpParser\Node;
|
||||||
|
use PhpParser\Node\FunctionLike;
|
||||||
use PhpParser\Node\Identifier;
|
use PhpParser\Node\Identifier;
|
||||||
use PhpParser\Node\Name;
|
use PhpParser\Node\Name;
|
||||||
use PhpParser\Node\NullableType;
|
use PhpParser\Node\NullableType;
|
||||||
|
@ -58,11 +59,11 @@ final class ReturnTypeAlreadyAddedChecker
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param ClassMethod|Function_ $node
|
* @param ClassMethod|Function_ $functionLike
|
||||||
*/
|
*/
|
||||||
public function isReturnTypeAlreadyAdded(Node $node, Type $returnType): bool
|
public function isReturnTypeAlreadyAdded(FunctionLike $functionLike, Type $returnType): bool
|
||||||
{
|
{
|
||||||
$nodeReturnType = $node->returnType;
|
$nodeReturnType = $functionLike->returnType;
|
||||||
|
|
||||||
/** @param Identifier|Name|NullableType|PhpParserUnionType|null $returnTypeNode */
|
/** @param Identifier|Name|NullableType|PhpParserUnionType|null $returnTypeNode */
|
||||||
if ($nodeReturnType === null) {
|
if ($nodeReturnType === null) {
|
||||||
|
@ -70,7 +71,7 @@ final class ReturnTypeAlreadyAddedChecker
|
||||||
}
|
}
|
||||||
|
|
||||||
$returnNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($returnType);
|
$returnNode = $this->staticTypeMapper->mapPHPStanTypeToPhpParserNode($returnType);
|
||||||
if ($this->betterStandardPrinter->areNodesEqual($nodeReturnType, $returnNode)) {
|
if ($this->betterStandardPrinter->areNodesWithoutCommentsEqual($nodeReturnType, $returnNode)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,14 +90,16 @@ final class ReturnTypeAlreadyAddedChecker
|
||||||
}
|
}
|
||||||
|
|
||||||
// prevent overriding self with itself
|
// prevent overriding self with itself
|
||||||
if ($this->betterStandardPrinter->printWithoutComments($node->returnType) === 'self') {
|
if (! $functionLike->returnType instanceof Name) {
|
||||||
$className = $node->getAttribute(AttributeKey::CLASS_NAME);
|
return false;
|
||||||
if (ltrim($this->betterStandardPrinter->printWithoutComments($returnNode), '\\') === $className) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
if ($functionLike->returnType->toLowerString() !== 'self') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$className = $functionLike->getAttribute(AttributeKey::CLASS_NAME);
|
||||||
|
return ltrim($this->betterStandardPrinter->printWithoutComments($returnNode), '\\') === $className;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -26,11 +26,13 @@ final class ParamTypeInferer
|
||||||
|
|
||||||
public function inferParam(Param $param): Type
|
public function inferParam(Param $param): Type
|
||||||
{
|
{
|
||||||
foreach ($this->paramTypeInferers as $paramTypeInferers) {
|
foreach ($this->paramTypeInferers as $paramTypeInferer) {
|
||||||
$type = $paramTypeInferers->inferParam($param);
|
$type = $paramTypeInferer->inferParam($param);
|
||||||
if (! $type instanceof MixedType) {
|
if ($type instanceof MixedType) {
|
||||||
return $type;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return $type;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new MixedType();
|
return new MixedType();
|
||||||
|
|
13
rules/type-declaration/src/ValueObject/NewType.php
Normal file
13
rules/type-declaration/src/ValueObject/NewType.php
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Rector\TypeDeclaration\ValueObject;
|
||||||
|
|
||||||
|
final class NewType
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
public const HAS_NEW_INHERITED_TYPE = 'has_new_inherited_type';
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\ParamTypeDeclarationRector\Fixture;
|
||||||
|
|
||||||
|
class InferFromPropertyPhpdocType
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
private $id;
|
||||||
|
|
||||||
|
public function setId($id)
|
||||||
|
{
|
||||||
|
$this->id = $id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
-----
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\ParamTypeDeclarationRector\Fixture;
|
||||||
|
|
||||||
|
class InferFromPropertyPhpdocType
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var int
|
||||||
|
*/
|
||||||
|
private $id;
|
||||||
|
|
||||||
|
public function setId(int $id)
|
||||||
|
{
|
||||||
|
$this->id = $id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
|
@ -4,10 +4,7 @@ namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\ParamTypeDeclarationRe
|
||||||
|
|
||||||
class InferFromPropertyType
|
class InferFromPropertyType
|
||||||
{
|
{
|
||||||
/**
|
private int $id;
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
private $id;
|
|
||||||
|
|
||||||
public function setId($id)
|
public function setId($id)
|
||||||
{
|
{
|
||||||
|
@ -23,10 +20,7 @@ namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\ParamTypeDeclarationRe
|
||||||
|
|
||||||
class InferFromPropertyType
|
class InferFromPropertyType
|
||||||
{
|
{
|
||||||
/**
|
private int $id;
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
private $id;
|
|
||||||
|
|
||||||
public function setId(int $id)
|
public function setId(int $id)
|
||||||
{
|
{
|
|
@ -0,0 +1,37 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Rector\TypeDeclaration\Tests\Rector\FunctionLike\ParamTypeDeclarationRector;
|
||||||
|
|
||||||
|
use Iterator;
|
||||||
|
use Rector\Core\Testing\PHPUnit\AbstractRectorTestCase;
|
||||||
|
use Rector\Core\ValueObject\PhpVersionFeature;
|
||||||
|
use Rector\TypeDeclaration\Rector\FunctionLike\ParamTypeDeclarationRector;
|
||||||
|
|
||||||
|
final class PropertyTypeParamTypeDeclarationRectorTest extends AbstractRectorTestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @requires PHP >= 7.4
|
||||||
|
* @dataProvider provideData()
|
||||||
|
*/
|
||||||
|
public function test(string $file): void
|
||||||
|
{
|
||||||
|
$this->doTestFile($file);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function provideData(): Iterator
|
||||||
|
{
|
||||||
|
return $this->yieldFilesFromDirectory(__DIR__ . '/FixturePropertyType');
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getRectorClass(): string
|
||||||
|
{
|
||||||
|
return ParamTypeDeclarationRector::class;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getPhpVersion(): string
|
||||||
|
{
|
||||||
|
return PhpVersionFeature::TYPED_PROPERTIES;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user