[PHP 8.0] Add property support to ConstantListClassToEnumRector (#2422)

* add property enum support

* fixup! add property enum support
This commit is contained in:
Tomas Votruba 2022-06-03 09:56:38 +02:00 committed by GitHub
parent 7722a57d80
commit 0df2351b89
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 124 additions and 22 deletions

View File

@ -0,0 +1,28 @@
<?php
namespace Rector\Tests\Php80\Rector\Class_\ConstantListClassToEnumRector\Fixture;
use Rector\Tests\Php80\Rector\Class_\ConstantListClassToEnumRector\Source\Gear;
final class EnumTypeProperty
{
/**
* @var Gear::* $gear
*/
private $gear;
}
?>
-----
<?php
namespace Rector\Tests\Php80\Rector\Class_\ConstantListClassToEnumRector\Fixture;
use Rector\Tests\Php80\Rector\Class_\ConstantListClassToEnumRector\Source\Gear;
final class EnumTypeProperty
{
private \Rector\Tests\Php80\Rector\Class_\ConstantListClassToEnumRector\Source\Gear $gear;
}
?>

View File

@ -7,12 +7,14 @@ namespace Rector\Php80\NodeAnalyzer;
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstFetchNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
use PHPStan\PhpDocParser\Ast\Type\ConstTypeNode;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\Reflection\ParameterReflection;
use PHPStan\Reflection\ReflectionProvider;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\BetterPhpDocParser\ValueObject\PhpDocAttributeKey;
use Rector\Php80\ValueObject\ClassNameAndTagValueNode;
/**
* Detects enum-like params, e.g.
@ -25,8 +27,10 @@ final class EnumParamAnalyzer
) {
}
public function matchParameterClassName(ParameterReflection $parameterReflection, PhpDocInfo $phpDocInfo): ?string
{
public function matchParameterClassName(
ParameterReflection $parameterReflection,
PhpDocInfo $phpDocInfo
): ?ClassNameAndTagValueNode {
$paramTagValueNode = $phpDocInfo->getParamTagValueByName($parameterReflection->getName());
if (! $paramTagValueNode instanceof ParamTagValueNode) {
return null;
@ -41,17 +45,37 @@ final class EnumParamAnalyzer
return null;
}
return $className;
return new ClassNameAndTagValueNode($className, $paramTagValueNode);
}
public function matchReturnClassName(PhpDocInfo $phpDocInfo): ?string
public function matchReturnClassName(PhpDocInfo $phpDocInfo): ?ClassNameAndTagValueNode
{
$returnTagValueNode = $phpDocInfo->getReturnTagValue();
if (! $returnTagValueNode instanceof ReturnTagValueNode) {
return null;
}
return $this->resolveClassFromConstType($returnTagValueNode->type);
$className = $this->resolveClassFromConstType($returnTagValueNode->type);
if (! is_string($className)) {
return null;
}
return new ClassNameAndTagValueNode($className, $returnTagValueNode);
}
public function matchPropertyClassName(PhpDocInfo $phpDocInfo): ?ClassNameAndTagValueNode
{
$varTagValueNode = $phpDocInfo->getVarTagValueNode();
if (! $varTagValueNode instanceof VarTagValueNode) {
return null;
}
$className = $this->resolveClassFromConstType($varTagValueNode->type);
if (! is_string($className)) {
return null;
}
return new ClassNameAndTagValueNode($className, $varTagValueNode);
}
private function resolveClassFromConstType(TypeNode $typeNode): ?string

View File

@ -9,8 +9,7 @@ use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Param;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
use PhpParser\Node\Stmt\Property;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Reflection\ParametersAcceptorSelector;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
@ -19,6 +18,7 @@ use Rector\Core\Rector\AbstractRector;
use Rector\Core\Reflection\ReflectionResolver;
use Rector\Php80\NodeAnalyzer\EnumConstListClassDetector;
use Rector\Php80\NodeAnalyzer\EnumParamAnalyzer;
use Rector\Php80\ValueObject\ClassNameAndTagValueNode;
use Rector\Php81\NodeFactory\EnumFactory;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
@ -68,11 +68,11 @@ CODE_SAMPLE
*/
public function getNodeTypes(): array
{
return [Class_::class, ClassMethod::class];
return [Class_::class, ClassMethod::class, Property::class];
}
/**
* @param Class_|ClassMethod $node
* @param Class_|ClassMethod|Property $node
*/
public function refactor(Node $node): ?Node
{
@ -84,7 +84,11 @@ CODE_SAMPLE
return $this->enumFactory->createFromClass($node);
}
return $this->refactorClassMethod($node);
if ($node instanceof ClassMethod) {
return $this->refactorClassMethod($node);
}
return $this->refactorProperty($node);
}
private function refactorClassMethod(ClassMethod $classMethod): ?ClassMethod
@ -137,8 +141,11 @@ CODE_SAMPLE
$parametersAcceptor = ParametersAcceptorSelector::selectSingle($methodReflection->getVariants());
foreach ($parametersAcceptor->getParameters() as $parameterReflection) {
$enumLikeClass = $this->enumParamAnalyzer->matchParameterClassName($parameterReflection, $phpDocInfo);
if ($enumLikeClass === null) {
$classNameAndTagValueNode = $this->enumParamAnalyzer->matchParameterClassName(
$parameterReflection,
$phpDocInfo
);
if (! $classNameAndTagValueNode instanceof ClassNameAndTagValueNode) {
continue;
}
@ -148,12 +155,10 @@ CODE_SAMPLE
}
// change and remove
$param->type = new FullyQualified($enumLikeClass);
$param->type = new FullyQualified($classNameAndTagValueNode->getEnumClass());
$hasNodeChanged = true;
/** @var ParamTagValueNode $paramTagValueNode */
$paramTagValueNode = $phpDocInfo->getParamTagValueByName($parameterReflection->getName());
$this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $paramTagValueNode);
$this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $classNameAndTagValueNode->getTagValueNode());
}
return $hasNodeChanged;
@ -161,17 +166,34 @@ CODE_SAMPLE
private function refactorReturn(PhpDocInfo $phpDocInfo, ClassMethod $classMethod): bool
{
$returnType = $this->enumParamAnalyzer->matchReturnClassName($phpDocInfo);
if ($returnType === null) {
$classNameAndTagValueNode = $this->enumParamAnalyzer->matchReturnClassName($phpDocInfo);
if (! $classNameAndTagValueNode instanceof ClassNameAndTagValueNode) {
return false;
}
$classMethod->returnType = new FullyQualified($returnType);
$classMethod->returnType = new FullyQualified($classNameAndTagValueNode->getEnumClass());
/** @var ReturnTagValueNode $returnTagValueNode */
$returnTagValueNode = $phpDocInfo->getReturnTagValue();
$this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $returnTagValueNode);
$this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $classNameAndTagValueNode->getTagValueNode());
return true;
}
private function refactorProperty(Property $property): ?Property
{
$phpDocInfo = $this->phpDocInfoFactory->createFromNode($property);
if (! $phpDocInfo instanceof PhpDocInfo) {
return null;
}
$classNameAndTagValueNode = $this->enumParamAnalyzer->matchPropertyClassName($phpDocInfo);
if (! $classNameAndTagValueNode instanceof ClassNameAndTagValueNode) {
return null;
}
$property->type = new FullyQualified($classNameAndTagValueNode->getEnumClass());
$this->phpDocTagRemover->removeTagValueFromNode($phpDocInfo, $classNameAndTagValueNode->getTagValueNode());
return $property;
}
}

View File

@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace Rector\Php80\ValueObject;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
final class ClassNameAndTagValueNode
{
public function __construct(
private readonly string $enumClass,
private readonly ParamTagValueNode|ReturnTagValueNode|VarTagValueNode $tagValueNode
) {
}
public function getEnumClass(): string
{
return $this->enumClass;
}
public function getTagValueNode(): ParamTagValueNode|ReturnTagValueNode|VarTagValueNode
{
return $this->tagValueNode;
}
}