mirror of
https://github.com/rectorphp/rector.git
synced 2024-05-28 15:00:50 +00:00
[DeadCode] Add RemoveDeadRecursiveClassMethodRector
This commit is contained in:
parent
0c2cbca6f2
commit
9a9f777513
|
@ -7,8 +7,8 @@ services:
|
|||
Rector\CodingStyle\Rector\Use_\RemoveUnusedAliasRector: null
|
||||
|
||||
# requires configuration
|
||||
# Rector\CodingStyle\Rector\ClassMethod\YieldClassMethodToArrayClassMethodRector: null
|
||||
# Rector\CodingStyle\Rector\ClassMethod\ReturnArrayClassMethodToYieldRector: null
|
||||
# Rector\CodingStyle\Rector\ClassMethod\YieldClassMethodToArrayClassMethodRector: null
|
||||
# Rector\CodingStyle\Rector\ClassMethod\ReturnArrayClassMethodToYieldRector: null
|
||||
Rector\CodingStyle\Rector\String_\SymplifyQuoteEscapeRector: null
|
||||
Rector\CodingStyle\Rector\ClassConst\SplitGroupedConstantsAndPropertiesRector: null
|
||||
Rector\CodingStyle\Rector\String_\SplitStringClassConstantToClassConstFetchRector: null
|
||||
|
|
|
@ -38,3 +38,4 @@ services:
|
|||
Rector\DeadCode\Rector\Function_\RemoveUnusedFunctionRector: null
|
||||
Rector\DeadCode\Rector\If_\RemoveUnusedNonEmptyArrayBeforeForeachRector: null
|
||||
Rector\DeadCode\Rector\Assign\RemoveAssignOfVoidReturnFunctionRector: null
|
||||
Rector\DeadCode\Rector\ClassMethod\RemoveDeadRecursiveClassMethodRector: null
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# All 475 Rectors Overview
|
||||
# All 476 Rectors Overview
|
||||
|
||||
- [Projects](#projects)
|
||||
- [General](#general)
|
||||
|
@ -2645,6 +2645,25 @@ Remove if, foreach and for that does not do anything
|
|||
|
||||
<br>
|
||||
|
||||
### `RemoveDeadRecursiveClassMethodRector`
|
||||
|
||||
- class: [`Rector\DeadCode\Rector\ClassMethod\RemoveDeadRecursiveClassMethodRector`](/../master/rules/dead-code/src/Rector/ClassMethod/RemoveDeadRecursiveClassMethodRector.php)
|
||||
- [test fixtures](/../master/rules/dead-code/tests/Rector/ClassMethod/RemoveDeadRecursiveClassMethodRector/Fixture)
|
||||
|
||||
Remove unused public method that only calls itself recursively
|
||||
|
||||
```diff
|
||||
class SomeClass
|
||||
{
|
||||
- public function run()
|
||||
- {
|
||||
- return $this->run();
|
||||
- }
|
||||
}
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
### `RemoveDeadReturnRector`
|
||||
|
||||
- class: [`Rector\DeadCode\Rector\FunctionLike\RemoveDeadReturnRector`](/../master/rules/dead-code/src/Rector/FunctionLike/RemoveDeadReturnRector.php)
|
||||
|
|
|
@ -228,16 +228,9 @@ final class ParsedNodeCollector
|
|||
|
||||
public function findClassConstantByClassConstFetch(ClassConstFetch $classConstFetch): ?ClassConst
|
||||
{
|
||||
$class = $this->nodeNameResolver->getName($classConstFetch->class);
|
||||
|
||||
if ($class === 'self') {
|
||||
/** @var string|null $class */
|
||||
$class = $classConstFetch->getAttribute(AttributeKey::CLASS_NAME);
|
||||
} elseif ($class === 'parent') {
|
||||
/** @var string|null $class */
|
||||
$class = $classConstFetch->getAttribute(AttributeKey::PARENT_CLASS_NAME);
|
||||
}
|
||||
$className = $this->nodeNameResolver->getName($classConstFetch->class);
|
||||
|
||||
$class = $this->resolveClassConstant($classConstFetch, $className);
|
||||
if ($class === null) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
@ -284,4 +277,17 @@ final class ParsedNodeCollector
|
|||
// PHPStan polution
|
||||
return Strings::startsWith($classNode->name->toString(), 'AnonymousClass');
|
||||
}
|
||||
|
||||
private function resolveClassConstant(ClassConstFetch $classConstFetch, ?string $className): ?string
|
||||
{
|
||||
if ($className === 'self') {
|
||||
return $classConstFetch->getAttribute(AttributeKey::CLASS_NAME);
|
||||
}
|
||||
|
||||
if ($className === 'parent') {
|
||||
return $classConstFetch->getAttribute(AttributeKey::PARENT_CLASS_NAME);
|
||||
}
|
||||
|
||||
return $className;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ final class MethodCallParsedNodesFinder
|
|||
/**
|
||||
* @return MethodCall[]|StaticCall[]|ArrayCallable[]
|
||||
*/
|
||||
public function findClassMethodCalls(ClassMethod $classMethod): array
|
||||
public function findByClassMethod(ClassMethod $classMethod): array
|
||||
{
|
||||
/** @var string|null $className */
|
||||
$className = $classMethod->getAttribute(AttributeKey::CLASS_NAME);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
services:
|
||||
Rector\Privatization\Rector\MethodCall\PrivatizeLocalGetterToPropertyRector: null
|
||||
# Rector\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector: null
|
||||
Rector\DeadCode\Rector\ClassMethod\RemoveDeadRecursiveClassMethodRector: null
|
||||
# Rector\Privatization\Rector\MethodCall\PrivatizeLocalGetterToPropertyRector: null
|
||||
|
||||
imports:
|
||||
- { resource: "create-rector.yaml", ignore_errors: true }
|
||||
|
|
|
@ -6,10 +6,13 @@ namespace Rector\CodingStyle\Application;
|
|||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Name;
|
||||
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
|
||||
use Rector\CodingStyle\Node\NameImporter;
|
||||
use Rector\Core\Configuration\Option;
|
||||
use Rector\Core\Contract\PhpParser\Node\CommanderInterface;
|
||||
use Rector\Core\PhpParser\NodeTraverser\CallableNodeTraverser;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockNameImporter;
|
||||
use Symplify\PackageBuilder\Parameter\ParameterProvider;
|
||||
|
||||
final class NameImportingCommander implements CommanderInterface
|
||||
|
@ -29,14 +32,28 @@ final class NameImportingCommander implements CommanderInterface
|
|||
*/
|
||||
private $callableNodeTraverser;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $importDocBlocks = false;
|
||||
|
||||
/**
|
||||
* @var DocBlockNameImporter
|
||||
*/
|
||||
private $docBlockNameImporter;
|
||||
|
||||
public function __construct(
|
||||
ParameterProvider $parameterProvider,
|
||||
NameImporter $nameImporter,
|
||||
CallableNodeTraverser $callableNodeTraverser
|
||||
CallableNodeTraverser $callableNodeTraverser,
|
||||
DocBlockNameImporter $docBlockNameImporter,
|
||||
bool $importDocBlocks
|
||||
) {
|
||||
$this->parameterProvider = $parameterProvider;
|
||||
$this->nameImporter = $nameImporter;
|
||||
$this->callableNodeTraverser = $callableNodeTraverser;
|
||||
$this->importDocBlocks = $importDocBlocks;
|
||||
$this->docBlockNameImporter = $docBlockNameImporter;
|
||||
}
|
||||
|
||||
public function isActive(): bool
|
||||
|
@ -50,12 +67,27 @@ final class NameImportingCommander implements CommanderInterface
|
|||
*/
|
||||
public function traverseNodes(array $nodes): array
|
||||
{
|
||||
$this->callableNodeTraverser->traverseNodesWithCallable($nodes, function (Node $node): ?Name {
|
||||
if (! $node instanceof Name) {
|
||||
return null;
|
||||
$this->callableNodeTraverser->traverseNodesWithCallable($nodes, function (Node $node): ?Node {
|
||||
if ($node instanceof Name) {
|
||||
return $this->nameImporter->importName($node);
|
||||
}
|
||||
|
||||
return $this->nameImporter->importName($node);
|
||||
if ($this->importDocBlocks) {
|
||||
/** @var PhpDocInfo|null $phpDocInfo */
|
||||
$phpDocInfo = $node->getAttribute(AttributeKey::PHP_DOC_INFO);
|
||||
if ($phpDocInfo === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$hasChanged = $this->docBlockNameImporter->importNames($phpDocInfo, $node);
|
||||
if (! $hasChanged) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
return $nodes;
|
||||
|
|
|
@ -17,22 +17,17 @@ use Rector\NodeTypeResolver\Node\AttributeKey;
|
|||
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockNameImporter;
|
||||
|
||||
/**
|
||||
* This rule must be last, as it shortens nodes before using them by other Rectors, thus breaking them.
|
||||
*
|
||||
* This file remains just for testing.
|
||||
* Breaks other rectors by making name nodes short, FQN → short names
|
||||
*
|
||||
* @see \Rector\CodingStyle\Tests\Rector\Namespace_\ImportFullyQualifiedNamesRector\ImportFullyQualifiedNamesRectorTest
|
||||
* @see \Rector\CodingStyle\Tests\Rector\Namespace_\ImportFullyQualifiedNamesRector\NonNamespacedTest
|
||||
* @see \Rector\CodingStyle\Tests\Rector\Namespace_\ImportFullyQualifiedNamesRector\ImportRootNamespaceClassesDisabledTest
|
||||
*/
|
||||
final class ImportFullyQualifiedNamesRector extends AbstractRector
|
||||
{
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $importDocBlocks = true;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $autoImportNames = false;
|
||||
|
||||
/**
|
||||
* @var NameImporter
|
||||
*/
|
||||
|
@ -43,15 +38,9 @@ final class ImportFullyQualifiedNamesRector extends AbstractRector
|
|||
*/
|
||||
private $docBlockNameImporter;
|
||||
|
||||
public function __construct(
|
||||
NameImporter $nameImporter,
|
||||
bool $importDocBlocks,
|
||||
bool $autoImportNames,
|
||||
DocBlockNameImporter $docBlockNameImporter
|
||||
) {
|
||||
public function __construct(NameImporter $nameImporter, DocBlockNameImporter $docBlockNameImporter)
|
||||
{
|
||||
$this->nameImporter = $nameImporter;
|
||||
$this->importDocBlocks = $importDocBlocks;
|
||||
$this->autoImportNames = $autoImportNames;
|
||||
$this->docBlockNameImporter = $docBlockNameImporter;
|
||||
}
|
||||
|
||||
|
@ -108,8 +97,10 @@ PHP
|
|||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
// this file remains just for testing
|
||||
// breaks other rectors by making name nodes short, FQN → short names
|
||||
/** prevents duplicated run with @see NameImportingCommander */
|
||||
if ($this->autoImportNames && ! PHPUnitEnvironment::isPHPUnitRun()) {
|
||||
if (! PHPUnitEnvironment::isPHPUnitRun()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -120,21 +111,17 @@ PHP
|
|||
}
|
||||
|
||||
// process doc blocks
|
||||
if ($this->importDocBlocks) {
|
||||
/** @var PhpDocInfo|null $phpDocInfo */
|
||||
$phpDocInfo = $node->getAttribute(AttributeKey::PHP_DOC_INFO);
|
||||
if ($phpDocInfo === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$hasChanged = $this->docBlockNameImporter->importNames($phpDocInfo, $node);
|
||||
if (! $hasChanged) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $node;
|
||||
/** @var PhpDocInfo|null $phpDocInfo */
|
||||
$phpDocInfo = $node->getAttribute(AttributeKey::PHP_DOC_INFO);
|
||||
if ($phpDocInfo === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
$hasChanged = $this->docBlockNameImporter->importNames($phpDocInfo, $node);
|
||||
if (! $hasChanged) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\DeadCode\NodeManipulator;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Expr\StaticCall;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use Rector\NodeNameResolver\NodeNameResolver;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\NodeTypeResolver\NodeTypeResolver;
|
||||
|
||||
final class ClassMethodAndCallMatcher
|
||||
{
|
||||
/**
|
||||
* @var NodeNameResolver
|
||||
*/
|
||||
private $nodeNameResolver;
|
||||
|
||||
/**
|
||||
* @var NodeTypeResolver
|
||||
*/
|
||||
private $nodeTypeResolver;
|
||||
|
||||
public function __construct(NodeNameResolver $nodeNameResolver, NodeTypeResolver $nodeTypeResolver)
|
||||
{
|
||||
$this->nodeNameResolver = $nodeNameResolver;
|
||||
$this->nodeTypeResolver = $nodeTypeResolver;
|
||||
}
|
||||
|
||||
public function isMethodLikeCallMatchingClassMethod(Node $node, ClassMethod $classMethod): bool
|
||||
{
|
||||
if ($node instanceof MethodCall) {
|
||||
return $this->isMethodCallMatchingClassMethod($node, $classMethod);
|
||||
}
|
||||
|
||||
if ($node instanceof StaticCall) {
|
||||
return $this->isStaticCallMatchingClassMethod($node, $classMethod);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function isMethodCallMatchingClassMethod(MethodCall $methodCall, ClassMethod $classMethod): bool
|
||||
{
|
||||
/** @var string $className */
|
||||
$className = $classMethod->getAttribute(AttributeKey::CLASS_NAME);
|
||||
|
||||
/** @var string $classMethodName */
|
||||
$classMethodName = $this->nodeNameResolver->getName($classMethod);
|
||||
|
||||
if (! $this->nodeNameResolver->isName($methodCall->name, $classMethodName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$classMethodStaticType = new ObjectType($className);
|
||||
$callerStaticType = $this->nodeTypeResolver->getStaticType($methodCall->var);
|
||||
|
||||
return $classMethodStaticType->isSuperTypeOf($callerStaticType)->yes();
|
||||
}
|
||||
|
||||
private function isStaticCallMatchingClassMethod(StaticCall $staticCall, ClassMethod $classMethod): bool
|
||||
{
|
||||
/** @var string $className */
|
||||
$className = $classMethod->getAttribute(AttributeKey::CLASS_NAME);
|
||||
|
||||
/** @var string $methodName */
|
||||
$methodName = $this->nodeNameResolver->getName($classMethod);
|
||||
|
||||
if (! $this->nodeNameResolver->isName($staticCall->name, $methodName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$classMethodStaticType = new ObjectType($className);
|
||||
|
||||
$callerStaticType = $this->nodeTypeResolver->resolve($staticCall->class);
|
||||
if ($callerStaticType instanceof MixedType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $classMethodStaticType->isSuperTypeOf($callerStaticType)->yes();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,150 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\DeadCode\Rector\ClassMethod;
|
||||
|
||||
use PhpParser\Node;
|
||||
use PhpParser\Node\Expr\MethodCall;
|
||||
use PhpParser\Node\Expr\StaticCall;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use Rector\Core\Rector\AbstractRector;
|
||||
use Rector\Core\RectorDefinition\CodeSample;
|
||||
use Rector\Core\RectorDefinition\RectorDefinition;
|
||||
use Rector\DeadCode\NodeManipulator\ClassMethodAndCallMatcher;
|
||||
use Rector\NodeCollector\NodeFinder\MethodCallParsedNodesFinder;
|
||||
use Rector\NodeCollector\ValueObject\ArrayCallable;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\VendorLocker\NodeVendorLocker\ClassMethodVendorLockResolver;
|
||||
|
||||
/**
|
||||
* @see \Rector\DeadCode\Tests\Rector\ClassMethod\RemoveDeadRecursiveClassMethodRector\RemoveDeadRecursiveClassMethodRectorTest
|
||||
*/
|
||||
final class RemoveDeadRecursiveClassMethodRector extends AbstractRector
|
||||
{
|
||||
/**
|
||||
* @var MethodCallParsedNodesFinder
|
||||
*/
|
||||
private $methodCallParsedNodesFinder;
|
||||
|
||||
/**
|
||||
* @var ClassMethodAndCallMatcher
|
||||
*/
|
||||
private $classMethodAndCallMatcher;
|
||||
|
||||
/**
|
||||
* @var ClassMethodVendorLockResolver
|
||||
*/
|
||||
private $classMethodVendorLockResolver;
|
||||
|
||||
public function __construct(
|
||||
MethodCallParsedNodesFinder $methodCallParsedNodesFinder,
|
||||
ClassMethodAndCallMatcher $classMethodAndCallMatcher,
|
||||
ClassMethodVendorLockResolver $classMethodVendorLockResolver
|
||||
) {
|
||||
$this->methodCallParsedNodesFinder = $methodCallParsedNodesFinder;
|
||||
$this->classMethodAndCallMatcher = $classMethodAndCallMatcher;
|
||||
$this->classMethodVendorLockResolver = $classMethodVendorLockResolver;
|
||||
}
|
||||
|
||||
public function getDefinition(): RectorDefinition
|
||||
{
|
||||
return new RectorDefinition('Remove unused public method that only calls itself recursively', [
|
||||
new CodeSample(
|
||||
<<<'PHP'
|
||||
class SomeClass
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
return $this->run();
|
||||
}
|
||||
}
|
||||
PHP
|
||||
,
|
||||
<<<'PHP'
|
||||
class SomeClass
|
||||
{
|
||||
}
|
||||
PHP
|
||||
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getNodeTypes(): array
|
||||
{
|
||||
return [ClassMethod::class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ClassMethod $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
$class = $node->getAttribute(AttributeKey::CLASS_NODE);
|
||||
if (! $class instanceof Class_) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (! $this->containsClassMethodAnyCalls($node)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$methodCalls = $this->methodCallParsedNodesFinder->findByClassMethod($node);
|
||||
|
||||
// handles remove dead methods rules
|
||||
if ($methodCalls === []) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach ($methodCalls as $methodCall) {
|
||||
if ($this->shouldSkipCall($node, $methodCall)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
$this->removeNode($node);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param StaticCall|MethodCall|ArrayCallable $methodCall
|
||||
*/
|
||||
private function shouldSkipCall(ClassMethod $classMethod, object $methodCall): bool
|
||||
{
|
||||
if ($this->classMethodVendorLockResolver->isRemovalVendorLocked($classMethod)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (! $methodCall instanceof MethodCall && ! $methodCall instanceof StaticCall) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/** @var string $methodCallMethodName */
|
||||
$methodCallMethodName = $methodCall->getAttribute(AttributeKey::METHOD_NAME);
|
||||
|
||||
// is method called not in itself
|
||||
if (! $this->isName($methodCall->name, $methodCallMethodName)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// differnt class, probably inheritance
|
||||
if ($methodCall->getAttribute(AttributeKey::CLASS_NAME) !== $classMethod->getAttribute(
|
||||
AttributeKey::CLASS_NAME
|
||||
)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return ! $this->classMethodAndCallMatcher->isMethodLikeCallMatchingClassMethod($methodCall, $classMethod);
|
||||
}
|
||||
|
||||
private function containsClassMethodAnyCalls(ClassMethod $classMethod): bool
|
||||
{
|
||||
return $this->betterNodeFinder->hasInstancesOf($classMethod, [MethodCall::class, StaticCall::class]);
|
||||
}
|
||||
}
|
|
@ -79,7 +79,7 @@ PHP
|
|||
return null;
|
||||
}
|
||||
|
||||
$classMethodCalls = $this->methodCallParsedNodesFinder->findClassMethodCalls($node);
|
||||
$classMethodCalls = $this->methodCallParsedNodesFinder->findByClassMethod($node);
|
||||
if ($classMethodCalls !== []) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -178,6 +178,10 @@ final class UnusedClassResolver
|
|||
return $classNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $items
|
||||
* @return string[]
|
||||
*/
|
||||
private function sortAndUniqueArray(array $items): array
|
||||
{
|
||||
sort($items);
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\DeadCode\Tests\Rector\ClassMethod\RemoveDeadRecursiveClassMethodRector\Fixture;
|
||||
|
||||
class SomeClass
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
return $this->run();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\DeadCode\Tests\Rector\ClassMethod\RemoveDeadRecursiveClassMethodRector\Fixture;
|
||||
|
||||
class SomeClass
|
||||
{
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\DeadCode\Tests\Rector\ClassMethod\RemoveDeadRecursiveClassMethodRector\Fixture;
|
||||
|
||||
class PrivateToo
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
return $this->run();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\DeadCode\Tests\Rector\ClassMethod\RemoveDeadRecursiveClassMethodRector\Fixture;
|
||||
|
||||
class PrivateToo
|
||||
{
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\DeadCode\Tests\Rector\ClassMethod\RemoveDeadRecursiveClassMethodRector\Fixture;
|
||||
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
|
||||
class FileRemover
|
||||
{
|
||||
/**
|
||||
* @var SmartFileInfo[]
|
||||
*/
|
||||
private $removedFiles;
|
||||
|
||||
public function removeFile(SmartFileInfo $smartFileInfo): void
|
||||
{
|
||||
$this->removedFiles[$smartFileInfo->getRealPath()] = $smartFileInfo;
|
||||
}
|
||||
}
|
||||
|
||||
abstract class AbstractClassUsingFileRemover
|
||||
{
|
||||
/**
|
||||
* @var FileRemover
|
||||
*/
|
||||
private $removeFile;
|
||||
|
||||
protected function removeFile(SmartFileInfo $smartFileInfo): void
|
||||
{
|
||||
$this->removeFile->removeFile($smartFileInfo);
|
||||
}
|
||||
}
|
||||
|
||||
class ClassExtendingClass extends AbstractClassUsingFileRemover
|
||||
{
|
||||
public function run($smartFileInfo)
|
||||
{
|
||||
$this->removeFile($smartFileInfo);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\DeadCode\Tests\Rector\ClassMethod\RemoveDeadRecursiveClassMethodRector\Fixture;
|
||||
|
||||
interface SkipInterface
|
||||
{
|
||||
public function process();
|
||||
}
|
||||
|
||||
class ImplementerOfTheInterface implements SkipInterface
|
||||
{
|
||||
public function process()
|
||||
{
|
||||
$this->process();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\DeadCode\Tests\Rector\ClassMethod\RemoveDeadRecursiveClassMethodRector\Fixture;
|
||||
|
||||
class SkipUsed
|
||||
{
|
||||
public function run()
|
||||
{
|
||||
return $this->run();
|
||||
}
|
||||
|
||||
public function skipUsed()
|
||||
{
|
||||
$this->run();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\DeadCode\Tests\Rector\ClassMethod\RemoveDeadRecursiveClassMethodRector\Fixture;
|
||||
|
||||
class StaticCall
|
||||
{
|
||||
public static function run()
|
||||
{
|
||||
return self::run();
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\DeadCode\Tests\Rector\ClassMethod\RemoveDeadRecursiveClassMethodRector\Fixture;
|
||||
|
||||
class StaticCall
|
||||
{
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\DeadCode\Tests\Rector\ClassMethod\RemoveDeadRecursiveClassMethodRector;
|
||||
|
||||
use Iterator;
|
||||
use Rector\Core\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
use Rector\DeadCode\Rector\ClassMethod\RemoveDeadRecursiveClassMethodRector;
|
||||
|
||||
final class RemoveDeadRecursiveClassMethodRectorTest extends AbstractRectorTestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideData()
|
||||
*/
|
||||
public function test(string $file): void
|
||||
{
|
||||
$this->doTestFile($file);
|
||||
}
|
||||
|
||||
public function provideData(): Iterator
|
||||
{
|
||||
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
|
||||
}
|
||||
|
||||
protected function getRectorClass(): string
|
||||
{
|
||||
return RemoveDeadRecursiveClassMethodRector::class;
|
||||
}
|
||||
}
|
|
@ -155,7 +155,7 @@ PHP
|
|||
|
||||
private function hasExternalCall(ClassMethod $classMethod): bool
|
||||
{
|
||||
$methodCalls = $this->methodCallParsedNodesFinder->findClassMethodCalls($classMethod);
|
||||
$methodCalls = $this->methodCallParsedNodesFinder->findByClassMethod($classMethod);
|
||||
$methodName = $this->getName($classMethod);
|
||||
|
||||
if ($this->isArrayCallable($classMethod, $methodCalls, $methodName)) {
|
||||
|
|
|
@ -98,7 +98,7 @@ PHP
|
|||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
$classMethodCalls = $this->methodCallParsedNodesFinder->findClassMethodCalls($node);
|
||||
$classMethodCalls = $this->methodCallParsedNodesFinder->findByClassMethod($node);
|
||||
$classParameterTypes = $this->getCallTypesByPosition($classMethodCalls);
|
||||
|
||||
foreach ($classParameterTypes as $position => $argumentStaticType) {
|
||||
|
|
|
@ -25,11 +25,12 @@ final class ExclusionManager
|
|||
|
||||
public function isNodeSkippedByRector(PhpRectorInterface $phpRector, Node $onNode): bool
|
||||
{
|
||||
foreach ($this->exclusionChecks as $check) {
|
||||
if ($check->isNodeSkippedByRector($phpRector, $onNode)) {
|
||||
foreach ($this->exclusionChecks as $exclusionCheck) {
|
||||
if ($exclusionCheck->isNodeSkippedByRector($phpRector, $onNode)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -106,6 +106,23 @@ final class BetterNodeFinder
|
|||
return $this->nodeFinder->findFirstInstanceOf($nodes, $type);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Node|Node[] $nodes
|
||||
* @param string[] $types
|
||||
*/
|
||||
public function hasInstancesOf($nodes, array $types): bool
|
||||
{
|
||||
foreach ($types as $type) {
|
||||
if ($this->nodeFinder->findFirstInstanceOf($nodes, $type) === null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Node|Node[] $nodes
|
||||
*/
|
||||
|
|
|
@ -82,7 +82,7 @@ trait ComplexRemovalTrait
|
|||
{
|
||||
$this->removeNode($classMethod);
|
||||
|
||||
$classMethodCalls = $this->methodCallParsedNodesFinder->findClassMethodCalls($classMethod);
|
||||
$classMethodCalls = $this->methodCallParsedNodesFinder->findByClassMethod($classMethod);
|
||||
foreach ($classMethodCalls as $classMethodCall) {
|
||||
if ($classMethodCall instanceof ArrayCallable) {
|
||||
continue;
|
||||
|
|
Loading…
Reference in New Issue
Block a user