[Renaming] Skip renaming method call on RenameMethodRector when both old and new exists, which implements interface as config (#2276)

* [Renaming] Skip renaming method call on RenameMethodRector when both old and new exists

* rollback fixture

* verify if caller is a class that implements interface, which both old and new method exists

* [ci-review] Rector Rectify

* clean up

* allow directly get ClassReflection from StaticCall/MethodCall from ReflectionResolver

* clean up

* [ci-review] Rector Rectify

* [ci-review] Rector Rectify

* [ci-review] Rector Rectify

* verify that classlike is different, it means it is a child

* final touch: comment

Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
Abdul Malik Ikhsan 2022-05-11 07:32:41 +07:00 committed by GitHub
parent 1dd739aab6
commit ebd4c3fee6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 88 additions and 4 deletions

View File

@ -0,0 +1,14 @@
<?php
namespace Rector\Tests\Renaming\Rector\MethodCall\RenameMethodRector\Fixture;
use Nette\Utils\Html;
use Rector\Tests\Renaming\Rector\MethodCall\RenameMethodRector\Source\AClass;
class SkipOldNewExistsImplementsInterface
{
public function run(AClass $aClass)
{
$aClass->some_old();
}
}

View File

@ -0,0 +1,14 @@
<?php
namespace Rector\Tests\Renaming\Rector\MethodCall\RenameMethodRector\Source;
final class AClass implements NewInterface
{
public function some_old()
{
}
public function some_new()
{
}
}

View File

@ -0,0 +1,8 @@
<?php
namespace Rector\Tests\Renaming\Rector\MethodCall\RenameMethodRector\Source;
interface NewInterface
{
public function some_new();
}

View File

@ -9,6 +9,7 @@ use Rector\Renaming\ValueObject\MethodCallRenameWithArrayKey;
use Rector\Tests\Renaming\Rector\MethodCall\RenameMethodRector\Source\AbstractType;
use Rector\Tests\Renaming\Rector\MethodCall\RenameMethodRector\Source\CustomType;
use Rector\Tests\Renaming\Rector\MethodCall\RenameMethodRector\Source\Foo;
use Rector\Tests\Renaming\Rector\MethodCall\RenameMethodRector\Source\NewInterface;
use Rector\Tests\Renaming\Rector\MethodCall\RenameMethodRector\Source\SomeSubscriber;
return static function (RectorConfig $rectorConfig): void {
@ -19,6 +20,7 @@ return static function (RectorConfig $rectorConfig): void {
new MethodCallRename(CustomType::class, 'notify', '__invoke'),
new MethodCallRename(SomeSubscriber::class, 'old', 'new'),
new MethodCallRename(Foo::class, 'old', 'new'),
new MethodCallRename(NewInterface::class, 'some_old', 'some_new'),
// with array key
new MethodCallRenameWithArrayKey('Nette\Utils\Html', 'addToArray', 'addToHtmlArray', 'hey'),
]);

View File

@ -12,9 +12,12 @@ use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\ReflectionProvider;
use Rector\Core\Contract\Rector\ConfigurableRectorInterface;
use Rector\Core\NodeManipulator\ClassManipulator;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\Reflection\ReflectionResolver;
use Rector\Renaming\Collector\MethodCallRenameCollector;
use Rector\Renaming\Contract\MethodCallRenameInterface;
use Rector\Renaming\ValueObject\MethodCallRename;
@ -35,7 +38,9 @@ final class RenameMethodRector extends AbstractRector implements ConfigurableRec
public function __construct(
private readonly ClassManipulator $classManipulator,
private readonly MethodCallRenameCollector $methodCallRenameCollector
private readonly MethodCallRenameCollector $methodCallRenameCollector,
private readonly ReflectionResolver $reflectionResolver,
private readonly ReflectionProvider $reflectionProvider
) {
}
@ -124,7 +129,28 @@ CODE_SAMPLE
MethodCallRenameInterface $methodCallRename
): bool {
if (! $node instanceof ClassMethod) {
return false;
$classReflection = $this->reflectionResolver->resolveClassReflection($node);
if (! $classReflection instanceof ClassReflection) {
return false;
}
$targetClass = $methodCallRename->getClass();
if (! $this->reflectionProvider->hasClass($targetClass)) {
return false;
}
$targetClassReflection = $this->reflectionProvider->getClass($targetClass);
if ($classReflection->getName() === $targetClassReflection->getName()) {
return false;
}
// different with configured ClassLike source? it is a child, which may has old and new exists
if (! $classReflection->hasMethod($methodCallRename->getOldMethod())) {
return false;
}
return $classReflection->hasMethod($methodCallRename->getNewMethod());
}
return $this->shouldSkipForAlreadyExistingClassMethod($node, $methodCallRename);

View File

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Rector\Core\Reflection;
use PhpParser\Node;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\New_;
@ -26,15 +27,19 @@ use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Type\TypeUtils;
use PHPStan\Type\TypeWithClassName;
use Rector\Core\NodeAnalyzer\ClassAnalyzer;
use Rector\Core\PhpParser\AstResolver;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
use Rector\Core\PHPStan\Reflection\TypeToCallReflectionResolver\TypeToCallReflectionResolverRegistry;
use Rector\Core\ValueObject\MethodName;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\NodeTypeResolver;
use Symfony\Contracts\Service\Attribute\Required;
final class ReflectionResolver
{
private AstResolver $astResolver;
public function __construct(
private readonly ReflectionProvider $reflectionProvider,
private readonly BetterNodeFinder $betterNodeFinder,
@ -45,6 +50,12 @@ final class ReflectionResolver
) {
}
#[Required]
public function autowire(AstResolver $astResolver): void
{
$this->astResolver = $astResolver;
}
public function resolveClassAndAnonymousClass(ClassLike $classLike): ClassReflection
{
if ($classLike instanceof Class_ && $this->classAnalyzer->isAnonymousClass($classLike)) {
@ -59,9 +70,18 @@ final class ReflectionResolver
}
public function resolveClassReflection(
ClassMethod|Property|ClassLike|New_|Function_|ClassConst $classMethod
ClassMethod|Property|ClassLike|New_|Function_|ClassConst|MethodCall|StaticCall|null $node
): ?ClassReflection {
$scope = $classMethod->getAttribute(AttributeKey::SCOPE);
if (! $node instanceof Node) {
return null;
}
if ($node instanceof MethodCall || $node instanceof StaticCall) {
$classMethod = $this->astResolver->resolveClassMethodFromCall($node);
return $this->resolveClassReflection($classMethod);
}
$scope = $node->getAttribute(AttributeKey::SCOPE);
if (! $scope instanceof Scope) {
return null;