mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-07 03:40:50 +00:00
Allow to rename method if in interface (#2362)
* add test fixture * allow rename method in interface
This commit is contained in:
parent
5d0138dcef
commit
66dbb1e307
|
@ -17,10 +17,10 @@ use PhpParser\Node\Identifier;
|
|||
use PhpParser\Node\Name;
|
||||
use PhpParser\Node\Name\FullyQualified;
|
||||
use PhpParser\Node\NullableType;
|
||||
use PhpParser\Node\Stmt\Class_;
|
||||
use PhpParser\Node\Stmt\Property;
|
||||
use PHPStan\Analyser\Scope;
|
||||
use PHPStan\Broker\ClassAutoloadingException;
|
||||
use PHPStan\Reflection\ClassReflection;
|
||||
use PHPStan\Reflection\ReflectionProvider;
|
||||
use PHPStan\Type\Constant\ConstantBooleanType;
|
||||
use PHPStan\Type\FloatType;
|
||||
|
@ -36,7 +36,6 @@ use PHPStan\Type\TypeWithClassName;
|
|||
use PHPStan\Type\UnionType;
|
||||
use Rector\Core\Configuration\RenamedClassesDataCollector;
|
||||
use Rector\Core\NodeAnalyzer\ClassAnalyzer;
|
||||
use Rector\Core\PhpParser\Node\BetterNodeFinder;
|
||||
use Rector\NodeTypeResolver\Contract\NodeTypeResolverInterface;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\NodeTypeResolver\NodeTypeCorrector\AccessoryNonEmptyStringTypeCorrector;
|
||||
|
@ -66,11 +65,12 @@ final class NodeTypeResolver
|
|||
private readonly AccessoryNonEmptyStringTypeCorrector $accessoryNonEmptyStringTypeCorrector,
|
||||
private readonly IdentifierTypeResolver $identifierTypeResolver,
|
||||
private readonly RenamedClassesDataCollector $renamedClassesDataCollector,
|
||||
private readonly BetterNodeFinder $betterNodeFinder,
|
||||
array $nodeTypeResolvers
|
||||
) {
|
||||
foreach ($nodeTypeResolvers as $nodeTypeResolver) {
|
||||
$this->addNodeTypeResolver($nodeTypeResolver);
|
||||
foreach ($nodeTypeResolver->getNodeClasses() as $nodeClass) {
|
||||
$this->nodeTypeResolvers[$nodeClass] = $nodeTypeResolver;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -285,12 +285,22 @@ final class NodeTypeResolver
|
|||
return $this->isObjectType($node->class, $objectType);
|
||||
}
|
||||
|
||||
$class = $this->betterNodeFinder->findParentType($node, Class_::class);
|
||||
if (! $class instanceof Class_) {
|
||||
$scope = $node->getAttribute(AttributeKey::SCOPE);
|
||||
if (! $scope instanceof Scope) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->isObjectType($class, $objectType);
|
||||
$classReflection = $scope->getClassReflection();
|
||||
|
||||
if (! $classReflection instanceof ClassReflection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($classReflection->getName() === $objectType->getClassName()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $classReflection->isSubclassOf($objectType->getClassName());
|
||||
}
|
||||
|
||||
private function isUnionTypeable(Type $first, Type $second): bool
|
||||
|
@ -298,13 +308,6 @@ final class NodeTypeResolver
|
|||
return ! $first instanceof UnionType && ! $second instanceof UnionType && ! $second instanceof NullType;
|
||||
}
|
||||
|
||||
private function addNodeTypeResolver(NodeTypeResolverInterface $nodeTypeResolver): void
|
||||
{
|
||||
foreach ($nodeTypeResolver->getNodeClasses() as $nodeClass) {
|
||||
$this->nodeTypeResolvers[$nodeClass] = $nodeTypeResolver;
|
||||
}
|
||||
}
|
||||
|
||||
private function isMatchingUnionType(Type $resolvedType, ObjectType $requiredObjectType): bool
|
||||
{
|
||||
$type = TypeCombinator::removeNull($resolvedType);
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\Renaming\Rector\MethodCall\RenameMethodRector\Fixture;
|
||||
|
||||
use Rector\Tests\Renaming\Rector\MethodCall\RenameMethodRector\Source\DifferentInterface;
|
||||
|
||||
interface WhenInterfaceAndParentInterface extends DifferentInterface
|
||||
{
|
||||
public function renameMe(): int;
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Rector\Tests\Renaming\Rector\MethodCall\RenameMethodRector\Fixture;
|
||||
|
||||
use Rector\Tests\Renaming\Rector\MethodCall\RenameMethodRector\Source\DifferentInterface;
|
||||
|
||||
interface WhenInterfaceAndParentInterface extends DifferentInterface
|
||||
{
|
||||
public function toNewVersion(): int;
|
||||
}
|
||||
|
||||
?>
|
|
@ -1,8 +1,10 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\Renaming\Rector\MethodCall\RenameMethodRector\Source;
|
||||
namespace Rector\Tests\Renaming\Rector\MethodCall\RenameMethodRector\Fixture;
|
||||
|
||||
class SkipRenameMethodCall
|
||||
use Rector\Tests\Renaming\Rector\MethodCall\RenameMethodRector\Source\SubscriberInterface;
|
||||
|
||||
final class SkipWhenParentInterface
|
||||
{
|
||||
public static function execute(): void
|
||||
{
|
||||
|
@ -11,12 +13,10 @@ class SkipRenameMethodCall
|
|||
}
|
||||
}
|
||||
|
||||
class SomeSubscriber implements SubscriberInterface
|
||||
final class SomeSubscriber implements SubscriberInterface
|
||||
{
|
||||
public function old(): int
|
||||
{
|
||||
return 5;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\Tests\Renaming\Rector\MethodCall\RenameMethodRector\Source;
|
||||
|
||||
interface DifferentInterface
|
||||
{
|
||||
public function renameMe();
|
||||
}
|
|
@ -8,20 +8,21 @@ use Rector\Renaming\ValueObject\MethodCallRename;
|
|||
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\DifferentInterface;
|
||||
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 {
|
||||
$rectorConfig
|
||||
->ruleWithConfiguration(RenameMethodRector::class, [
|
||||
new MethodCallRename(AbstractType::class, 'setDefaultOptions', 'configureOptions'),
|
||||
new MethodCallRename('Nette\Utils\Html', 'add', 'addHtml'),
|
||||
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'),
|
||||
]);
|
||||
$rectorConfig->ruleWithConfiguration(RenameMethodRector::class, [
|
||||
new MethodCallRename(AbstractType::class, 'setDefaultOptions', 'configureOptions'),
|
||||
new MethodCallRename('Nette\Utils\Html', 'add', 'addHtml'),
|
||||
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'),
|
||||
new MethodCallRename(DifferentInterface::class, 'renameMe', 'toNewVersion'),
|
||||
// with array key
|
||||
new MethodCallRenameWithArrayKey('Nette\Utils\Html', 'addToArray', 'addToHtmlArray', 'hey'),
|
||||
]);
|
||||
};
|
||||
|
|
|
@ -87,7 +87,10 @@ CODE_SAMPLE
|
|||
public function refactor(Node $node): ?Node
|
||||
{
|
||||
foreach ($this->typeToPreference as $type => $preference) {
|
||||
if (! $this->nodeTypeResolver->isMethodStaticCallOrClassMethodObjectType($node, new ObjectType($type))) {
|
||||
if (! $this->nodeTypeResolver->isMethodStaticCallOrClassMethodObjectType(
|
||||
$node,
|
||||
new ObjectType($type)
|
||||
)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,11 +12,12 @@ use PhpParser\Node\Expr\StaticCall;
|
|||
use PhpParser\Node\Identifier;
|
||||
use PhpParser\Node\Stmt\ClassLike;
|
||||
use PhpParser\Node\Stmt\ClassMethod;
|
||||
use PHPStan\Analyser\Scope;
|
||||
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\Rector\AbstractScopeAwareRector;
|
||||
use Rector\Core\Reflection\ReflectionResolver;
|
||||
use Rector\Renaming\Collector\MethodCallRenameCollector;
|
||||
use Rector\Renaming\Contract\MethodCallRenameInterface;
|
||||
|
@ -29,7 +30,7 @@ use Webmozart\Assert\Assert;
|
|||
/**
|
||||
* @see \Rector\Tests\Renaming\Rector\MethodCall\RenameMethodRector\RenameMethodRectorTest
|
||||
*/
|
||||
final class RenameMethodRector extends AbstractRector implements ConfigurableRectorInterface
|
||||
final class RenameMethodRector extends AbstractScopeAwareRector implements ConfigurableRectorInterface
|
||||
{
|
||||
/**
|
||||
* @var MethodCallRenameInterface[]
|
||||
|
@ -74,15 +75,16 @@ CODE_SAMPLE
|
|||
/**
|
||||
* @param MethodCall|StaticCall|ClassMethod $node
|
||||
*/
|
||||
public function refactor(Node $node): ?Node
|
||||
public function refactorWithScope(Node $node, Scope $scope): ?Node
|
||||
{
|
||||
$classReflection = $scope->getClassReflection();
|
||||
|
||||
foreach ($this->methodCallRenames as $methodCallRename) {
|
||||
$implementsInterface = $this->classManipulator->hasParentMethodOrInterface(
|
||||
$methodCallRename->getObjectType(),
|
||||
$methodCallRename->getOldMethod(),
|
||||
$methodCallRename->getNewMethod()
|
||||
);
|
||||
if ($implementsInterface) {
|
||||
if (! $this->isName($node->name, $methodCallRename->getOldMethod())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($this->shouldKeepForParentInterface($methodCallRename, $node, $classReflection)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -93,10 +95,6 @@ CODE_SAMPLE
|
|||
continue;
|
||||
}
|
||||
|
||||
if (! $this->isName($node->name, $methodCallRename->getOldMethod())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($this->shouldSkipClassMethod($node, $methodCallRename)) {
|
||||
continue;
|
||||
}
|
||||
|
@ -167,4 +165,29 @@ CODE_SAMPLE
|
|||
|
||||
return (bool) $classLike->getMethod($methodCallRename->getNewMethod());
|
||||
}
|
||||
|
||||
private function shouldKeepForParentInterface(
|
||||
MethodCallRenameInterface $methodCallRename,
|
||||
ClassMethod|StaticCall|MethodCall $node,
|
||||
?ClassReflection $classReflection
|
||||
): bool {
|
||||
if (! $node instanceof ClassMethod) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! $classReflection instanceof ClassReflection) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// interface can change current method, as parent contract is still valid
|
||||
if (! $classReflection->isInterface()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->classManipulator->hasParentMethodOrInterface(
|
||||
$methodCallRename->getObjectType(),
|
||||
$methodCallRename->getOldMethod(),
|
||||
$methodCallRename->getNewMethod()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user