[Renaming] Skip docblock rename different namespace on RenameClassRector (#2419)

* [Renaming] Skip docblock rename different namespace on RenameClassRector

* debug

* [ci-review] Rector Rectify

* debug

* [ci-review] Rector Rectify

* \ between namespace and name

* up

* debug

* debug

* check no namespace

* up

* Fixed 🎉

* final touch: add GroupUse_ support

* final touch: add GroupUse_ support

* final touch: clean up

* really final touch: clean up

* final touch: ensure no subnamespace check as check against getLast()

* final touch: remove unnneeded string cast

Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
Abdul Malik Ikhsan 2022-06-04 14:31:58 +07:00 committed by GitHub
parent e8f058a6e4
commit 283a9af3f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 137 additions and 2 deletions

View File

@ -4,6 +4,11 @@ declare(strict_types=1);
namespace Rector\NodeTypeResolver\PhpDocNodeVisitor;
use PhpParser\Node as PhpParserNode;
use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt\GroupUse;
use PhpParser\Node\Stmt\Namespace_;
use PhpParser\Node\Stmt\Use_;
use PHPStan\PhpDocParser\Ast\Node;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
@ -11,6 +16,11 @@ use PHPStan\Type\ObjectType;
use Rector\BetterPhpDocParser\ValueObject\PhpDocAttributeKey;
use Rector\Core\Configuration\CurrentNodeProvider;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\PhpParser\Comparing\NodeComparator;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
use Rector\Naming\Naming\UseImportsResolver;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\ValueObject\OldToNewType;
use Rector\PHPStanStaticTypeMapper\Enum\TypeKind;
use Rector\StaticTypeMapper\StaticTypeMapper;
@ -26,7 +36,11 @@ final class ClassRenamePhpDocNodeVisitor extends AbstractPhpDocNodeVisitor
public function __construct(
private readonly StaticTypeMapper $staticTypeMapper,
private readonly CurrentNodeProvider $currentNodeProvider
private readonly CurrentNodeProvider $currentNodeProvider,
private readonly UseImportsResolver $useImportsResolver,
private readonly BetterNodeFinder $betterNodeFinder,
private readonly NodeNameResolver $nodeNameResolver,
private readonly NodeComparator $nodeComparator
) {
}
@ -48,7 +62,11 @@ final class ClassRenamePhpDocNodeVisitor extends AbstractPhpDocNodeVisitor
throw new ShouldNotHappenException();
}
$staticType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType($node, $phpParserNode);
$identifier = clone $node;
$namespacedName = $this->resolveNamespacedName($phpParserNode, $identifier->name);
$identifier->name = $namespacedName;
$staticType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType($identifier, $phpParserNode);
// make sure to compare FQNs
if ($staticType instanceof ShortenedObjectType) {
@ -84,4 +102,59 @@ final class ClassRenamePhpDocNodeVisitor extends AbstractPhpDocNodeVisitor
{
$this->oldToNewTypes = $oldToNewTypes;
}
private function resolveNamespacedName(PhpParserNode $phpParserNode, string $name): string
{
if (str_starts_with($name, '\\')) {
return $name;
}
if (str_contains($name, '\\')) {
return $name;
}
$namespace = $this->betterNodeFinder->findParentType($phpParserNode, Namespace_::class);
$uses = $this->useImportsResolver->resolveForNode($phpParserNode);
if (! $namespace instanceof Namespace_) {
return $this->resolveNamefromUse($uses, $name);
}
$originalNode = $namespace->getAttribute(AttributeKey::ORIGINAL_NODE);
if (! $this->nodeComparator->areNodesEqual($originalNode, $namespace)) {
return $name;
}
if ($uses === []) {
$namespaceName = $this->nodeNameResolver->getName($namespace);
return $namespaceName . '\\' . $name;
}
return $this->resolveNamefromUse($uses, $name);
}
/**
* @param Use_[]|GroupUse[] $uses
*/
private function resolveNamefromUse(array $uses, string $name): string
{
foreach ($uses as $use) {
$prefix = $use instanceof GroupUse
? $use->prefix . '\\'
: '';
foreach ($use->uses as $useUse) {
if ($useUse->alias instanceof Identifier) {
continue;
}
$lastName = $useUse->name->getLast();
if ($lastName === $name) {
return $prefix . $useUse->name->toString();
}
}
}
return $name;
}
}

View File

@ -0,0 +1,16 @@
<?php
/**
* Future note: Not inside namespace on purpose to check last name Use_ used
*/
use \Company2\Bar;
final class SkipDocblockRenameDifferentNamespace
{
/**
* @param Bar $foo
*/
public function importedClassesReferencedViaAlias($foo)
{
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace Rector\Tests\Renaming\Rector\Name\RenameClassRector\Fixture;
final class SkipDocblockRenameDifferentNamespace2
{
/**
* @param Bar $foo
*/
public function importedClassViaSameNamespace($foo)
{
}
}

View File

@ -0,0 +1,15 @@
<?php
namespace Rector\Tests\Renaming\Rector\Name\RenameClassRector\Fixture;
use \Company2\{ Bar };
final class SkipDocblockRenameDifferentNamespace3
{
/**
* @param Bar $foo
*/
public function importedClassViaSameNamespace($foo)
{
}
}

View File

@ -0,0 +1,17 @@
<?php
/**
* Future note: Not inside namespace on purpose to check last name GroupUse_ used
*/
use \Company2\{ Bar };
final class SkipDocblockRenameDifferentNamespace4
{
/**
* @param Bar $foo
*/
public function importedClassViaSameNamespace($foo)
{
}
}

View File

@ -45,5 +45,6 @@ return static function (RectorConfig $rectorConfig): void {
SomeNonFinalClass::class => SomeFinalClass::class,
'Foo\Bar' => 'Foo\Bar\BarInterface',
'Doctrine\DBAL\DBALException' => 'Doctrine\DBAL\Exception',
'Bar' => 'BarInterface',
]);
};