diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingClassMethodRector/Fixture/skip_added_native_types.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingClassMethodRector/Fixture/skip_added_native_types.php.inc deleted file mode 100644 index f9cd0205f43..00000000000 --- a/rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingClassMethodRector/Fixture/skip_added_native_types.php.inc +++ /dev/null @@ -1,13 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingClassMethodRector/Fixture/with_params_and_return.php.inc b/rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingClassMethodRector/Fixture/with_params_and_return.php.inc deleted file mode 100644 index 4f96644a3f8..00000000000 --- a/rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingClassMethodRector/Fixture/with_params_and_return.php.inc +++ /dev/null @@ -1,27 +0,0 @@ - ------ - diff --git a/rules-tests/DeadCode/Rector/Class_/RemoveParentDelegatingClassMethodRector/Fixture/skip_added_native_types.php.inc b/rules-tests/DeadCode/Rector/Class_/RemoveParentDelegatingClassMethodRector/Fixture/skip_added_native_types.php.inc new file mode 100644 index 00000000000..e6057b01e16 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Class_/RemoveParentDelegatingClassMethodRector/Fixture/skip_added_native_types.php.inc @@ -0,0 +1,13 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/Class_/RemoveParentDelegatingClassMethodRector/Fixture/with_params_and_return.php.inc b/rules-tests/DeadCode/Rector/Class_/RemoveParentDelegatingClassMethodRector/Fixture/with_params_and_return.php.inc new file mode 100644 index 00000000000..55cb235e517 --- /dev/null +++ b/rules-tests/DeadCode/Rector/Class_/RemoveParentDelegatingClassMethodRector/Fixture/with_params_and_return.php.inc @@ -0,0 +1,27 @@ + +----- + diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingClassMethodRector/RemoveParentDelegatingClassMethodRectorTest.php b/rules-tests/DeadCode/Rector/Class_/RemoveParentDelegatingClassMethodRector/RemoveParentDelegatingClassMethodRectorTest.php similarity index 87% rename from rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingClassMethodRector/RemoveParentDelegatingClassMethodRectorTest.php rename to rules-tests/DeadCode/Rector/Class_/RemoveParentDelegatingClassMethodRector/RemoveParentDelegatingClassMethodRectorTest.php index 941b97043cf..803dde6d407 100644 --- a/rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingClassMethodRector/RemoveParentDelegatingClassMethodRectorTest.php +++ b/rules-tests/DeadCode/Rector/Class_/RemoveParentDelegatingClassMethodRector/RemoveParentDelegatingClassMethodRectorTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\DeadCode\Rector\ClassMethod\RemoveParentDelegatingClassMethodRector; +namespace Rector\Tests\DeadCode\Rector\Class_\RemoveParentDelegatingClassMethodRector; use Iterator; use PHPUnit\Framework\Attributes\DataProvider; diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingClassMethodRector/Source/SomeParentCommand.php b/rules-tests/DeadCode/Rector/Class_/RemoveParentDelegatingClassMethodRector/Source/SomeParentCommand.php similarity index 75% rename from rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingClassMethodRector/Source/SomeParentCommand.php rename to rules-tests/DeadCode/Rector/Class_/RemoveParentDelegatingClassMethodRector/Source/SomeParentCommand.php index af0cd22e709..9359e1c47b4 100644 --- a/rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingClassMethodRector/Source/SomeParentCommand.php +++ b/rules-tests/DeadCode/Rector/Class_/RemoveParentDelegatingClassMethodRector/Source/SomeParentCommand.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\DeadCode\Rector\ClassMethod\RemoveParentDelegatingClassMethodRector\Source; +namespace Rector\Tests\DeadCode\Rector\Class_\RemoveParentDelegatingClassMethodRector\Source; abstract class SomeParentCommand { diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingClassMethodRector/Source/SomeParentWithConstructor.php b/rules-tests/DeadCode/Rector/Class_/RemoveParentDelegatingClassMethodRector/Source/SomeParentWithConstructor.php similarity index 57% rename from rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingClassMethodRector/Source/SomeParentWithConstructor.php rename to rules-tests/DeadCode/Rector/Class_/RemoveParentDelegatingClassMethodRector/Source/SomeParentWithConstructor.php index 24785290bc2..ecf2b05b1c4 100644 --- a/rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingClassMethodRector/Source/SomeParentWithConstructor.php +++ b/rules-tests/DeadCode/Rector/Class_/RemoveParentDelegatingClassMethodRector/Source/SomeParentWithConstructor.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\DeadCode\Rector\ClassMethod\RemoveParentDelegatingClassMethodRector\Source; +namespace Rector\Tests\DeadCode\Rector\Class_\RemoveParentDelegatingClassMethodRector\Source; abstract class SomeParentWithConstructor { diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingClassMethodRector/Source/SomeParentWithDocblockTypes.php b/rules-tests/DeadCode/Rector/Class_/RemoveParentDelegatingClassMethodRector/Source/SomeParentWithDocblockTypes.php similarity index 68% rename from rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingClassMethodRector/Source/SomeParentWithDocblockTypes.php rename to rules-tests/DeadCode/Rector/Class_/RemoveParentDelegatingClassMethodRector/Source/SomeParentWithDocblockTypes.php index aa2b4c8641e..2ceba86e8d5 100644 --- a/rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingClassMethodRector/Source/SomeParentWithDocblockTypes.php +++ b/rules-tests/DeadCode/Rector/Class_/RemoveParentDelegatingClassMethodRector/Source/SomeParentWithDocblockTypes.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\DeadCode\Rector\ClassMethod\RemoveParentDelegatingClassMethodRector\Source; +namespace Rector\Tests\DeadCode\Rector\Class_\RemoveParentDelegatingClassMethodRector\Source; abstract class SomeParentWithDocblockTypes { diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingClassMethodRector/Source/SomeParentWithPrivateMethod.php b/rules-tests/DeadCode/Rector/Class_/RemoveParentDelegatingClassMethodRector/Source/SomeParentWithPrivateMethod.php similarity index 56% rename from rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingClassMethodRector/Source/SomeParentWithPrivateMethod.php rename to rules-tests/DeadCode/Rector/Class_/RemoveParentDelegatingClassMethodRector/Source/SomeParentWithPrivateMethod.php index ece0bdb646f..cf6e73fdbfc 100644 --- a/rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingClassMethodRector/Source/SomeParentWithPrivateMethod.php +++ b/rules-tests/DeadCode/Rector/Class_/RemoveParentDelegatingClassMethodRector/Source/SomeParentWithPrivateMethod.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\DeadCode\Rector\ClassMethod\RemoveParentDelegatingClassMethodRector\Source; +namespace Rector\Tests\DeadCode\Rector\Class_\RemoveParentDelegatingClassMethodRector\Source; abstract class SomeParentWithPrivateMethod { diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingClassMethodRector/Source/SomeParentWithTwoParams.php b/rules-tests/DeadCode/Rector/Class_/RemoveParentDelegatingClassMethodRector/Source/SomeParentWithTwoParams.php similarity index 64% rename from rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingClassMethodRector/Source/SomeParentWithTwoParams.php rename to rules-tests/DeadCode/Rector/Class_/RemoveParentDelegatingClassMethodRector/Source/SomeParentWithTwoParams.php index 13ebc6b6ba1..f3b50e150e6 100644 --- a/rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingClassMethodRector/Source/SomeParentWithTwoParams.php +++ b/rules-tests/DeadCode/Rector/Class_/RemoveParentDelegatingClassMethodRector/Source/SomeParentWithTwoParams.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Rector\Tests\DeadCode\Rector\ClassMethod\RemoveParentDelegatingClassMethodRector\Source; +namespace Rector\Tests\DeadCode\Rector\Class_\RemoveParentDelegatingClassMethodRector\Source; abstract class SomeParentWithTwoParams { diff --git a/rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingClassMethodRector/config/configured_rule.php b/rules-tests/DeadCode/Rector/Class_/RemoveParentDelegatingClassMethodRector/config/configured_rule.php similarity index 67% rename from rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingClassMethodRector/config/configured_rule.php rename to rules-tests/DeadCode/Rector/Class_/RemoveParentDelegatingClassMethodRector/config/configured_rule.php index b4fb62c6d5d..a5c5a9c967d 100644 --- a/rules-tests/DeadCode/Rector/ClassMethod/RemoveParentDelegatingClassMethodRector/config/configured_rule.php +++ b/rules-tests/DeadCode/Rector/Class_/RemoveParentDelegatingClassMethodRector/config/configured_rule.php @@ -3,7 +3,7 @@ declare(strict_types=1); use Rector\Config\RectorConfig; -use Rector\DeadCode\Rector\ClassMethod\RemoveParentDelegatingClassMethodRector; +use Rector\DeadCode\Rector\Class_\RemoveParentDelegatingClassMethodRector; return RectorConfig::configure() ->withRules([RemoveParentDelegatingClassMethodRector::class]); diff --git a/rules/DeadCode/Rector/ClassMethod/RemoveParentDelegatingClassMethodRector.php b/rules/DeadCode/Rector/Class_/RemoveParentDelegatingClassMethodRector.php similarity index 75% rename from rules/DeadCode/Rector/ClassMethod/RemoveParentDelegatingClassMethodRector.php rename to rules/DeadCode/Rector/Class_/RemoveParentDelegatingClassMethodRector.php index a43d794af75..e27d2e6c16a 100644 --- a/rules/DeadCode/Rector/ClassMethod/RemoveParentDelegatingClassMethodRector.php +++ b/rules/DeadCode/Rector/Class_/RemoveParentDelegatingClassMethodRector.php @@ -2,24 +2,24 @@ declare(strict_types=1); -namespace Rector\DeadCode\Rector\ClassMethod; +namespace Rector\DeadCode\Rector\Class_; use PhpParser\Node; use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Stmt; +use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Expression; use PhpParser\Node\Stmt\Return_; -use PhpParser\NodeVisitor; use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocChildNode; use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode; use PHPStan\Reflection\ClassReflection; use PHPStan\Reflection\ExtendedMethodReflection; use PHPStan\Reflection\ExtendedParameterReflection; +use PHPStan\Reflection\ReflectionProvider; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; use Rector\Enum\ObjectReference; -use Rector\PHPStan\ScopeFetcher; use Rector\PHPStanStaticTypeMapper\Enum\TypeKind; use Rector\Rector\AbstractRector; use Rector\RuleDoc\CodeSample\CodeSample; @@ -28,13 +28,14 @@ use Rector\ValueObject\MethodName; /** - * @see \Rector\Tests\DeadCode\Rector\ClassMethod\RemoveParentDelegatingClassMethodRector\RemoveParentDelegatingClassMethodRectorTest + * @see \Rector\Tests\DeadCode\Rector\Class_\RemoveParentDelegatingClassMethodRector\RemoveParentDelegatingClassMethodRectorTest */ final class RemoveParentDelegatingClassMethodRector extends AbstractRector { public function __construct( private readonly StaticTypeMapper $staticTypeMapper, - private readonly PhpDocInfoFactory $phpDocInfoFactory + private readonly PhpDocInfoFactory $phpDocInfoFactory, + private readonly ReflectionProvider $reflectionProvider ) { } @@ -69,75 +70,101 @@ class SomeCommand extends Command */ public function getNodeTypes(): array { - return [ClassMethod::class]; + return [Class_::class]; } /** - * @param ClassMethod $node + * @param Class_ $node */ - public function refactor(Node $node): ?int + public function refactor(Node $node): ?Node { - // skip constructors, as removing a delegating constructor is risky and usually intentional - if ($this->isName($node, MethodName::CONSTRUCT)) { + $className = $this->getName($node); + if (! is_string($className)) { return null; } - if ($node->isFinal() || $node->isAbstract() || $node->isPrivate() || $node->byRef) { + if (! $this->reflectionProvider->hasClass($className)) { return null; } - if ($node->stmts === null || count($node->stmts) !== 1) { + $classReflection = $this->reflectionProvider->getClass($className); + if (! $classReflection->isClass()) { return null; } - if ($node->attrGroups !== []) { + $parentClassReflection = $classReflection->getParentClass(); + if (! $parentClassReflection instanceof ClassReflection) { return null; } - if (! $this->isParentCallDelegatingParams($node, $node->stmts[0])) { - return null; - } + $hasChanged = false; + foreach ($node->stmts as $key => $stmt) { + if (! $stmt instanceof ClassMethod) { + continue; + } - if ($this->hasRefiningDocblock($node)) { - return null; - } + if (! $this->isDelegatingToParent($stmt, $parentClassReflection)) { + continue; + } - $parentMethodReflection = $this->matchParentMethodReflection($node); - if (! $parentMethodReflection instanceof ExtendedMethodReflection) { - return null; + unset($node->stmts[$key]); + $hasChanged = true; } - if (! $this->isSignatureMatching($node, $parentMethodReflection)) { + if (! $hasChanged) { return null; } - return NodeVisitor::REMOVE_NODE; - } + $node->stmts = array_values($node->stmts); - private function hasRefiningDocblock(ClassMethod $classMethod): bool - { - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod); - return array_any($phpDocInfo->getPhpDocNode()->children, fn (PhpDocChildNode $phpDocChildNode): bool => $phpDocChildNode instanceof PhpDocTagNode); + return $node; } - private function matchParentMethodReflection(ClassMethod $classMethod): ?ExtendedMethodReflection + private function isDelegatingToParent(ClassMethod $classMethod, ClassReflection $parentClassReflection): bool { - $scope = ScopeFetcher::fetch($classMethod); + // skip constructors, as removing a delegating constructor is risky and usually intentional + if ($this->isName($classMethod, MethodName::CONSTRUCT)) { + return false; + } - $classReflection = $scope->getClassReflection(); - if (! $classReflection instanceof ClassReflection) { - return null; + if ($classMethod->isFinal() || $classMethod->isAbstract() || $classMethod->isPrivate() || $classMethod->byRef) { + return false; } - if (! $classReflection->isClass()) { - return null; + if ($classMethod->stmts === null || count($classMethod->stmts) !== 1) { + return false; } - $parentClassReflection = $classReflection->getParentClass(); - if (! $parentClassReflection instanceof ClassReflection) { - return null; + if ($classMethod->attrGroups !== []) { + return false; } + if (! $this->isParentCallDelegatingParams($classMethod, $classMethod->stmts[0])) { + return false; + } + + if ($this->hasRefiningDocblock($classMethod)) { + return false; + } + + $parentMethodReflection = $this->matchParentMethodReflection($classMethod, $parentClassReflection); + if (! $parentMethodReflection instanceof ExtendedMethodReflection) { + return false; + } + + return $this->isSignatureMatching($classMethod, $parentMethodReflection); + } + + private function hasRefiningDocblock(ClassMethod $classMethod): bool + { + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($classMethod); + return array_any($phpDocInfo->getPhpDocNode()->children, fn (PhpDocChildNode $phpDocChildNode): bool => $phpDocChildNode instanceof PhpDocTagNode); + } + + private function matchParentMethodReflection( + ClassMethod $classMethod, + ClassReflection $parentClassReflection + ): ?ExtendedMethodReflection { $methodName = $classMethod->name->toString(); if (! $parentClassReflection->hasNativeMethod($methodName)) { return null; diff --git a/src/Config/Level/DeadCodeLevel.php b/src/Config/Level/DeadCodeLevel.php index 402a6c68327..4456519bc27 100644 --- a/src/Config/Level/DeadCodeLevel.php +++ b/src/Config/Level/DeadCodeLevel.php @@ -18,12 +18,12 @@ use Rector\DeadCode\Rector\Block\ReplaceBlockToItsStmtsRector; use Rector\DeadCode\Rector\BooleanAnd\RemoveAndTrueRector; use Rector\DeadCode\Rector\Cast\RecastingRemovalRector; +use Rector\DeadCode\Rector\Class_\RemoveParentDelegatingClassMethodRector; use Rector\DeadCode\Rector\ClassConst\RemoveUnusedPrivateClassConstantRector; use Rector\DeadCode\Rector\ClassMethod\RemoveArgumentFromDefaultParentCallRector; use Rector\DeadCode\Rector\ClassMethod\RemoveEmptyClassMethodRector; use Rector\DeadCode\Rector\ClassMethod\RemoveMixedDocblockOverruledByNativeTypeRector; use Rector\DeadCode\Rector\ClassMethod\RemoveNullTagValueNodeRector; -use Rector\DeadCode\Rector\ClassMethod\RemoveParentDelegatingClassMethodRector; use Rector\DeadCode\Rector\ClassMethod\RemoveReturnTagIncompatibleWithNativeTypeRector; use Rector\DeadCode\Rector\ClassMethod\RemoveTestsOverriddenPrivateMethodParameterRector; use Rector\DeadCode\Rector\ClassMethod\RemoveUnusedConstructorParamRector;