mirror of
https://github.com/rectorphp/rector.git
synced 2024-05-30 07:50:53 +00:00
[Restoration] Add InferParamFromClassMethodReturnRector (#4471)
Co-authored-by: rector-bot <tomas@getrector.org>
This commit is contained in:
parent
10ae0d687a
commit
6c51f3fb4f
|
@ -1,4 +1,4 @@
|
|||
# All 596 Rectors Overview
|
||||
# All 597 Rectors Overview
|
||||
|
||||
- [Projects](#projects)
|
||||
---
|
||||
|
@ -64,7 +64,7 @@
|
|||
- [RectorGenerator](#rectorgenerator) (1)
|
||||
- [RemovingStatic](#removingstatic) (6)
|
||||
- [Renaming](#renaming) (10)
|
||||
- [Restoration](#restoration) (8)
|
||||
- [Restoration](#restoration) (9)
|
||||
- [SOLID](#solid) (13)
|
||||
- [Sensio](#sensio) (3)
|
||||
- [StrictCodeQuality](#strictcodequality) (1)
|
||||
|
@ -13928,6 +13928,56 @@ return static function (ContainerConfigurator $containerConfigurator): void {
|
|||
|
||||
<br><br>
|
||||
|
||||
### `InferParamFromClassMethodReturnRector`
|
||||
|
||||
- class: [`Rector\Restoration\Rector\ClassMethod\InferParamFromClassMethodReturnRector`](/rules/restoration/src/Rector/ClassMethod/InferParamFromClassMethodReturnRector.php)
|
||||
- [test fixtures](/rules/restoration/tests/Rector/ClassMethod/InferParamFromClassMethodReturnRector/Fixture)
|
||||
|
||||
Change @param doc based on another method return type
|
||||
|
||||
```php
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Rector\Restoration\Rector\ClassMethod\InferParamFromClassMethodReturnRector;
|
||||
use Rector\Restoration\ValueObject\InferParamFromClassMethodReturn;
|
||||
use function Rector\SymfonyPhpConfig\inline_value_objects;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$services = $containerConfigurator->services();
|
||||
|
||||
$services->set(InferParamFromClassMethodReturnRector::class)
|
||||
->call('configure', [[
|
||||
InferParamFromClassMethodReturnRector::INFER_PARAMS_FROM_CLASS_METHOD_RETURNS => inline_value_objects(
|
||||
[new InferParamFromClassMethodReturn('SomeClass', 'process', 'getNodeTypes')]
|
||||
),
|
||||
]]);
|
||||
};
|
||||
```
|
||||
|
||||
↓
|
||||
|
||||
```diff
|
||||
class SomeClass
|
||||
{
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [String_::class];
|
||||
}
|
||||
|
||||
+ /**
|
||||
+ * @param String_ $node
|
||||
+ */
|
||||
public function process(Node $node)
|
||||
{
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<br><br>
|
||||
|
||||
### `MakeTypedPropertyNullableIfCheckedRector`
|
||||
|
||||
- class: [`Rector\Restoration\Rector\Property\MakeTypedPropertyNullableIfCheckedRector`](/rules/restoration/src/Rector/Property/MakeTypedPropertyNullableIfCheckedRector.php)
|
||||
|
|
|
@ -13,7 +13,6 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
|
|||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\AttributeAwareParamTagValueNode;
|
||||
|
@ -34,7 +33,6 @@ use Rector\PhpAttribute\Contract\PhpAttributableTagNodeInterface;
|
|||
use Rector\PHPStan\Type\FullyQualifiedObjectType;
|
||||
use Rector\PHPStan\Type\ShortenedObjectType;
|
||||
use Rector\StaticTypeMapper\StaticTypeMapper;
|
||||
use Webmozart\Assert\Assert;
|
||||
|
||||
/**
|
||||
* @see \Rector\BetterPhpDocParser\Tests\PhpDocInfo\PhpDocInfo\PhpDocInfoTest
|
||||
|
@ -372,13 +370,8 @@ final class PhpDocInfo
|
|||
return $this->tokens === [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Type|TypeNode $type
|
||||
*/
|
||||
public function changeParamType($type, Param $param, string $paramName): void
|
||||
public function changeParamType(Type $type, Param $param, string $paramName): void
|
||||
{
|
||||
Assert::isAnyOf($type, [Type::class, TypeNode::class]);
|
||||
|
||||
$this->phpDocTypeChanger->changeParamType($this, $type, $param, $paramName);
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ declare(strict_types=1);
|
|||
namespace Rector\BetterPhpDocParser\PhpDocManipulator;
|
||||
|
||||
use PhpParser\Node\Param;
|
||||
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
|
||||
use PHPStan\Type\Constant\ConstantArrayType;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\NeverType;
|
||||
|
@ -19,7 +18,6 @@ use Rector\Core\Exception\ShouldNotHappenException;
|
|||
use Rector\NodeTypeResolver\PHPStan\TypeComparator;
|
||||
use Rector\StaticTypeMapper\StaticTypeMapper;
|
||||
use Rector\TypeDeclaration\PhpDocParser\ParamPhpDocNodeFactory;
|
||||
use Webmozart\Assert\Assert;
|
||||
|
||||
final class PhpDocTypeChanger
|
||||
{
|
||||
|
@ -115,23 +113,24 @@ final class PhpDocTypeChanger
|
|||
$this->notifyChange();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Type|TypeNode $type
|
||||
*/
|
||||
public function changeParamType(PhpDocInfo $phpDocInfo, $type, Param $param, string $paramName): void
|
||||
public function changeParamType(PhpDocInfo $phpDocInfo, Type $newType, Param $param, string $paramName): void
|
||||
{
|
||||
Assert::isAnyOf($type, [Type::class, TypeNode::class]);
|
||||
|
||||
if (! $type instanceof TypeNode) {
|
||||
$phpDocType = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode($type);
|
||||
} else {
|
||||
$phpDocType = $type;
|
||||
}
|
||||
$phpDocType = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode($newType);
|
||||
|
||||
$paramTagValueNode = $phpDocInfo->getParamTagValueByName($paramName);
|
||||
|
||||
// override existing type
|
||||
if ($paramTagValueNode !== null) {
|
||||
// already set
|
||||
$currentType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType(
|
||||
$paramTagValueNode->type,
|
||||
$param
|
||||
);
|
||||
|
||||
if ($this->typeComparator->areTypesEquals($currentType, $newType)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$paramTagValueNode->type = $phpDocType;
|
||||
} else {
|
||||
$paramTagValueNode = $this->paramPhpDocNodeFactory->create($phpDocType, $param);
|
||||
|
|
|
@ -12,6 +12,7 @@ use PHPStan\Type\MixedType;
|
|||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\TypeWithClassName;
|
||||
use PHPStan\Type\UnionType;
|
||||
use Rector\PHPStan\Type\AliasedObjectType;
|
||||
use Rector\PHPStan\Type\ShortenedObjectType;
|
||||
use Rector\PHPStanStaticTypeMapper\PHPStanStaticTypeMapper;
|
||||
|
||||
|
@ -37,16 +38,12 @@ final class TypeHasher
|
|||
return $this->createTypeHash($type->getItemType()) . '[]';
|
||||
}
|
||||
|
||||
if ($type instanceof ShortenedObjectType) {
|
||||
return $type->getFullyQualifiedName();
|
||||
}
|
||||
|
||||
if ($type instanceof GenericObjectType) {
|
||||
return $this->phpStanStaticTypeMapper->mapToDocString($type);
|
||||
}
|
||||
|
||||
if ($type instanceof TypeWithClassName) {
|
||||
return $type->getClassName();
|
||||
return $this->resolveUniqueTypeWithClassNameHash($type);
|
||||
}
|
||||
|
||||
if ($type instanceof ConstantType) {
|
||||
|
@ -69,6 +66,19 @@ final class TypeHasher
|
|||
return $this->createTypeHash($firstType) === $this->createTypeHash($secondType);
|
||||
}
|
||||
|
||||
private function resolveUniqueTypeWithClassNameHash(Type $type): string
|
||||
{
|
||||
if ($type instanceof ShortenedObjectType) {
|
||||
return $type->getFullyQualifiedName();
|
||||
}
|
||||
|
||||
if ($type instanceof AliasedObjectType) {
|
||||
return $type->getFullyQualifiedClass();
|
||||
}
|
||||
|
||||
return $type->getClassName();
|
||||
}
|
||||
|
||||
private function createUnionTypeHash(UnionType $unionType): string
|
||||
{
|
||||
$unionedTypesHashes = [];
|
||||
|
|
|
@ -7,9 +7,12 @@ namespace Rector\PhpAttribute;
|
|||
use PhpParser\Node;
|
||||
use PhpParser\Node\Attribute;
|
||||
use PhpParser\Node\AttributeGroup;
|
||||
use PhpParser\Node\Expr\ArrowFunction;
|
||||
use PhpParser\Node\Expr\Closure;
|
||||
use PhpParser\Node\Name\FullyQualified;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
|
||||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
|
||||
|
@ -30,7 +33,7 @@ final class AnnotationToAttributeConverter
|
|||
}
|
||||
|
||||
/**
|
||||
* @param Class_|ClassMethod|Property $node
|
||||
* @param Class_|Property|ClassMethod|Function_|Closure|ArrowFunction $node
|
||||
*/
|
||||
public function convertNode(Node $node): ?Node
|
||||
{
|
||||
|
|
|
@ -767,3 +767,5 @@ parameters:
|
|||
- utils/phpstan-extensions/src/Rule/RectorRuleAndValueObjectHaveSameStartsRule.php # 33
|
||||
- utils/phpstan-static-type-mapper-checker/src/Finder/PHPStanTypeClassFinder.php # 15
|
||||
- utils/project-validator/src/Process/ParallelTaskRunner.php # 19
|
||||
|
||||
- '#Parameter \#1 \$node of method Rector\\DeadCode\\Rector\\Plus\\RemoveDeadZeroAndOneOperationRector\:\:refactor\(\) expects PhpParser\\Node\\Expr\\AssignOp\\Div\|PhpParser\\Node\\Expr\\AssignOp\\Minus\|PhpParser\\Node\\Expr\\AssignOp\\Mul\|PhpParser\\Node\\Expr\\AssignOp\\Plus\|PhpParser\\Node\\Expr\\BinaryOp\\Div\|PhpParser\\Node\\Expr\\BinaryOp\\Minus\|PhpParser\\Node\\Expr\\BinaryOp\\Mul\|PhpParser\\Node\\Expr\\BinaryOp\\Plus, PhpParser\\Node\\Expr\\AssignOp\|PhpParser\\Node\\Expr\\BinaryOp given#'
|
||||
|
|
|
@ -4,12 +4,25 @@ declare(strict_types=1);
|
|||
|
||||
use Rector\CodingStyle\Rector\String_\SplitStringClassConstantToClassConstFetchRector;
|
||||
use Rector\Core\Configuration\Option;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\DeadCode\Rector\ClassConst\RemoveUnusedClassConstantRector;
|
||||
use Rector\Php55\Rector\String_\StringClassNameToClassConstantRector;
|
||||
use Rector\Restoration\Rector\ClassMethod\InferParamFromClassMethodReturnRector;
|
||||
use Rector\Restoration\ValueObject\InferParamFromClassMethodReturn;
|
||||
use Rector\Set\ValueObject\SetList;
|
||||
use function Rector\SymfonyPhpConfig\inline_value_objects;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$services = $containerConfigurator->services();
|
||||
|
||||
$services->set(InferParamFromClassMethodReturnRector::class)
|
||||
->call('configure', [[
|
||||
InferParamFromClassMethodReturnRector::INFER_PARAMS_FROM_CLASS_METHOD_RETURNS => inline_value_objects([
|
||||
new InferParamFromClassMethodReturn(AbstractRector::class, 'refactor', 'getNodeTypes'),
|
||||
]),
|
||||
]]);
|
||||
|
||||
$parameters = $containerConfigurator->parameters();
|
||||
|
||||
$parameters->set(Option::SETS, [
|
||||
|
|
|
@ -117,7 +117,7 @@ CODE_SAMPLE
|
|||
}
|
||||
|
||||
/**
|
||||
* @param Node|Throw_|MethodCall|FuncCall $node
|
||||
* @param Throw_|MethodCall|FuncCall $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
|
|
|
@ -75,7 +75,7 @@ CODE_SAMPLE
|
|||
}
|
||||
|
||||
/**
|
||||
* @param AssignOp|BinaryOp $node
|
||||
* @param Plus|Minus|Mul|Div|AssignPlus|AssignMinus|AssignMul|AssignDiv $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
|
|
|
@ -9,7 +9,7 @@ use PhpParser\Node\Expr\PropertyFetch;
|
|||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
|
||||
use Rector\BetterPhpDocParser\PhpDocNode\Doctrine\Property_\OneToManyTagValueNode;
|
||||
use Rector\Core\PhpParser\Node\Manipulator\AssignManipulator;
|
||||
|
@ -201,7 +201,7 @@ CODE_SAMPLE
|
|||
return $phpDocInfo->hasByType($tagValueNodeClass);
|
||||
}
|
||||
|
||||
private function resolveCollectionSetterAssignType(ClassMethod $classMethod): ?TypeNode
|
||||
private function resolveCollectionSetterAssignType(ClassMethod $classMethod): ?Type
|
||||
{
|
||||
$propertyFetches = $this->assignManipulator->resolveAssignsToLocalPropertyFetches($classMethod);
|
||||
if (count($propertyFetches) !== 1) {
|
||||
|
@ -218,7 +218,7 @@ CODE_SAMPLE
|
|||
return null;
|
||||
}
|
||||
|
||||
return $varTagValueNode->type;
|
||||
return $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType($varTagValueNode->type, $property);
|
||||
}
|
||||
|
||||
private function matchPropertyFetchToClassProperty(PropertyFetch $propertyFetch): ?Property
|
||||
|
|
|
@ -48,6 +48,9 @@ CODE_SAMPLE
|
|||
return [Include_::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Include_ $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
$nop = new Nop();
|
||||
|
|
|
@ -5,9 +5,11 @@ declare(strict_types=1);
|
|||
namespace Rector\Php80\Rector\Class_;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\FunctionLike;
|
||||
use PhpParser\Node\Expr\ArrowFunction;
|
||||
use PhpParser\Node\Expr\Closure;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Function_;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\Core\RectorDefinition\CodeSample;
|
||||
|
@ -65,11 +67,18 @@ CODE_SAMPLE
|
|||
*/
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [Class_::class, ClassMethod::class, Property::class, FunctionLike::class];
|
||||
return [
|
||||
Class_::class,
|
||||
Property::class,
|
||||
ClassMethod::class,
|
||||
Function_::class,
|
||||
Closure::class,
|
||||
ArrowFunction::class,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Class_|ClassMethod|Property $node
|
||||
* @param Class_|Property|ClassMethod|Function_|Closure|ArrowFunction $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
|
|
|
@ -64,7 +64,7 @@ CODE_SAMPLE
|
|||
*/
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [FunctionLike::class];
|
||||
return [ClassMethod::class, Function_::class, Closure::class, ArrowFunction::class];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||
namespace Rector\Restoration\ClassMap;
|
||||
|
||||
use Nette\Loaders\RobotLoader;
|
||||
use Nette\Utils\Arrays;
|
||||
use Symplify\ComposerJsonManipulator\ComposerJsonFactory;
|
||||
|
||||
final class ExistingClassesProvider
|
||||
|
@ -51,19 +52,8 @@ final class ExistingClassesProvider
|
|||
$composerJsonFilePath = getcwd() . '/composer.json';
|
||||
$composerJson = $this->composerJsonFactory->createFromFilePath($composerJsonFilePath);
|
||||
|
||||
$directories = [];
|
||||
|
||||
foreach ($composerJson->getAutoload()['psr-4'] ?? [] as $paths) {
|
||||
if (is_array($paths)) {
|
||||
$directories = array_merge($directories, $paths);
|
||||
} else {
|
||||
$directories[] = $paths;
|
||||
}
|
||||
}
|
||||
|
||||
$classmapPaths = $composerJson->getAutoload()['classmap'] ?? [];
|
||||
|
||||
return array_merge($directories, $classmapPaths);
|
||||
$psr4AndClassmapDirectories = $composerJson->getPsr4AndClassmapDirectories();
|
||||
return Arrays::flatten($psr4AndClassmapDirectories);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,199 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Restoration\Rector\ClassMethod;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Param;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\UnionType;
|
||||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
|
||||
use Rector\Core\Contract\Rector\ConfigurableRectorInterface;
|
||||
use Rector\Core\Exception\ShouldNotHappenException;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\Core\RectorDefinition\ConfiguredCodeSample;
|
||||
use Rector\Core\RectorDefinition\RectorDefinition;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\Restoration\Type\ConstantReturnToParamTypeConverter;
|
||||
use Rector\Restoration\ValueObject\InferParamFromClassMethodReturn;
|
||||
use Rector\TypeDeclaration\TypeInferer\ReturnTypeInferer;
|
||||
use Webmozart\Assert\Assert;
|
||||
|
||||
/**
|
||||
* @sponsor Thanks https://github.com/eonx-com for sponsoring this rule
|
||||
*
|
||||
* @see \Rector\Restoration\Tests\Rector\ClassMethod\InferParamFromClassMethodReturnRector\InferParamFromClassMethodReturnRectorTest
|
||||
*/
|
||||
final class InferParamFromClassMethodReturnRector extends AbstractRector implements ConfigurableRectorInterface
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public const INFER_PARAMS_FROM_CLASS_METHOD_RETURNS = 'infer_params_from_class_method_returns';
|
||||
|
||||
/**
|
||||
* @var InferParamFromClassMethodReturn[]
|
||||
*/
|
||||
private $inferParamFromClassMethodReturn = [];
|
||||
|
||||
/**
|
||||
* @var ReturnTypeInferer
|
||||
*/
|
||||
private $returnTypeInferer;
|
||||
|
||||
/**
|
||||
* @var ConstantReturnToParamTypeConverter
|
||||
*/
|
||||
private $constantReturnToParamTypeConverter;
|
||||
|
||||
public function __construct(
|
||||
ReturnTypeInferer $returnTypeInferer,
|
||||
ConstantReturnToParamTypeConverter $constantReturnToParamTypeConverter
|
||||
) {
|
||||
$this->returnTypeInferer = $returnTypeInferer;
|
||||
$this->constantReturnToParamTypeConverter = $constantReturnToParamTypeConverter;
|
||||
}
|
||||
|
||||
public function getDefinition(): RectorDefinition
|
||||
{
|
||||
return new RectorDefinition('Change @param doc based on another method return type', [
|
||||
new ConfiguredCodeSample(
|
||||
<<<'CODE_SAMPLE'
|
||||
class SomeClass
|
||||
{
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [String_::class];
|
||||
}
|
||||
|
||||
public function process(Node $node)
|
||||
{
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE
|
||||
,
|
||||
<<<'CODE_SAMPLE'
|
||||
class SomeClass
|
||||
{
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [String_::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param String_ $node
|
||||
*/
|
||||
public function process(Node $node)
|
||||
{
|
||||
}
|
||||
}
|
||||
CODE_SAMPLE
|
||||
,
|
||||
[
|
||||
self::INFER_PARAMS_FROM_CLASS_METHOD_RETURNS => [
|
||||
new InferParamFromClassMethodReturn('SomeClass', 'process', 'getNodeTypes'),
|
||||
],
|
||||
]
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [ClassMethod::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClassMethod $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
// must be exactly 1 param
|
||||
if (count($node->params) !== 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$firstParam = $node->params[0];
|
||||
$paramName = $this->getName($firstParam);
|
||||
if ($paramName === null) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
foreach ($this->inferParamFromClassMethodReturn as $inferParamFromClassMethodReturn) {
|
||||
$returnClassMethod = $this->matchReturnClassMethod($node, $inferParamFromClassMethodReturn);
|
||||
if ($returnClassMethod === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$returnType = $this->returnTypeInferer->inferFunctionLike($returnClassMethod);
|
||||
|
||||
/** @var PhpDocInfo|null $currentPhpDocInfo */
|
||||
$currentPhpDocInfo = $node->getAttribute(AttributeKey::PHP_DOC_INFO);
|
||||
if ($currentPhpDocInfo === null) {
|
||||
$currentPhpDocInfo = $this->phpDocInfoFactory->createFromNode($node);
|
||||
}
|
||||
|
||||
$paramType = $this->constantReturnToParamTypeConverter->convert($returnType);
|
||||
if ($paramType === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($this->isParamDocTypeEqualToPhpType($firstParam, $paramType)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$currentPhpDocInfo->changeParamType($paramType, $firstParam, $paramName);
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $configuration
|
||||
*/
|
||||
public function configure(array $configuration): void
|
||||
{
|
||||
$inferParamsFromClassMethodReturns = $configuration[self::INFER_PARAMS_FROM_CLASS_METHOD_RETURNS] ?? [];
|
||||
Assert::allIsInstanceOf($inferParamsFromClassMethodReturns, InferParamFromClassMethodReturn::class);
|
||||
|
||||
$this->inferParamFromClassMethodReturn = $inferParamsFromClassMethodReturns;
|
||||
}
|
||||
|
||||
private function matchReturnClassMethod(
|
||||
ClassMethod $classMethod,
|
||||
InferParamFromClassMethodReturn $inferParamFromClassMethodReturn
|
||||
): ?ClassMethod {
|
||||
if (! $this->isInClassNamed($classMethod, $inferParamFromClassMethodReturn->getClass())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $this->isName($classMethod->name, $inferParamFromClassMethodReturn->getParamMethod())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$class = $classMethod->getAttribute(AttributeKey::CLASS_NODE);
|
||||
if (! $class instanceof Class_) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $class->getMethod($inferParamFromClassMethodReturn->getReturnMethod());
|
||||
}
|
||||
|
||||
private function isParamDocTypeEqualToPhpType(Param $param, Type $paramType): bool
|
||||
{
|
||||
$currentParamType = $this->getObjectType($param);
|
||||
if ($currentParamType instanceof UnionType) {
|
||||
$currentParamType = $currentParamType->getTypes()[0];
|
||||
}
|
||||
|
||||
return $currentParamType->equals($paramType);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Restoration\Type;
|
||||
|
||||
use PHPStan\Type\Constant\ConstantArrayType;
|
||||
use PHPStan\Type\Constant\ConstantStringType;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\UnionType;
|
||||
use Rector\Core\Exception\NotImplementedYetException;
|
||||
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
|
||||
|
||||
final class ConstantReturnToParamTypeConverter
|
||||
{
|
||||
/**
|
||||
* @var TypeFactory
|
||||
*/
|
||||
private $typeFactory;
|
||||
|
||||
public function __construct(TypeFactory $typeFactory)
|
||||
{
|
||||
$this->typeFactory = $typeFactory;
|
||||
}
|
||||
|
||||
public function convert(Type $type): ?Type
|
||||
{
|
||||
if (! $type instanceof ConstantStringType && ! $type instanceof ConstantArrayType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->unwrapConstantTypeToObjectType($type);
|
||||
}
|
||||
|
||||
private function unwrapConstantTypeToObjectType(Type $type): Type
|
||||
{
|
||||
if ($type instanceof ConstantArrayType) {
|
||||
return $this->unwrapConstantTypeToObjectType($type->getItemType());
|
||||
} elseif ($type instanceof ConstantStringType) {
|
||||
return new ObjectType($type->getValue());
|
||||
} elseif ($type instanceof UnionType) {
|
||||
$types = [];
|
||||
foreach ($type->getTypes() as $unionedType) {
|
||||
$types[] = $this->unwrapConstantTypeToObjectType($unionedType);
|
||||
}
|
||||
|
||||
return $this->typeFactory->createMixedPassedOrUnionType($types);
|
||||
}
|
||||
|
||||
throw new NotImplementedYetException();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Restoration\ValueObject;
|
||||
|
||||
final class InferParamFromClassMethodReturn
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $class;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $paramMethod;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $returnMethod;
|
||||
|
||||
public function __construct(string $class, string $paramMethod, string $returnMethod)
|
||||
{
|
||||
$this->class = $class;
|
||||
$this->paramMethod = $paramMethod;
|
||||
$this->returnMethod = $returnMethod;
|
||||
}
|
||||
|
||||
public function getClass(): string
|
||||
{
|
||||
return $this->class;
|
||||
}
|
||||
|
||||
public function getParamMethod(): string
|
||||
{
|
||||
return $this->paramMethod;
|
||||
}
|
||||
|
||||
public function getReturnMethod(): string
|
||||
{
|
||||
return $this->returnMethod;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Restoration\Tests\Rector\ClassMethod\InferParamFromClassMethodReturnRector\Fixture;
|
||||
|
||||
use PhpParser\Node\Scalar\String_;
|
||||
use Rector\Restoration\Tests\Rector\ClassMethod\InferParamFromClassMethodReturnRector\Source\SomeType;
|
||||
|
||||
class SomeClass extends SomeType
|
||||
{
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [String_::class];
|
||||
}
|
||||
|
||||
public function process(\PhpParser\Node $node)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Restoration\Tests\Rector\ClassMethod\InferParamFromClassMethodReturnRector\Fixture;
|
||||
|
||||
use PhpParser\Node\Scalar\String_;
|
||||
use Rector\Restoration\Tests\Rector\ClassMethod\InferParamFromClassMethodReturnRector\Source\SomeType;
|
||||
|
||||
class SomeClass extends SomeType
|
||||
{
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [String_::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \PhpParser\Node\Scalar\String_ $node
|
||||
*/
|
||||
public function process(\PhpParser\Node $node)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Restoration\Tests\Rector\ClassMethod\InferParamFromClassMethodReturnRector\Fixture;
|
||||
|
||||
use Hoa\Protocol\Node\Node;
|
||||
use PhpParser\Node\Expr\AssignOp\Plus;
|
||||
use PhpParser\Node\Expr\BinaryOp\Plus as BinaryPlus;
|
||||
use PhpParser\Node\Scalar\String_;
|
||||
use Rector\Restoration\Tests\Rector\ClassMethod\InferParamFromClassMethodReturnRector\Source\SomeType;
|
||||
|
||||
class SkipAliasAlready extends SomeType
|
||||
{
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [Plus::class, BinaryPlus::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Plus|BinaryPlus $node
|
||||
*/
|
||||
public function process(Node $node)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Restoration\Tests\Rector\ClassMethod\InferParamFromClassMethodReturnRector\Fixture;
|
||||
|
||||
use PhpParser\Node\Scalar\String_;
|
||||
use Rector\Restoration\Tests\Rector\ClassMethod\InferParamFromClassMethodReturnRector\Source\SomeType;
|
||||
|
||||
class SkipCorrect extends SomeType
|
||||
{
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [String_::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param String_ $node
|
||||
*/
|
||||
public function process(\PhpParser\Node $node)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Restoration\Tests\Rector\ClassMethod\InferParamFromClassMethodReturnRector\Fixture;
|
||||
|
||||
use PhpParser\Node\Scalar\String_;
|
||||
use Rector\Restoration\Tests\Rector\ClassMethod\InferParamFromClassMethodReturnRector\Source\SomeType;
|
||||
|
||||
class SkipSameName extends SomeType
|
||||
{
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [String_::class];
|
||||
}
|
||||
|
||||
public function process(String_ $node)
|
||||
{
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Restoration\Tests\Rector\ClassMethod\InferParamFromClassMethodReturnRector\Fixture;
|
||||
|
||||
use PhpParser\Node\Scalar\LNumber;
|
||||
use PhpParser\Node\Scalar\String_;
|
||||
use Rector\Restoration\Tests\Rector\ClassMethod\InferParamFromClassMethodReturnRector\Source\SomeType;
|
||||
|
||||
class UnionTypes extends SomeType
|
||||
{
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [String_::class, LNumber::class];
|
||||
}
|
||||
|
||||
public function process(\PhpParser\Node $node)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Restoration\Tests\Rector\ClassMethod\InferParamFromClassMethodReturnRector\Fixture;
|
||||
|
||||
use PhpParser\Node\Scalar\LNumber;
|
||||
use PhpParser\Node\Scalar\String_;
|
||||
use Rector\Restoration\Tests\Rector\ClassMethod\InferParamFromClassMethodReturnRector\Source\SomeType;
|
||||
|
||||
class UnionTypes extends SomeType
|
||||
{
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [String_::class, LNumber::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param \PhpParser\Node\Scalar\LNumber|\PhpParser\Node\Scalar\String_ $node
|
||||
*/
|
||||
public function process(\PhpParser\Node $node)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Restoration\Tests\Rector\ClassMethod\InferParamFromClassMethodReturnRector;
|
||||
|
||||
use Iterator;
|
||||
use Rector\Core\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
|
||||
final class InferParamFromClassMethodReturnRectorTest extends AbstractRectorTestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideData()
|
||||
*/
|
||||
public function test(SmartFileInfo $fileInfo): void
|
||||
{
|
||||
$this->doTestFileInfo($fileInfo);
|
||||
}
|
||||
|
||||
public function provideData(): Iterator
|
||||
{
|
||||
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
|
||||
}
|
||||
|
||||
protected function provideConfigFileInfo(): ?SmartFileInfo
|
||||
{
|
||||
return new SmartFileInfo(__DIR__ . '/config/configured_rule.php');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Restoration\Tests\Rector\ClassMethod\InferParamFromClassMethodReturnRector\Source;
|
||||
|
||||
abstract class SomeType
|
||||
{
|
||||
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Rector\Restoration\Rector\ClassMethod\InferParamFromClassMethodReturnRector;
|
||||
use Rector\Restoration\Tests\Rector\ClassMethod\InferParamFromClassMethodReturnRector\Source\SomeType;
|
||||
use Rector\Restoration\ValueObject\InferParamFromClassMethodReturn;
|
||||
use function Rector\SymfonyPhpConfig\inline_value_objects;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$services = $containerConfigurator->services();
|
||||
|
||||
$services->set(InferParamFromClassMethodReturnRector::class)
|
||||
->call('configure', [[
|
||||
InferParamFromClassMethodReturnRector::INFER_PARAMS_FROM_CLASS_METHOD_RETURNS => inline_value_objects([
|
||||
new InferParamFromClassMethodReturn(SomeType::class, 'process', 'getNodeTypes'),
|
||||
]),
|
||||
]]);
|
||||
};
|
|
@ -1,51 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddArrayParamDocTypeRector\Fixture;
|
||||
|
||||
use Message;
|
||||
|
||||
class ParamDouble
|
||||
{
|
||||
/**
|
||||
* @var array $meta
|
||||
*/
|
||||
private $meta = [];
|
||||
|
||||
/**
|
||||
* @param array $meta
|
||||
*
|
||||
* @return Message
|
||||
*/
|
||||
public function setMeta(array $meta)
|
||||
{
|
||||
$this->meta = $meta;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddArrayParamDocTypeRector\Fixture;
|
||||
|
||||
use Message;
|
||||
|
||||
class ParamDouble
|
||||
{
|
||||
/**
|
||||
* @var array $meta
|
||||
*/
|
||||
private $meta = [];
|
||||
|
||||
/**
|
||||
* @param mixed[] $meta
|
||||
*
|
||||
* @return Message
|
||||
*/
|
||||
public function setMeta(array $meta)
|
||||
{
|
||||
$this->meta = $meta;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -196,7 +196,12 @@ trait NameResolverTrait
|
|||
|
||||
protected function isInClassNamed(Node $node, string $desiredClassName): bool
|
||||
{
|
||||
return $node->getAttribute(AttributeKey::CLASS_NAME) === $desiredClassName;
|
||||
$className = $node->getAttribute(AttributeKey::CLASS_NAME);
|
||||
if ($className === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return is_a($className, $desiredClassName, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -172,6 +172,9 @@ abstract class AbstractGenericRectorTestCase extends AbstractKernelTestCase impl
|
|||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use config instead, just to narrow 2 ways to add configured config to just 1. Now
|
||||
* with PHP its easy pick.
|
||||
*
|
||||
* @return mixed[]
|
||||
*/
|
||||
protected function getRectorsWithConfiguration(): array
|
||||
|
|
Loading…
Reference in New Issue
Block a user