mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-26 04:42:36 +00:00
[Naming] Decopule ConflictingNameResolver and ExpectedNameResolver
This commit is contained in:
parent
a8ea0fb9a7
commit
012fe62736
|
@ -251,4 +251,4 @@ parameters:
|
|||
# mostly strings in tests
|
||||
- '#Class (.*?) should be written with \:\:class notation, string found#'
|
||||
- '#Parameter \#2 \$key of method Rector\\BetterPhpDocParser\\PhpDocNode\\AbstractTagValueNode\:\:printArrayItem\(\) expects string\|null, int\|string given#'
|
||||
- '#Method Rector\\Naming\\PropertyNaming\:\:resolveShortClassName\(\) should return string but returns string\|null#'
|
||||
- '#Method Rector\\Naming\\Naming\\PropertyNaming\:\:resolveShortClassName\(\) should return string but returns string\|null#'
|
||||
|
|
77
rules/naming/src/Naming/ConflictingNameResolver.php
Normal file
77
rules/naming/src/Naming/ConflictingNameResolver.php
Normal file
|
@ -0,0 +1,77 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Naming\Naming;
|
||||
|
||||
use PhpParser\Node\Stmt\ClassLike;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
|
||||
final class ConflictingNameResolver
|
||||
{
|
||||
/**
|
||||
* @var ExpectedNameResolver
|
||||
*/
|
||||
private $expectedNameResolver;
|
||||
|
||||
public function __construct(ExpectedNameResolver $expectedNameResolver)
|
||||
{
|
||||
$this->expectedNameResolver = $expectedNameResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function resolveConflictingPropertyNames(ClassLike $classLike): array
|
||||
{
|
||||
$expectedNames = [];
|
||||
foreach ($classLike->getProperties() as $property) {
|
||||
$expectedName = $this->expectedNameResolver->resolveForProperty($property);
|
||||
if ($expectedName === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$expectedNames[] = $expectedName;
|
||||
}
|
||||
|
||||
return $this->filterConflictingNames($expectedNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function resolveConflictingVariableNames(ClassMethod $classMethod): array
|
||||
{
|
||||
$expectedNames = [];
|
||||
foreach ($classMethod->params as $param) {
|
||||
$expectedName = $this->expectedNameResolver->resolveForParam($param);
|
||||
if ($expectedName === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$expectedNames[] = $expectedName;
|
||||
}
|
||||
|
||||
return $this->filterConflictingNames($expectedNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $expectedNames
|
||||
* @return string[]
|
||||
*/
|
||||
private function filterConflictingNames(array $expectedNames): array
|
||||
{
|
||||
$expectedNamesToCount = array_count_values($expectedNames);
|
||||
|
||||
$conflictingExpectedNames = [];
|
||||
foreach ($expectedNamesToCount as $expectedName => $count) {
|
||||
if ($count < 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$conflictingExpectedNames[] = $expectedName;
|
||||
}
|
||||
|
||||
return $conflictingExpectedNames;
|
||||
}
|
||||
}
|
91
rules/naming/src/Naming/ExpectedNameResolver.php
Normal file
91
rules/naming/src/Naming/ExpectedNameResolver.php
Normal file
|
@ -0,0 +1,91 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Naming\Naming;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use PhpParser\Node\Param;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\StaticTypeMapper\StaticTypeMapper;
|
||||
|
||||
final class ExpectedNameResolver
|
||||
{
|
||||
/**
|
||||
* @var NodeNameResolver
|
||||
*/
|
||||
private $nodeNameResolver;
|
||||
|
||||
/**
|
||||
* @var PropertyNaming
|
||||
*/
|
||||
private $propertyNaming;
|
||||
|
||||
/**
|
||||
* @var StaticTypeMapper
|
||||
*/
|
||||
private $staticTypeMapper;
|
||||
|
||||
public function __construct(
|
||||
NodeNameResolver $nodeNameResolver,
|
||||
PropertyNaming $propertyNaming,
|
||||
StaticTypeMapper $staticTypeMapper
|
||||
) {
|
||||
$this->nodeNameResolver = $nodeNameResolver;
|
||||
$this->propertyNaming = $propertyNaming;
|
||||
$this->staticTypeMapper = $staticTypeMapper;
|
||||
}
|
||||
|
||||
public function resolveForProperty(Property $property): ?string
|
||||
{
|
||||
$currentName = $this->nodeNameResolver->getName($property);
|
||||
|
||||
/** @var PhpDocInfo|null $phpDocInfo */
|
||||
$phpDocInfo = $property->getAttribute(AttributeKey::PHP_DOC_INFO);
|
||||
$expectedName = $this->propertyNaming->getExpectedNameFromType($phpDocInfo->getVarType());
|
||||
|
||||
if ($expectedName === $currentName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $expectedName;
|
||||
}
|
||||
|
||||
public function resolveForParam(Param $param): ?string
|
||||
{
|
||||
// nothing to verify
|
||||
if ($param->type === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$staticType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($param->type);
|
||||
$expectedName = $this->propertyNaming->getExpectedNameFromType($staticType);
|
||||
if ($expectedName === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @var string $currentName */
|
||||
$currentName = $this->nodeNameResolver->getName($param->var);
|
||||
if ($currentName === $expectedName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->endsWith($currentName, $expectedName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $expectedName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends with ucname
|
||||
* Starts with adjective, e.g. (Post $firstPost, Post $secondPost)
|
||||
*/
|
||||
private function endsWith(string $currentName, string $expectedName): bool
|
||||
{
|
||||
return (bool) Strings::match($currentName, '#\w+' . lcfirst($expectedName) . '#');
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Naming;
|
||||
namespace Rector\Naming\Naming;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use PHPStan\Type\StaticType;
|
|
@ -4,7 +4,6 @@ declare(strict_types=1);
|
|||
|
||||
namespace Rector\Naming\Rector\Class_;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\PropertyFetch;
|
||||
use PhpParser\Node\Identifier;
|
||||
|
@ -15,12 +14,11 @@ use PhpParser\Node\Stmt\ClassMethod;
|
|||
use PhpParser\Node\Stmt\Interface_;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use PhpParser\Node\VarLikeIdentifier;
|
||||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\Core\RectorDefinition\CodeSample;
|
||||
use Rector\Core\RectorDefinition\RectorDefinition;
|
||||
use Rector\Naming\PropertyNaming;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\Naming\Naming\ConflictingNameResolver;
|
||||
use Rector\Naming\Naming\ExpectedNameResolver;
|
||||
|
||||
/**
|
||||
* @see \Rector\Naming\Tests\Rector\Class_\RenamePropertyToMatchTypeRector\RenamePropertyToMatchTypeRectorTest
|
||||
|
@ -28,13 +26,21 @@ use Rector\NodeTypeResolver\Node\AttributeKey;
|
|||
final class RenamePropertyToMatchTypeRector extends AbstractRector
|
||||
{
|
||||
/**
|
||||
* @var PropertyNaming
|
||||
* @var ConflictingNameResolver
|
||||
*/
|
||||
private $propertyNaming;
|
||||
private $conflictingNameResolver;
|
||||
|
||||
public function __construct(PropertyNaming $propertyNaming)
|
||||
{
|
||||
$this->propertyNaming = $propertyNaming;
|
||||
/**
|
||||
* @var ExpectedNameResolver
|
||||
*/
|
||||
private $expectedNameResolver;
|
||||
|
||||
public function __construct(
|
||||
ConflictingNameResolver $conflictingNameResolver,
|
||||
ExpectedNameResolver $expectedNameResolver
|
||||
) {
|
||||
$this->conflictingNameResolver = $conflictingNameResolver;
|
||||
$this->expectedNameResolver = $expectedNameResolver;
|
||||
}
|
||||
|
||||
public function getDefinition(): RectorDefinition
|
||||
|
@ -88,57 +94,19 @@ PHP
|
|||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
$this->refactorClassMethods((array) $node->getMethods());
|
||||
$this->refactorClassProperties((array) $node->getProperties(), $node);
|
||||
$this->refactorClassMethods($node);
|
||||
$this->refactorClassProperties($node);
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
private function matchExpectedParamNameIfNotYet(Param $param): ?string
|
||||
private function refactorClassMethods(ClassLike $classLike): void
|
||||
{
|
||||
// nothing to verify
|
||||
if ($param->type === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$staticType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($param->type);
|
||||
$expectedName = $this->propertyNaming->getExpectedNameFromType($staticType);
|
||||
if ($expectedName === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @var string $currentName */
|
||||
$currentName = $this->getName($param->var);
|
||||
if ($currentName === $expectedName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->endsWith($currentName, $expectedName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $expectedName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends with ucname
|
||||
* Starts with adjective, e.g. (Post $firstPost, Post $secondPost)
|
||||
*/
|
||||
private function endsWith(string $currentName, string $expectedName): bool
|
||||
{
|
||||
return (bool) Strings::match($currentName, '#\w+' . lcfirst($expectedName) . '#');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClassMethod[] $classMethods
|
||||
*/
|
||||
private function refactorClassMethods(array $classMethods): void
|
||||
{
|
||||
foreach ($classMethods as $classMethod) {
|
||||
$conflictingNames = $this->resolveConflictingNamesFromClassMethod($classMethod);
|
||||
foreach ($classLike->getMethods() as $classMethod) {
|
||||
$conflictingNames = $this->conflictingNameResolver->resolveConflictingVariableNames($classMethod);
|
||||
|
||||
foreach ($classMethod->params as $param) {
|
||||
$expectedName = $this->matchExpectedParamNameIfNotYet($param);
|
||||
$expectedName = $this->expectedNameResolver->resolveForParam($param);
|
||||
if ($expectedName === null) {
|
||||
continue;
|
||||
}
|
||||
|
@ -158,20 +126,17 @@ PHP
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Property[] $properties
|
||||
*/
|
||||
private function refactorClassProperties(array $properties, ClassLike $classLike): void
|
||||
private function refactorClassProperties(ClassLike $classLike): void
|
||||
{
|
||||
$conflictingPropertyNames = $this->resolveConflictingPropertyNamesFromClass($classLike);
|
||||
$conflictingPropertyNames = $this->conflictingNameResolver->resolveConflictingPropertyNames($classLike);
|
||||
|
||||
foreach ($properties as $property) {
|
||||
foreach ($classLike->getProperties() as $property) {
|
||||
if (count($property->props) !== 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$oldName = $this->getName($property);
|
||||
$expectedName = $this->matchExpectedPropertyNameIfNotYet($property);
|
||||
$expectedName = $this->expectedNameResolver->resolveForProperty($property);
|
||||
if ($expectedName === null) {
|
||||
continue;
|
||||
}
|
||||
|
@ -187,21 +152,6 @@ PHP
|
|||
}
|
||||
}
|
||||
|
||||
private function matchExpectedPropertyNameIfNotYet(Property $property): ?string
|
||||
{
|
||||
$currentName = $this->getName($property);
|
||||
|
||||
/** @var PhpDocInfo|null $phpDocInfo */
|
||||
$phpDocInfo = $property->getAttribute(AttributeKey::PHP_DOC_INFO);
|
||||
$expectedName = $this->propertyNaming->getExpectedNameFromType($phpDocInfo->getVarType());
|
||||
|
||||
if ($expectedName === $currentName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $expectedName;
|
||||
}
|
||||
|
||||
private function renameVariableInClassMethod(ClassMethod $classMethod, string $oldName, string $expectedName): void
|
||||
{
|
||||
$this->traverseNodesWithCallable((array) $classMethod->stmts, function (Node $node) use (
|
||||
|
@ -217,60 +167,6 @@ PHP
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
private function resolveConflictingNamesFromClassMethod(ClassMethod $classMethod): array
|
||||
{
|
||||
$expectedNames = [];
|
||||
foreach ($classMethod->params as $param) {
|
||||
$expectedName = $this->matchExpectedParamNameIfNotYet($param);
|
||||
if ($expectedName === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$expectedNames[] = $expectedName;
|
||||
}
|
||||
|
||||
$expectedNamesToCount = array_count_values($expectedNames);
|
||||
|
||||
$conflictingExpectedNames = [];
|
||||
foreach ($expectedNamesToCount as $expectedName => $count) {
|
||||
if ($count >= 2) {
|
||||
$conflictingExpectedNames[] = $expectedName;
|
||||
}
|
||||
}
|
||||
|
||||
return $conflictingExpectedNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
private function resolveConflictingPropertyNamesFromClass(ClassLike $classLike): array
|
||||
{
|
||||
$expectedNames = [];
|
||||
foreach ($classLike->getProperties() as $property) {
|
||||
$expectedName = $this->matchExpectedPropertyNameIfNotYet($property);
|
||||
if ($expectedName === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$expectedNames[] = $expectedName;
|
||||
}
|
||||
|
||||
$expectedNamesToCount = array_count_values($expectedNames);
|
||||
|
||||
$conflictingExpectedNames = [];
|
||||
foreach ($expectedNamesToCount as $expectedName => $count) {
|
||||
if ($count >= 2) {
|
||||
$conflictingExpectedNames[] = $expectedName;
|
||||
}
|
||||
}
|
||||
|
||||
return $conflictingExpectedNames;
|
||||
}
|
||||
|
||||
private function renamePropertyFetchesInClass(ClassLike $classLike, ?string $oldName, string $expectedName): void
|
||||
{
|
||||
// 1. replace property fetch rename in whole class
|
||||
|
|
Loading…
Reference in New Issue
Block a user