[Php 8] Enable RenamePropertyToMatchTypeRector (#168)

Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: Tomas Votruba <tomas.vot@gmail.com>
This commit is contained in:
Abdul Malik Ikhsan 2021-06-19 02:18:23 +07:00 committed by GitHub
parent c251806319
commit 24384c10ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 381 additions and 135 deletions

View File

@ -9,7 +9,6 @@ use Rector\CodingStyle\ValueObject\PreferenceSelfThis;
use Rector\Core\Configuration\Option;
use Rector\Core\Rector\AbstractRector;
use Rector\DeadCode\Rector\Stmt\RemoveUnreachableStatementRector;
use Rector\Naming\Rector\Class_\RenamePropertyToMatchTypeRector;
use Rector\Nette\Set\NetteSetList;
use Rector\Php55\Rector\String_\StringClassNameToClassConstantRector;
use Rector\Php74\Rector\MethodCall\ChangeReflectionTypeToStringToGetNameRector;
@ -86,8 +85,6 @@ return static function (ContainerConfigurator $containerConfigurator): void {
SplitStringClassConstantToClassConstFetchRector::class,
// to be enabled when all rules for php 8 syntax applied
RenamePropertyToMatchTypeRector::class,
RemoveUnreachableStatementRector::class => [
__DIR__ . '/rules/Php70/Rector/FuncCall/MultiDirnameRector.php',
],

View File

@ -0,0 +1,18 @@
<?php
namespace Rector\Tests\Naming\Rector\Class_\RenamePropertyToMatchTypeRector\Fixture;
use Rector\Symfony\DataProvider\ServiceMapProvider;
final class KeepPromotedPropertyPrefix
{
public function __construct(
private ServiceMapProvider $applicationServiceMapProvider,
) {
}
public function getApplicationServiceMapProvider(): ServiceMapProvider
{
return $this->applicationServiceMapProvider;
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace Rector\Tests\Naming\Rector\Class_\RenamePropertyToMatchTypeRector\Fixture;
use PhpParser\Node;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
final class SkipChangeuplicatedTypePrefix
{
/**
* @param Name|Identifier $nameNode
*/
public function __construct(
private Node $nameNode,
private Node $parentNode
) {
}
/**
* @return Name|Identifier
*/
public function getNameNode(): Node
{
return $this->nameNode;
}
public function getParentNode(): Node
{
return $this->parentNode;
}
}

View File

@ -4,7 +4,7 @@ namespace Rector\Tests\Naming\Rector\Class_\RenamePropertyToMatchTypeRector\Fixt
use Rector\Tests\Naming\Rector\Class_\RenamePropertyToMatchTypeRector\Source\EliteManager;
class SomeClass
final class SkipChangeDuplicateTypeSuffix
{
/**
* @var EliteManager

View File

@ -0,0 +1,14 @@
<?php
namespace Rector\Tests\Naming\Rector\Class_\RenamePropertyToMatchTypeRector\Fixture;
use PhpParser\Node;
final class SkipMultipleSameTypes
{
public function __construct(
private Node $variable,
private Node $assign
) {
}
}

View File

@ -0,0 +1,59 @@
<?php
namespace Rector\Tests\Naming\Rector\Class_\RenamePropertyToMatchTypeRector\FixturePhp80;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
final class NullableExprType
{
/**
* @param FuncCall|StaticCall|MethodCall|null $call
*/
public function __construct(
private ?Expr $call
) {
}
/**
* @return FuncCall|StaticCall|MethodCall|null
*/
public function getCall(): ?Expr
{
return $this->call;
}
}
?>
-----
<?php
namespace Rector\Tests\Naming\Rector\Class_\RenamePropertyToMatchTypeRector\FixturePhp80;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
final class NullableExprType
{
/**
* @param FuncCall|StaticCall|MethodCall|null $expr
*/
public function __construct(
private ?Expr $expr
) {
}
/**
* @return FuncCall|StaticCall|MethodCall|null
*/
public function getCall(): ?Expr
{
return $this->expr;
}
}
?>

View File

@ -2,31 +2,13 @@
namespace Rector\Tests\Naming\Rector\Class_\RenamePropertyToMatchTypeRector\FixturePhp80;
use PhpParser\Node;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use Rector\Core\NodeAnalyzer\ClassAnalyzer;
final class NameAndParent
final class SkipChangeDuplicateType
{
/**
* @param Name|Identifier $nameNode
*/
public function __construct(
private Node $nameNode,
private Node $parentNode
private ClassAnalyzer $classAnalyzer1,
private ClassAnalyzer $classAnalyzer2
) {
}
/**
* @return Name|Identifier
*/
public function getNameNode(): Node
{
return $this->nameNode;
}
public function getParentNode(): Node
{
return $this->parentNode;
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace Rector\Tests\Naming\Rector\Class_\RenamePropertyToMatchTypeRector\FixturePhp80;
use PHPStan\Type\Type;
final class SkipPHPStanTypeConstruct
{
public function __construct(
private Type $returnType
) {
}
public function getReturnType(): Type
{
return $this->returnType;
}
}

View File

@ -0,0 +1,59 @@
<?php
namespace Rector\Tests\Naming\Rector\Class_\RenamePropertyToMatchTypeRector\FixturePhp80;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
final class UnionTypesToExpr
{
/**
* @param FuncCall|StaticCall|MethodCall $call
*/
public function __construct(
private Expr $call
) {
}
/**
* @return FuncCall|StaticCall|MethodCall
*/
public function getCall(): Expr
{
return $this->call;
}
}
?>
-----
<?php
namespace Rector\Tests\Naming\Rector\Class_\RenamePropertyToMatchTypeRector\FixturePhp80;
use PhpParser\Node\Expr;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
final class UnionTypesToExpr
{
/**
* @param FuncCall|StaticCall|MethodCall $expr
*/
public function __construct(
private Expr $expr
) {
}
/**
* @return FuncCall|StaticCall|MethodCall
*/
public function getCall(): Expr
{
return $this->expr;
}
}
?>

View File

@ -0,0 +1,161 @@
<?php
declare(strict_types=1);
namespace Rector\Naming\PropertyRenamer;
use PhpParser\Node\Param;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
use Rector\Core\Php\PhpVersionProvider;
use Rector\Core\ValueObject\MethodName;
use Rector\Core\ValueObject\PhpVersionFeature;
use Rector\Naming\ExpectedNameResolver\MatchParamTypeExpectedNameResolver;
use Rector\Naming\ParamRenamer\ParamRenamer;
use Rector\Naming\ValueObject\ParamRename;
use Rector\Naming\ValueObjectFactory\ParamRenameFactory;
use Rector\NodeNameResolver\NodeNameResolver;
final class PropertyPromotionRenamer
{
public function __construct(
private PhpVersionProvider $phpVersionProvider,
private MatchParamTypeExpectedNameResolver $matchParamTypeExpectedNameResolver,
private ParamRenameFactory $paramRenameFactory,
private PhpDocInfoFactory $phpDocInfoFactory,
private ParamRenamer $paramRenamer,
private PropertyFetchRenamer $propertyFetchRenamer,
private NodeNameResolver $nodeNameResolver
) {
}
public function renamePropertyPromotion(ClassLike $classLike): void
{
if (! $this->phpVersionProvider->isAtLeastPhpVersion(PhpVersionFeature::PROPERTY_PROMOTION)) {
return;
}
$constructClassMethod = $classLike->getMethod(MethodName::CONSTRUCT);
if (! $constructClassMethod instanceof ClassMethod) {
return;
}
// resolve possible and existing param names
$blockingParamNames = $this->resolveBlockingParamNames($constructClassMethod);
foreach ($constructClassMethod->params as $param) {
if ($param->flags === 0) {
continue;
}
// promoted property
$desiredPropertyName = $this->matchParamTypeExpectedNameResolver->resolve($param);
if ($desiredPropertyName === null) {
continue;
}
if (in_array($desiredPropertyName, $blockingParamNames, true)) {
continue;
}
$currentParamName = $this->nodeNameResolver->getName($param);
if ($this->isNameSuffixed($currentParamName, $desiredPropertyName)) {
continue;
}
$this->renameParamVarName($classLike, $constructClassMethod, $desiredPropertyName, $param);
}
}
private function renameParamVarName(
ClassLike $classLike,
ClassMethod $classMethod,
string $desiredPropertyName,
Param $param
): void {
$classMethodPhpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod);
$currentParamName = $this->nodeNameResolver->getName($param);
$this->propertyFetchRenamer->renamePropertyFetchesInClass(
$classLike,
$currentParamName,
$desiredPropertyName
);
/** @var string $paramVarName */
$paramVarName = $param->var->name;
$this->renameParamDoc($classMethodPhpDocInfo, $param, $paramVarName, $desiredPropertyName);
$param->var->name = $desiredPropertyName;
}
private function renameParamDoc(
PhpDocInfo $phpDocInfo,
Param $param,
string $paramVarName,
string $desiredPropertyName
): void {
$paramTagValueNode = $phpDocInfo->getParamTagValueNodeByName($paramVarName);
if (! $paramTagValueNode instanceof ParamTagValueNode) {
return;
}
$paramRename = $this->paramRenameFactory->createFromResolvedExpectedName($param, $desiredPropertyName);
if (! $paramRename instanceof ParamRename) {
return;
}
$this->paramRenamer->rename($paramRename);
}
/**
* Sometimes the bare type is not enough.
* This allows prefixing type in variable names, e.g. "Type $firstType"
*/
private function isNameSuffixed(string $currentParamName, string $desiredPropertyName): bool
{
$currentNameLowercased = strtolower($currentParamName);
$expectedNameLowercased = strtolower($desiredPropertyName);
return str_ends_with($currentNameLowercased, $expectedNameLowercased);
}
/**
* @return int[]|string[]
*/
private function resolveBlockingParamNames(ClassMethod $classMethod): array
{
$futureParamNames = [];
foreach ($classMethod->params as $param) {
$futureParamName = $this->matchParamTypeExpectedNameResolver->resolve($param);
if ($futureParamName === null) {
continue;
}
$futureParamNames[] = $futureParamName;
}
// remove null values
$futureParamNames = array_filter($futureParamNames);
if ($futureParamNames === []) {
return [];
}
// resolve duplicated names
$blockingParamNames = [];
$valuesToCount = array_count_values($futureParamNames);
foreach ($valuesToCount as $value => $count) {
if ($count < 2) {
continue;
}
$blockingParamNames[] = $value;
}
return $blockingParamNames;
}
}

View File

@ -8,22 +8,13 @@ use PhpParser\Node;
use PhpParser\Node\Param;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Interface_;
use PhpParser\Node\Stmt\Property;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\ValueObject\MethodName;
use Rector\Core\ValueObject\PhpVersionFeature;
use Rector\Naming\ExpectedNameResolver\MatchParamTypeExpectedNameResolver;
use Rector\Naming\ExpectedNameResolver\MatchPropertyTypeExpectedNameResolver;
use Rector\Naming\ParamRenamer\ParamRenamer;
use Rector\Naming\PropertyRenamer\MatchTypePropertyRenamer;
use Rector\Naming\PropertyRenamer\PropertyFetchRenamer;
use Rector\Naming\ValueObject\ParamRename;
use Rector\Naming\PropertyRenamer\PropertyPromotionRenamer;
use Rector\Naming\ValueObject\PropertyRename;
use Rector\Naming\ValueObjectFactory\ParamRenameFactory;
use Rector\Naming\ValueObjectFactory\PropertyRenameFactory;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
@ -39,10 +30,7 @@ final class RenamePropertyToMatchTypeRector extends AbstractRector
private MatchTypePropertyRenamer $matchTypePropertyRenamer,
private PropertyRenameFactory $propertyRenameFactory,
private MatchPropertyTypeExpectedNameResolver $matchPropertyTypeExpectedNameResolver,
private MatchParamTypeExpectedNameResolver $matchParamTypeExpectedNameResolver,
private PropertyFetchRenamer $propertyFetchRenamer,
private ParamRenameFactory $paramRenameFactory,
private ParamRenamer $paramRenamer
private PropertyPromotionRenamer $propertyPromotionRenamer,
) {
}
@ -66,7 +54,7 @@ class SomeClass
}
}
CODE_SAMPLE
,
,
<<<'CODE_SAMPLE'
class SomeClass
{
@ -100,7 +88,7 @@ CODE_SAMPLE
public function refactor(Node $node): ?Node
{
$this->refactorClassProperties($node);
$this->renamePropertyPromotion($node);
$this->propertyPromotionRenamer->renamePropertyPromotion($node);
if (! $this->hasChanged) {
return null;
@ -130,87 +118,4 @@ CODE_SAMPLE
$this->hasChanged = true;
}
}
private function renamePropertyPromotion(ClassLike $classLike): void
{
if (! $this->isAtLeastPhpVersion(PhpVersionFeature::PROPERTY_PROMOTION)) {
return;
}
$constructClassMethod = $classLike->getMethod(MethodName::CONSTRUCT);
if (! $constructClassMethod instanceof ClassMethod) {
return;
}
$desiredPropertyNames = [];
foreach ($constructClassMethod->params as $key => $param) {
if ($param->flags === 0) {
continue;
}
// promoted property
$desiredPropertyName = $this->matchParamTypeExpectedNameResolver->resolve($param);
if ($desiredPropertyName === null) {
continue;
}
if (in_array($desiredPropertyName, $desiredPropertyNames, true)) {
return;
}
$desiredPropertyNames[$key] = $desiredPropertyName;
}
$this->renameParamVarName($classLike, $constructClassMethod, $desiredPropertyNames);
}
/**
* @param string[] $desiredPropertyNames
*/
private function renameParamVarName(
ClassLike $classLike,
ClassMethod $constructClassMethod,
array $desiredPropertyNames
): void {
$keys = array_keys($desiredPropertyNames);
$params = $constructClassMethod->params;
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($constructClassMethod);
foreach ($params as $key => $param) {
if (in_array($key, $keys, true)) {
$currentName = $this->getName($param);
$desiredPropertyName = $desiredPropertyNames[$key];
$this->propertyFetchRenamer->renamePropertyFetchesInClass(
$classLike,
$currentName,
$desiredPropertyName
);
/** @var string $paramVarName */
$paramVarName = $param->var->name;
$this->renameParamDoc($phpDocInfo, $param, $paramVarName, $desiredPropertyName);
$param->var->name = $desiredPropertyName;
}
}
}
private function renameParamDoc(
PhpDocInfo $phpDocInfo,
Param $param,
string $paramVarName,
string $desiredPropertyName
): void {
$paramTagValueNode = $phpDocInfo->getParamTagValueNodeByName($paramVarName);
if (! $paramTagValueNode instanceof ParamTagValueNode) {
return;
}
$paramRename = $this->paramRenameFactory->createFromResolvedExpectedName($param, $desiredPropertyName);
if (! $paramRename instanceof ParamRename) {
return;
}
$this->paramRenamer->rename($paramRename);
}
}

View File

@ -26,7 +26,7 @@ final class RenameForeachValueVariableToMatchMethodCallReturnTypeRector extends
private ExpectedNameResolver $expectedNameResolver,
private NamingConventionAnalyzer $namingConventionAnalyzer,
private VariableRenamer $variableRenamer,
private ForeachMatcher $varValueAndCallForeachMatcher
private ForeachMatcher $foreachMatcher
) {
}
@ -80,7 +80,7 @@ CODE_SAMPLE
*/
public function refactor(Node $node): ?Node
{
$variableAndCallForeach = $this->varValueAndCallForeachMatcher->match($node);
$variableAndCallForeach = $this->foreachMatcher->match($node);
if (! $variableAndCallForeach instanceof VariableAndCallForeach) {
return null;
}

View File

@ -18,12 +18,12 @@ use PhpParser\Node\Stmt\Function_;
final class VariableAndCallAssign
{
/**
* @param FuncCall|StaticCall|MethodCall $call
* @param FuncCall|StaticCall|MethodCall $expr
* @param ClassMethod|Function_|Closure $functionLike
*/
public function __construct(
private Variable $variable,
private Expr $call,
private Expr $expr,
private Assign $assign,
private string $variableName,
private FunctionLike $functionLike
@ -37,7 +37,7 @@ final class VariableAndCallAssign
public function getCall(): FuncCall | MethodCall | StaticCall
{
return $this->call;
return $this->expr;
}
public function getVariableName(): string

View File

@ -17,12 +17,12 @@ use PhpParser\Node\Stmt\Function_;
final class VariableAndCallForeach
{
/**
* @param FuncCall|StaticCall|MethodCall $call
* @param FuncCall|StaticCall|MethodCall $expr
* @param ClassMethod|Function_|Closure $functionLike
*/
public function __construct(
private Variable $variable,
private Expr $call,
private Expr $expr,
private string $variableName,
private FunctionLike $functionLike
) {
@ -35,7 +35,7 @@ final class VariableAndCallForeach
public function getCall(): FuncCall | MethodCall | StaticCall
{
return $this->call;
return $this->expr;
}
public function getVariableName(): string

View File

@ -39,6 +39,7 @@ final class ParamRenameFactory
if ($currentName === null) {
return null;
}
return new ParamRename($currentName, $expectedName, $param, $param->var, $functionLike);
}
}

View File

@ -30,7 +30,7 @@ use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
final class ClassPropertyAssignToConstructorPromotionRector extends AbstractRector
{
public function __construct(
private PromotedPropertyCandidateResolver $promotedPropertyResolver,
private PromotedPropertyCandidateResolver $promotedPropertyCandidateResolver,
private VariableRenamer $variableRenamer,
private VarTagRemover $varTagRemover
) {
@ -84,7 +84,7 @@ CODE_SAMPLE
return null;
}
$promotionCandidates = $this->promotedPropertyResolver->resolveFromClass($node);
$promotionCandidates = $this->promotedPropertyCandidateResolver->resolveFromClass($node);
if ($promotionCandidates === []) {
return null;
}