mirror of
https://github.com/rectorphp/rector.git
synced 2025-04-21 07:52:01 +02:00
improve trait and interface support for RemoveUnusedAliasRector
This commit is contained in:
parent
62fc1f102c
commit
624d1b528d
1
ecs.yml
1
ecs.yml
@ -105,6 +105,7 @@ parameters:
|
||||
- 'packages/CodeQuality/src/Rector/Foreach_/SimplifyForeachToArrayFilterRector.php'
|
||||
- 'src/PhpParser/Node/Resolver/NameResolver.php'
|
||||
- 'src/Rector/MethodBody/NormalToFluentRector.php'
|
||||
- 'packages/CodingStyle/src/Rector/Use_/RemoveUnusedAliasRector.php'
|
||||
# copied 3rd party logic
|
||||
- 'packages/Php/src/EregToPcreTransformer.php'
|
||||
# dev
|
||||
|
@ -3,11 +3,17 @@
|
||||
namespace Rector\CodingStyle\Rector\Use_;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\New_;
|
||||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Param;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassLike;
|
||||
use PhpParser\Node\Stmt\Namespace_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PhpParser\Node\Stmt\Interface_;
|
||||
use PhpParser\Node\Stmt\TraitUse;
|
||||
use PhpParser\Node\Stmt\Use_;
|
||||
use PhpParser\Node\Stmt\UseUse;
|
||||
use PhpParser\NodeVisitor\NameResolver;
|
||||
use Rector\Exception\ShouldNotHappenException;
|
||||
use Rector\NodeTypeResolver\Node\Attribute;
|
||||
@ -18,6 +24,11 @@ use Rector\RectorDefinition\RectorDefinition;
|
||||
|
||||
final class RemoveUnusedAliasRector extends AbstractRector
|
||||
{
|
||||
/**
|
||||
* @var Node[][][]
|
||||
*/
|
||||
private $resolvedNodeNames = [];
|
||||
|
||||
/**
|
||||
* @var BetterNodeFinder
|
||||
*/
|
||||
@ -64,8 +75,9 @@ CODE_SAMPLE
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
$usedNameNodes = $this->resolveUsedNameNodes($node);
|
||||
if ($usedNameNodes === []) {
|
||||
$this->resolvedNodeNames = [];
|
||||
$this->resolveUsedNameNodes($node);
|
||||
if ($this->resolvedNodeNames === []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -78,19 +90,19 @@ CODE_SAMPLE
|
||||
$aliasName = $this->getName($use->alias);
|
||||
|
||||
// both are used → nothing to remove
|
||||
if (isset($usedNameNodes[$lastName]) && isset($usedNameNodes[$aliasName])) {
|
||||
if (isset($this->resolvedNodeNames[$lastName]) && isset($this->resolvedNodeNames[$aliasName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// only last name is used → no need for alias
|
||||
if (isset($usedNameNodes[$lastName])) {
|
||||
if (isset($this->resolvedNodeNames[$lastName])) {
|
||||
$use->alias = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
// only alias name is used → use last name directly
|
||||
if (isset($usedNameNodes[$aliasName])) {
|
||||
$this->renameNameNode($usedNameNodes[$aliasName], $lastName);
|
||||
if (isset($this->resolvedNodeNames[$aliasName])) {
|
||||
$this->renameNameNode($this->resolvedNodeNames[$aliasName], $lastName);
|
||||
$use->alias = null;
|
||||
}
|
||||
}
|
||||
@ -98,25 +110,115 @@ CODE_SAMPLE
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Node[][][]
|
||||
*/
|
||||
private function resolveUsedNameNodes(Use_ $node): array
|
||||
private function resolveUsedNameNodes(Use_ $node): void
|
||||
{
|
||||
$parentNode = $node->getAttribute(Attribute::PARENT_NODE);
|
||||
if ($parentNode === null) { // no namespace
|
||||
$nextNode = $node->getAttribute(Attribute::NEXT_NODE);
|
||||
if ($nextNode === null) {
|
||||
throw new ShouldNotHappenException();
|
||||
$searchNode = $this->resolveSearchNode($node);
|
||||
|
||||
$this->resolvedUsedNames($searchNode);
|
||||
$this->resolveUsedClassNames($searchNode);
|
||||
$this->resolvedTraitUseNames($searchNode);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Node[][] $usedNameNodes
|
||||
*/
|
||||
private function renameNameNode(array $usedNameNodes, string $lastName): void
|
||||
{
|
||||
/** @var Identifier|Name $usedName */
|
||||
foreach ($usedNameNodes as [$usedName, $parentNode]) {
|
||||
if ($parentNode instanceof TraitUse) {
|
||||
foreach ($parentNode->traits as $key => $traitName) {
|
||||
if (! $this->areNamesEqual($traitName, $usedName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$parentNode->traits[$key] = new Name($lastName);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($parentNode instanceof Class_) {
|
||||
if ($parentNode->name !== null) {
|
||||
if ($this->areNamesEqual($parentNode->name, $usedName)) {
|
||||
$parentNode->name = new Identifier($lastName);
|
||||
}
|
||||
}
|
||||
|
||||
if ($parentNode->extends !== null) {
|
||||
if ($this->areNamesEqual($parentNode->extends, $usedName)) {
|
||||
$parentNode->extends = new Name($lastName);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($parentNode->implements as $key => $implementNode) {
|
||||
if ($this->areNamesEqual($implementNode, $usedName)) {
|
||||
$parentNode->implements[$key] = new Name($lastName);
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($parentNode instanceof Param) {
|
||||
if ($parentNode->type !== null) {
|
||||
if ($this->areNamesEqual($parentNode->type, $usedName)) {
|
||||
$parentNode->type = new Name($lastName);
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($parentNode instanceof New_) {
|
||||
if ($this->areNamesEqual($parentNode->class, $usedName)) {
|
||||
$parentNode->class = new Name($lastName);
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($parentNode instanceof ClassMethod) {
|
||||
if ($parentNode->returnType !== null) {
|
||||
if ($this->areNamesEqual($parentNode->returnType, $usedName)) {
|
||||
$parentNode->returnType = new Name($lastName);
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($parentNode instanceof Interface_) {
|
||||
foreach ($parentNode->extends as $key => $extendInterfaceName) {
|
||||
if ($this->areNamesEqual($extendInterfaceName, $usedName)) {
|
||||
$parentNode->extends[$key] = new Name($lastName);
|
||||
}
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$groupNode = $nextNode ?? $parentNode;
|
||||
private function resolveSearchNode(Use_ $node): Node
|
||||
{
|
||||
$searchNode = $node->getAttribute(Attribute::PARENT_NODE);
|
||||
if ($searchNode) {
|
||||
return $searchNode;
|
||||
}
|
||||
|
||||
$usedNameNodes = [];
|
||||
$searchNode = $node->getAttribute(Attribute::NEXT_NODE);
|
||||
if ($searchNode) {
|
||||
return $searchNode;
|
||||
}
|
||||
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
private function resolvedUsedNames(Node $searchNode): void
|
||||
{
|
||||
/** @var Name[] $namedNodes */
|
||||
$namedNodes = $this->betterNodeFinder->findInstanceOf($groupNode, Name::class);
|
||||
$namedNodes = $this->betterNodeFinder->findInstanceOf($searchNode, Name::class);
|
||||
|
||||
foreach ($namedNodes as $nameNode) {
|
||||
/** node name before becoming FQN - attribute from @see NameResolver */
|
||||
@ -127,47 +229,40 @@ CODE_SAMPLE
|
||||
|
||||
$parentNode = $nameNode->getAttribute(Attribute::PARENT_NODE);
|
||||
if ($parentNode === null) {
|
||||
throw new ShouldNotHappenException();
|
||||
}
|
||||
|
||||
$this->resolvedNodeNames[$originalName->toString()][] = [$nameNode, $parentNode];
|
||||
}
|
||||
}
|
||||
|
||||
private function resolveUsedClassNames(Node $searchNode): void
|
||||
{
|
||||
/** @var ClassLike[] $classLikeNodes */
|
||||
$classLikeNodes = $this->betterNodeFinder->findInstanceOf($searchNode, ClassLike::class);
|
||||
|
||||
foreach ($classLikeNodes as $classLikeNode) {
|
||||
if ($classLikeNode->name === null) { // skip anonymous classes
|
||||
continue;
|
||||
}
|
||||
|
||||
$usedNameNodes[$originalName->toString()][] = [$nameNode, $parentNode];
|
||||
}
|
||||
|
||||
/** @var ClassLike[] $classLikeNodes */
|
||||
$classLikeNodes = $this->betterNodeFinder->findInstanceOf($parentNode, ClassLike::class);
|
||||
|
||||
foreach ($classLikeNodes as $classLikeNode) {
|
||||
if ($classLikeNode->name) {
|
||||
$name = $this->getName($classLikeNode->name);
|
||||
$usedNameNodes[$name] = [$classLikeNode->name, $parentNode];
|
||||
}
|
||||
}
|
||||
|
||||
return $usedNameNodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Node[][] $usedNameNodes
|
||||
*/
|
||||
private function renameNameNode(array $usedNameNodes, string $lastName): void
|
||||
{
|
||||
foreach ($usedNameNodes as [$usedName, $parentNode]) {
|
||||
foreach ($this->getObjectPublicPropertyNames($parentNode) as $parentNodePropertyName) {
|
||||
if ($parentNode->{$parentNodePropertyName} !== $usedName) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$parentNode->{$parentNodePropertyName} = new Name($lastName);
|
||||
}
|
||||
$name = $this->getName($classLikeNode->name);
|
||||
$this->resolvedNodeNames[$name][] = [$classLikeNode->name, $classLikeNode];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $node
|
||||
* @return string[]
|
||||
*/
|
||||
private function getObjectPublicPropertyNames($node): array
|
||||
private function resolvedTraitUseNames(Node $searchNode): void
|
||||
{
|
||||
return array_keys(get_object_vars($node));
|
||||
/** @var Identifier[] $identifierNodes */
|
||||
$identifierNodes = $this->betterNodeFinder->findInstanceOf($searchNode, Identifier::class);
|
||||
|
||||
foreach ($identifierNodes as $identifierNode) {
|
||||
$parentNode = $identifierNode->getAttribute(Attribute::PARENT_NODE);
|
||||
if (! $parentNode instanceof UseUse) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->resolvedNodeNames[$identifierNode->name][] = [$identifierNode, $parentNode];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,8 @@
|
||||
|
||||
namespace Rector\CodingStyle\Tests\Rector\Use_\RemoveUnusedAliasRector\Fixture;
|
||||
|
||||
use Rector\CodingStyle\Tests\Rector\Use_\RemoveUnusedAliasRector\Source\Kernel as BaseKernel;
|
||||
use Rector\CodingStyle\Tests\Rector\Use_\RemoveUnusedAliasRector\Source\StandaloneClass as BaseKernel;
|
||||
|
||||
class Kernel extends BaseKernel
|
||||
class StandaloneClass extends BaseKernel
|
||||
{
|
||||
}
|
||||
|
@ -2,11 +2,13 @@
|
||||
|
||||
namespace Rector\CodingStyle\Tests\Rector\Use_\RemoveUnusedAliasRector\Fixture;
|
||||
|
||||
use Rector\CodingStyle\Tests\Rector\Use_\RemoveUnusedAliasRector\Source\AbstractInterface as BaseInterface;
|
||||
use Rector\CodingStyle\Tests\Rector\Use_\RemoveUnusedAliasRector\Source\AbstractKernel as BaseKernel;
|
||||
|
||||
class SomeClass extends BaseKernel
|
||||
|
||||
class SomeClass extends BaseKernel implements BaseInterface
|
||||
{
|
||||
public function run(BaseKernel $baseKernel)
|
||||
public function run(BaseKernel $baseKernel): BaseInterface
|
||||
{
|
||||
$anotherBaseKernel = new BaseKernel();
|
||||
}
|
||||
@ -18,11 +20,13 @@ class SomeClass extends BaseKernel
|
||||
|
||||
namespace Rector\CodingStyle\Tests\Rector\Use_\RemoveUnusedAliasRector\Fixture;
|
||||
|
||||
use Rector\CodingStyle\Tests\Rector\Use_\RemoveUnusedAliasRector\Source\AbstractInterface;
|
||||
use Rector\CodingStyle\Tests\Rector\Use_\RemoveUnusedAliasRector\Source\AbstractKernel;
|
||||
|
||||
class SomeClass extends AbstractKernel
|
||||
|
||||
class SomeClass extends AbstractKernel implements AbstractInterface
|
||||
{
|
||||
public function run(AbstractKernel $baseKernel)
|
||||
public function run(AbstractKernel $baseKernel): AbstractInterface
|
||||
{
|
||||
$anotherBaseKernel = new AbstractKernel();
|
||||
}
|
||||
|
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
namespace Rector\CodingStyle\Tests\Rector\Use_\RemoveUnusedAliasRector\Fixture;
|
||||
|
||||
use Rector\CodingStyle\Tests\Rector\Use_\RemoveUnusedAliasRector\Source\AbstractInterface as NotNeededAliasInterface;
|
||||
|
||||
interface BasicInterface extends NotNeededAliasInterface
|
||||
{
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\CodingStyle\Tests\Rector\Use_\RemoveUnusedAliasRector\Fixture;
|
||||
|
||||
use Rector\CodingStyle\Tests\Rector\Use_\RemoveUnusedAliasRector\Source\AbstractInterface;
|
||||
|
||||
interface BasicInterface extends AbstractInterface
|
||||
{
|
||||
}
|
||||
|
||||
?>
|
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
use Rector\CodingStyle\Tests\Rector\Use_\RemoveUnusedAliasRector\Source\AbstractKernel as BaseKernel;
|
||||
|
||||
class SomeClass extends BaseKernel
|
||||
{
|
||||
public function run(BaseKernel $baseKernel)
|
||||
{
|
||||
$anotherBaseKernel = new BaseKernel();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
use Rector\CodingStyle\Tests\Rector\Use_\RemoveUnusedAliasRector\Source\AbstractKernel;
|
||||
|
||||
class SomeClass extends AbstractKernel
|
||||
{
|
||||
public function run(AbstractKernel $baseKernel)
|
||||
{
|
||||
$anotherBaseKernel = new AbstractKernel();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
use Rector\CodingStyle\Tests\Rector\Use_\RemoveUnusedAliasRector\Source\StandaloneClass as BaseKernel;
|
||||
|
||||
class StandaloneClass extends BaseKernel
|
||||
{
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Rector\CodingStyle\Tests\Rector\Use_\RemoveUnusedAliasRector\Fixture;
|
||||
|
||||
use Rector\CodingStyle\Tests\Rector\Use_\RemoveUnusedAliasRector\Source\StandaloneTrait as DuplicatedStandaloneTrait;
|
||||
|
||||
trait StandaloneTrait
|
||||
{
|
||||
use DuplicatedStandaloneTrait;
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace Rector\CodingStyle\Tests\Rector\Use_\RemoveUnusedAliasRector\Fixture;
|
||||
|
||||
use Rector\CodingStyle\Tests\Rector\Use_\RemoveUnusedAliasRector\Source\ClassicTrait;
|
||||
use Rector\CodingStyle\Tests\Rector\Use_\RemoveUnusedAliasRector\Source\StandaloneTrait as DuplicatedStandaloneTrait;
|
||||
|
||||
trait UnneededStandaloneTrait
|
||||
{
|
||||
use ClassicTrait, DuplicatedStandaloneTrait;
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\CodingStyle\Tests\Rector\Use_\RemoveUnusedAliasRector\Fixture;
|
||||
|
||||
use Rector\CodingStyle\Tests\Rector\Use_\RemoveUnusedAliasRector\Source\ClassicTrait;
|
||||
use Rector\CodingStyle\Tests\Rector\Use_\RemoveUnusedAliasRector\Source\StandaloneTrait;
|
||||
|
||||
trait UnneededStandaloneTrait
|
||||
{
|
||||
use ClassicTrait, StandaloneTrait;
|
||||
}
|
||||
|
||||
?>
|
@ -9,7 +9,19 @@ final class RemoveUnusedAliasRectorTest extends AbstractRectorTestCase
|
||||
{
|
||||
public function test(): void
|
||||
{
|
||||
$this->doTestFiles([__DIR__ . '/Fixture/fixture.php.inc', __DIR__ . '/Fixture/used.php.inc']);
|
||||
$this->doTestFiles([
|
||||
__DIR__ . '/Fixture/fixture.php.inc',
|
||||
__DIR__ . '/Fixture/used.php.inc',
|
||||
__DIR__ . '/Fixture/class_name.php.inc',
|
||||
# no namespace
|
||||
__DIR__ . '/Fixture/no_namespace.php.inc',
|
||||
__DIR__ . '/Fixture/no_namespace_class_name.php.inc',
|
||||
# traits
|
||||
__DIR__ . '/Fixture/trait_name.php.inc',
|
||||
__DIR__ . '/Fixture/unneeded_trait_name.php.inc',
|
||||
# interfaces
|
||||
__DIR__ . '/Fixture/interace_extending.php.inc',
|
||||
]);
|
||||
}
|
||||
|
||||
protected function getRectorClass(): string
|
||||
|
@ -0,0 +1,8 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\CodingStyle\Tests\Rector\Use_\RemoveUnusedAliasRector\Source;
|
||||
|
||||
interface AbstractInterface
|
||||
{
|
||||
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\CodingStyle\Tests\Rector\Use_\RemoveUnusedAliasRector\Source;
|
||||
|
||||
trait ClassicTrait
|
||||
{
|
||||
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\CodingStyle\Tests\Rector\Use_\RemoveUnusedAliasRector\Source;
|
||||
|
||||
class Kernel
|
||||
{
|
||||
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\CodingStyle\Tests\Rector\Use_\RemoveUnusedAliasRector\Source;
|
||||
|
||||
class StandaloneClass
|
||||
{
|
||||
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\CodingStyle\Tests\Rector\Use_\RemoveUnusedAliasRector\Source;
|
||||
|
||||
trait StandaloneTrait
|
||||
{
|
||||
|
||||
}
|
@ -28,6 +28,8 @@ parameters:
|
||||
ignoreErrors:
|
||||
# false positive
|
||||
- '#PHPDoc tag \@param for parameter \$node with type float is incompatible with native type PhpParser\\Node#'
|
||||
- '#Method Rector\\CodingStyle\\Rector\\Use_\\RemoveUnusedAliasRector::resolveUsedClassNames\(\) should return array<array<array<PhpParser\\Node>>> but returns array<string, array<int, array<int, PhpParser\\Node\\Identifier\|PhpParser\\Node\\Stmt\\ClassLike\|null>>>#'
|
||||
- '#Result of && is always true#'
|
||||
|
||||
# missuse of interface and class
|
||||
- '#Parameter \#1 (.*?) expects Symfony\\Component\\DependencyInjection\\ContainerBuilder, Symfony\\Component\\DependencyInjection\\ContainerInterface given#'
|
||||
|
Loading…
x
Reference in New Issue
Block a user