mirror of
https://github.com/rectorphp/rector.git
synced 2025-01-18 05:48:21 +01:00
[CodingStyle] Add RemoveUnusedAliasRector
This commit is contained in:
parent
392d444862
commit
3414bd925e
@ -6,3 +6,4 @@ services:
|
||||
Rector\CodingStyle\Rector\Switch_\BinarySwitchToIfElseRector: ~
|
||||
Rector\CodingStyle\Rector\FuncCall\ConsistentImplodeRector: ~
|
||||
Rector\CodingStyle\Rector\FuncCall\SetTypeToCastRector: ~
|
||||
Rector\CodingStyle\Rector\Use_\RemoveUnusedAliasRector: ~
|
||||
|
159
packages/CodingStyle/src/Rector/Use_/RemoveUnusedAliasRector.php
Normal file
159
packages/CodingStyle/src/Rector/Use_/RemoveUnusedAliasRector.php
Normal file
@ -0,0 +1,159 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\CodingStyle\Rector\Use_;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Param;
|
||||
use PhpParser\Node\Stmt\Namespace_;
|
||||
use PhpParser\Node\Stmt\Use_;
|
||||
use PhpParser\NodeVisitor\NameResolver;
|
||||
use Rector\NodeTypeResolver\Node\Attribute;
|
||||
use Rector\PhpParser\Node\BetterNodeFinder;
|
||||
use Rector\Rector\AbstractRector;
|
||||
use Rector\RectorDefinition\CodeSample;
|
||||
use Rector\RectorDefinition\RectorDefinition;
|
||||
|
||||
final class RemoveUnusedAliasRector extends AbstractRector
|
||||
{
|
||||
/**
|
||||
* @var BetterNodeFinder
|
||||
*/
|
||||
private $betterNodeFinder;
|
||||
|
||||
public function __construct(BetterNodeFinder $betterNodeFinder)
|
||||
{
|
||||
$this->betterNodeFinder = $betterNodeFinder;
|
||||
}
|
||||
|
||||
public function getDefinition(): RectorDefinition
|
||||
{
|
||||
return new RectorDefinition('Removes unused use aliases', [
|
||||
new CodeSample(
|
||||
<<<'CODE_SAMPLE'
|
||||
use Symfony\Kernel as BaseKernel;
|
||||
|
||||
class SomeClass extends BaseKernel
|
||||
{
|
||||
}
|
||||
CODE_SAMPLE
|
||||
,
|
||||
<<<'CODE_SAMPLE'
|
||||
use Symfony\Kernel;
|
||||
|
||||
class SomeClass extends Kernel
|
||||
{
|
||||
}
|
||||
CODE_SAMPLE
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [Use_::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Use_ $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
$parentNode = $node->getAttribute(Attribute::PARENT_NODE);
|
||||
$usedNameNodes = $this->resolveUsedNameNodes($parentNode);
|
||||
if ($usedNameNodes === []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach ($node->uses as $use) {
|
||||
if ($use->alias === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$lastName = $use->name->getLast();
|
||||
$aliasName = $this->getName($use->alias);
|
||||
|
||||
// both are used → nothing to remove
|
||||
if (isset($usedNameNodes[$lastName]) && isset($usedNameNodes[$aliasName])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// only last name is used → no need for alias
|
||||
if (isset($usedNameNodes[$lastName])) {
|
||||
$use->alias = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
// only alis name is used → use last name directly
|
||||
if (isset($usedNameNodes[$aliasName])) {
|
||||
$this->renameNameNode($usedNameNodes, $aliasName, $lastName);
|
||||
$use->alias = null;
|
||||
}
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Name[][]|Node[][]
|
||||
*/
|
||||
private function resolveUsedNameNodes(Node $parentNode): array
|
||||
{
|
||||
$usedNameNodes = [];
|
||||
|
||||
// assumption for namespaced content
|
||||
if ($parentNode instanceof Namespace_) {
|
||||
/** @var Name[] $namedNodes */
|
||||
$namedNodes = $this->betterNodeFinder->find($parentNode, function (Node $node) {
|
||||
if ($node instanceof Name) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
foreach ($namedNodes as $nameNode) {
|
||||
/** node name before becoming FQN - attribute from @see NameResolver */
|
||||
$originalName = $nameNode->getAttribute('originalName');
|
||||
if (! $originalName instanceof Name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$usedNameNodes[$originalName->toString()][] = [
|
||||
$nameNode,
|
||||
$nameNode->getAttribute(Attribute::PARENT_NODE),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $usedNameNodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Name[][]|Node[][] $usedNameNodes
|
||||
*/
|
||||
private function renameNameNode(array $usedNameNodes, string $aliasName, string $lastName): void
|
||||
{
|
||||
foreach ($usedNameNodes[$aliasName] as [$usedName, $parentNode]) {
|
||||
foreach ($this->getObjectPublicPropertyNames($parentNode) as $parentNodePropertyName) {
|
||||
if ($parentNode->{$parentNodePropertyName} !== $usedName) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$parentNode->{$parentNodePropertyName} = new Name($lastName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object $node
|
||||
* @return string[]
|
||||
*/
|
||||
private function getObjectPublicPropertyNames($node): array
|
||||
{
|
||||
return array_keys(get_object_vars($node));
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace Rector\CodingStyle\Tests\Rector\Use_\RemoveUnusedAliasRector\Fixture;
|
||||
|
||||
use Rector\CodingStyle\Tests\Rector\Use_\RemoveUnusedAliasRector\Source\AbstractKernel as BaseKernel;
|
||||
|
||||
class SomeClass extends BaseKernel
|
||||
{
|
||||
public function run(BaseKernel $baseKernel)
|
||||
{
|
||||
$anotherBaseKernel = new BaseKernel();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\CodingStyle\Tests\Rector\Use_\RemoveUnusedAliasRector\Fixture;
|
||||
|
||||
use Rector\CodingStyle\Tests\Rector\Use_\RemoveUnusedAliasRector\Source\AbstractKernel;
|
||||
|
||||
class SomeClass extends AbstractKernel
|
||||
{
|
||||
public function run(AbstractKernel $baseKernel)
|
||||
{
|
||||
$anotherBaseKernel = new AbstractKernel();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace Rector\CodingStyle\Tests\Rector\Use_\RemoveUnusedAliasRector\Fixture;
|
||||
|
||||
use Rector\CodingStyle\Tests\Rector\Use_\RemoveUnusedAliasRector\Source\AbstractKernel as BaseKernel;
|
||||
use Rector\CodingStyle\Tests\Rector\Use_\RemoveUnusedAliasRector\Source\Another\AbstractKernel;
|
||||
|
||||
class Used extends BaseKernel
|
||||
{
|
||||
public function run(BaseKernel $baseKernel, AbstractKernel $abstractKernel)
|
||||
{
|
||||
$anotherBaseKernel = new BaseKernel();
|
||||
$anotherAbstractKernel = new AbstractKernel();
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\CodingStyle\Tests\Rector\Use_\RemoveUnusedAliasRector;
|
||||
|
||||
use Rector\CodingStyle\Rector\Use_\RemoveUnusedAliasRector;
|
||||
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
|
||||
final class RemoveUnusedAliasRectorTest extends AbstractRectorTestCase
|
||||
{
|
||||
public function test(): void
|
||||
{
|
||||
$this->doTestFiles([
|
||||
__DIR__ . '/Fixture/fixture.php.inc',
|
||||
__DIR__ . '/Fixture/used.php.inc',
|
||||
]);
|
||||
}
|
||||
|
||||
protected function getRectorClass(): string
|
||||
{
|
||||
return RemoveUnusedAliasRector::class;
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\CodingStyle\Tests\Rector\Use_\RemoveUnusedAliasRector\Source;
|
||||
|
||||
class AbstractKernel
|
||||
{
|
||||
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Rector\CodingStyle\Tests\Rector\Use_\RemoveUnusedAliasRector\Source\Another;
|
||||
|
||||
class AbstractKernel
|
||||
{
|
||||
|
||||
}
|
@ -83,14 +83,17 @@ final class NodeScopeAndMetadataDecorator
|
||||
public function decorateNodesFromFile(array $nodes, string $filePath): array
|
||||
{
|
||||
$nodeTraverser = new NodeTraverser();
|
||||
// specially rewrite nodes for PHPStan
|
||||
$nodeTraverser->addVisitor(new NameResolver());
|
||||
$nodeTraverser->addVisitor(new NameResolver(null, [
|
||||
'preserveOriginalNames' => true,
|
||||
'replaceNodes' => true, // required by PHPStan
|
||||
]));
|
||||
$nodes = $nodeTraverser->traverse($nodes);
|
||||
|
||||
$nodes = $this->nodeScopeResolver->processNodes($nodes, $filePath);
|
||||
|
||||
$nodeTraverser = new NodeTraverser();
|
||||
$nodeTraverser->addVisitor(new NameResolver(null, [
|
||||
'preserveOriginalNames' => true,
|
||||
// this option would override old non-fqn-namespaced nodes otherwise, so it needs to be disabled
|
||||
'replaceNodes' => false,
|
||||
]));
|
||||
|
Loading…
x
Reference in New Issue
Block a user