[DeadCode] Add RemoveEmptyAbstractClassRector (#5558)

Co-authored-by: kaizen-ci <info@kaizen-ci.org>
This commit is contained in:
Abdul Malik Ikhsan 2021-02-17 02:15:45 +07:00 committed by GitHub
parent 06906a0659
commit 7475c7f025
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 458 additions and 0 deletions

View File

@ -0,0 +1,134 @@
<?php
declare(strict_types=1);
namespace Rector\DeadCode\Rector\Class_;
use PhpParser\Node;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\Class_;
use Rector\Core\PhpParser\Node\CustomNode\FileNode;
use Rector\Core\Rector\AbstractRector;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
/**
* @see \Rector\DeadCode\Tests\Rector\Class_\RemoveEmptyAbstractClassRector\RemoveEmptyAbstractClassRectorTest
*/
final class RemoveEmptyAbstractClassRector extends AbstractRector
{
/**
* @var FullyQualified[]
*/
private $fullyQualifieds = [];
/**
* @return string[]
*/
public function getNodeTypes(): array
{
return [FileNode::class, Class_::class];
}
/**
* @param FileNode|Class_ $node
*/
public function refactor(Node $node): ?Node
{
if ($node instanceof FileNode) {
/** @var FullyQualified[] $fullyQualifieds */
$fullyQualifieds = $this->betterNodeFinder->findInstanceOf($node, FullyQualified::class);
foreach ($fullyQualifieds as $fullyQualified) {
$parent = $fullyQualified->getAttribute(AttributeKey::PARENT_NODE);
if ($parent instanceof Class_) {
continue;
}
$this->fullyQualifieds[] = $fullyQualified;
}
return $node;
}
if ($this->shouldSkip($node)) {
return null;
}
return $this->processRemove($node);
}
public function getRuleDefinition(): RuleDefinition
{
return new RuleDefinition(
'Empty abstract class that does nothing',
[
new CodeSample(
<<<'CODE_SAMPLE'
class SomeClass extends SomeAbstractClass
{
}
abstract class SomeAbstractClass extends AnotherAbstractClass
{
}
abstract class AnotherAbstractClass
{
public function getName()
{
return 'name';
}
}
CODE_SAMPLE
,
<<<'CODE_SAMPLE'
class SomeClass extends AnotherAbstractClass
{
}
abstracst clas AnotherAbstractClass
{
public function getName()
{
return 'cowo';
}
}
CODE_SAMPLE
),
]);
}
private function shouldSkip(Class_ $class): bool
{
if (! $class->isAbstract()) {
return true;
}
if ($class->implements !== []) {
return true;
}
$stmts = $class->stmts;
return $stmts !== [];
}
private function processRemove(Class_ $class): ?Class_
{
$className = $class->namespacedName->toString();
foreach ($this->fullyQualifieds as $fullyQualified) {
if ($className === $fullyQualified->toString()) {
return null;
}
}
$children = $this->nodeRepository->findChildrenOfClass($this->getName($class->namespacedName));
$extends = $class->extends;
foreach ($children as $child) {
$child->extends = $extends;
}
$this->removeNode($class);
return $class;
}
}

View File

@ -0,0 +1,43 @@
<?php
namespace Rector\DeadCode\Tests\Rector\Class_\RemoveEmptyAbstractClassRector\Fixture;
abstract class AbstractClassMulti
{
}
abstract class AbstractClassUsedMulti extends AbstractClassMulti
{
}
final class AnyClass2
{
public function process($some)
{
if ($some instanceof AbstractClassUsedMulti) {
}
}
}
?>
-----
<?php
namespace Rector\DeadCode\Tests\Rector\Class_\RemoveEmptyAbstractClassRector\Fixture;
abstract class AbstractClassUsedMulti
{
}
final class AnyClass2
{
public function process($some)
{
if ($some instanceof AbstractClassUsedMulti) {
}
}
}
?>

View File

@ -0,0 +1,43 @@
<?php
namespace Rector\DeadCode\Tests\Rector\Class_\RemoveEmptyAbstractClassRector\Fixture;
abstract class AbstractClassMultiUpper
{
}
abstract class AbstractClassUsedMultiUpper extends AbstractClassMultiUpper
{
}
final class AnyClass3
{
public function process($some)
{
if ($some instanceof AbstractClassMultiUpper) {
}
}
}
?>
-----
<?php
namespace Rector\DeadCode\Tests\Rector\Class_\RemoveEmptyAbstractClassRector\Fixture;
abstract class AbstractClassMultiUpper
{
}
final class AnyClass3
{
public function process($some)
{
if ($some instanceof AbstractClassMultiUpper) {
}
}
}
?>

View File

@ -0,0 +1,39 @@
<?php
namespace Rector\DeadCode\Tests\Rector\Class_\RemoveEmptyAbstractClassRector\Fixture;
abstract class AnotherAbstractClass
{
public function getName()
{
return 'name';
}
}
abstract class SomeAbstractClass extends AnotherAbstractClass
{
}
class SomeClass extends SomeAbstractClass
{
}
?>
-----
<?php
namespace Rector\DeadCode\Tests\Rector\Class_\RemoveEmptyAbstractClassRector\Fixture;
abstract class AnotherAbstractClass
{
public function getName()
{
return 'name';
}
}
class SomeClass extends AnotherAbstractClass
{
}
?>

View File

@ -0,0 +1,43 @@
<?php
namespace Rector\DeadCode\Tests\Rector\Class_\RemoveEmptyAbstractClassRector\Fixture;
abstract class AnotherAbstractClassMulti
{
public function getName()
{
return 'name';
}
}
abstract class SomeAbstractClassMulti1 extends AnotherAbstractClassMulti
{
}
abstract class SomeAbstractClassMulti2 extends SomeAbstractClassMulti1
{
}
class MultiExtends extends SomeAbstractClassMulti2
{
}
?>
-----
<?php
namespace Rector\DeadCode\Tests\Rector\Class_\RemoveEmptyAbstractClassRector\Fixture;
abstract class AnotherAbstractClassMulti
{
public function getName()
{
return 'name';
}
}
class MultiExtends extends AnotherAbstractClassMulti
{
}
?>

View File

@ -0,0 +1,23 @@
<?php
namespace Rector\DeadCode\Tests\Rector\Class_\RemoveEmptyAbstractClassRector\Fixture;
abstract class SingleEmptyAbstract
{
}
class Single extends SingleEmptyAbstract
{
}
?>
-----
<?php
namespace Rector\DeadCode\Tests\Rector\Class_\RemoveEmptyAbstractClassRector\Fixture;
class Single
{
}
?>

View File

@ -0,0 +1,21 @@
<?php
namespace Rector\DeadCode\Tests\Rector\Class_\RemoveEmptyAbstractClassRector\Fixture;
abstract class AbstractClassUsed
{
}
class skipAbstractClassUsed extends AbstractClassUsed
{
}
final class AnyClass
{
public function process($some)
{
if ($some instanceof AbstractClassUsed) {
}
}
}

View File

@ -0,0 +1,8 @@
<?php
namespace Rector\DeadCode\Tests\Rector\Class_\RemoveEmptyAbstractClassRector\Fixture;
abstract class SkipHasConstant
{
protected const A_CONSTANT = 'foo';
}

View File

@ -0,0 +1,8 @@
<?php
namespace Rector\DeadCode\Tests\Rector\Class_\RemoveEmptyAbstractClassRector\Fixture;
abstract class SkipHasMethod
{
public abstract function run();
}

View File

@ -0,0 +1,8 @@
<?php
namespace Rector\DeadCode\Tests\Rector\Class_\RemoveEmptyAbstractClassRector\Fixture;
abstract class SkipHasProperty
{
protected $foo;
}

View File

@ -0,0 +1,10 @@
<?php
namespace Rector\DeadCode\Tests\Rector\Class_\RemoveEmptyAbstractClassRector\Fixture;
use Rector\DeadCode\Tests\Rector\Class_\RemoveEmptyAbstractClassRector\Source\ATrait;
abstract class SkipHasTrait
{
use ATrait;
}

View File

@ -0,0 +1,9 @@
<?php
namespace Rector\DeadCode\Tests\Rector\Class_\RemoveEmptyAbstractClassRector\Fixture;
use Rector\DeadCode\Tests\Rector\Class_\RemoveEmptyAbstractClassRector\Source\AnInterface;
abstract class SkipImplementsInterface implements AnInterface
{
}

View File

@ -0,0 +1,7 @@
<?php
namespace Rector\DeadCode\Tests\Rector\Class_\RemoveEmptyAbstractClassRector\Fixture;
final class SkipNotAbstract
{
}

View File

@ -0,0 +1,19 @@
<?php
namespace Rector\DeadCode\Tests\Rector\Class_\RemoveEmptyAbstractClassRector\Fixture;
abstract class SkipSingleEmptyAbstractUsed
{
}
class Single2
{
function run($some)
{
if ($some instanceof SkipSingleEmptyAbstractUsed) {
}
}
}
?>

View File

@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Rector\DeadCode\Tests\Rector\Class_\RemoveEmptyAbstractClassRector;
use Iterator;
use Rector\DeadCode\Rector\Class_\RemoveEmptyAbstractClassRector;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
use Symplify\SmartFileSystem\SmartFileInfo;
final class RemoveEmptyAbstractClassRectorTest extends AbstractRectorTestCase
{
/**
* @dataProvider provideData()
*/
public function test(SmartFileInfo $fileInfo): void
{
$this->doTestFileInfo($fileInfo);
}
public function provideData(): Iterator
{
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
}
protected function getRectorClass(): string
{
return RemoveEmptyAbstractClassRector::class;
}
}

View File

@ -0,0 +1,6 @@
<?php
namespace Rector\DeadCode\Tests\Rector\Class_\RemoveEmptyAbstractClassRector\Source;
trait ATrait
{}

View File

@ -0,0 +1,6 @@
<?php
namespace Rector\DeadCode\Tests\Rector\Class_\RemoveEmptyAbstractClassRector\Source;
interface AnInterface
{}