[Restoration] Add InferParamFromClassMethodReturnRector (#4471)

Co-authored-by: rector-bot <tomas@getrector.org>
This commit is contained in:
Tomas Votruba 2020-10-21 23:06:24 +02:00 committed by GitHub
parent 10ae0d687a
commit 6c51f3fb4f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 642 additions and 103 deletions

View File

@ -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)

View File

@ -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);
}

View File

@ -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);

View File

@ -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 = [];

View File

@ -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
{

View File

@ -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#'

View File

@ -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, [

View File

@ -117,7 +117,7 @@ CODE_SAMPLE
}
/**
* @param Node|Throw_|MethodCall|FuncCall $node
* @param Throw_|MethodCall|FuncCall $node
*/
public function refactor(Node $node): ?Node
{

View File

@ -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
{

View File

@ -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

View File

@ -48,6 +48,9 @@ CODE_SAMPLE
return [Include_::class];
}
/**
* @param Include_ $node
*/
public function refactor(Node $node): ?Node
{
$nop = new Nop();

View File

@ -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
{

View File

@ -64,7 +64,7 @@ CODE_SAMPLE
*/
public function getNodeTypes(): array
{
return [FunctionLike::class];
return [ClassMethod::class, Function_::class, Closure::class, ArrowFunction::class];
}
/**

View File

@ -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);
}
/**

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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)
{
}
}
?>

View File

@ -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)
{
}
}

View File

@ -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)
{
}
}

View File

@ -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)
{
}
}

View File

@ -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)
{
}
}
?>

View File

@ -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');
}
}

View File

@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Rector\Restoration\Tests\Rector\ClassMethod\InferParamFromClassMethodReturnRector\Source;
abstract class SomeType
{
}

View File

@ -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'),
]),
]]);
};

View File

@ -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;
}
}
?>

View File

@ -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);
}
/**

View File

@ -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