diff --git a/packages/NodeTypeResolver/PhpDocNodeVisitor/ClassRenamePhpDocNodeVisitor.php b/packages/NodeTypeResolver/PhpDocNodeVisitor/ClassRenamePhpDocNodeVisitor.php index 905392571aa..a4cb2d395f1 100644 --- a/packages/NodeTypeResolver/PhpDocNodeVisitor/ClassRenamePhpDocNodeVisitor.php +++ b/packages/NodeTypeResolver/PhpDocNodeVisitor/ClassRenamePhpDocNodeVisitor.php @@ -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; + } } diff --git a/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/skip_docblock_rename_different_namespace.php.inc b/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/skip_docblock_rename_different_namespace.php.inc new file mode 100644 index 00000000000..f464cbc294e --- /dev/null +++ b/rules-tests/Renaming/Rector/Name/RenameClassRector/Fixture/skip_docblock_rename_different_namespace.php.inc @@ -0,0 +1,16 @@ + SomeFinalClass::class, 'Foo\Bar' => 'Foo\Bar\BarInterface', 'Doctrine\DBAL\DBALException' => 'Doctrine\DBAL\Exception', + 'Bar' => 'BarInterface', ]); };