[Renaming] Apply rename fully qualified namespace docblock on RenameNamespaceRector (#1917)

This commit is contained in:
Abdul Malik Ikhsan 2022-03-10 17:01:23 +07:00 committed by GitHub
parent 84f7ebde04
commit 888f483102
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 301 additions and 4 deletions

View File

@ -18,7 +18,7 @@
"helmich/typo3-typoscript-parser": "^2.4.1",
"idiosyncratic/editorconfig": "^0.1.3",
"myclabs/php-enum": "^1.8",
"nette/neon": "^3.3.2",
"nette/neon": "3.3.2",
"nette/utils": "^3.2.7",
"nikic/php-parser": "^4.13.2",
"ondram/ci-detector": "^4.1",

View File

@ -0,0 +1,119 @@
<?php
declare(strict_types=1);
namespace Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer;
use PhpParser\Node;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Expression;
use PhpParser\Node\Stmt\Function_;
use PhpParser\Node\Stmt\Property;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory;
use Rector\BetterPhpDocParser\ValueObject\PhpDoc\VariadicAwareParamTagValueNode;
use Rector\BetterPhpDocParser\ValueObject\Type\BracketsAwareUnionTypeNode;
use Rector\Core\PhpParser\Node\CustomNode\FileWithoutNamespace;
use Rector\Naming\NamespaceMatcher;
use Rector\Renaming\ValueObject\RenamedNamespace;
final class DocBlockNamespaceRenamer
{
/**
* @var array<class-string<PhpDocTagValueNode>>
*/
private const TO_BE_CHANGED = [
ReturnTagValueNode::class,
VariadicAwareParamTagValueNode::class,
VarTagValueNode::class,
];
public function __construct(
private readonly NamespaceMatcher $namespaceMatcher,
private readonly PhpDocInfoFactory $phpDocInfoFactory
) {
}
/**
* @param array<string, string> $oldToNewNamespaces
*/
public function renameFullyQualifiedNamespace(
Property|ClassMethod|Function_|Expression|ClassLike|FileWithoutNamespace $node,
array $oldToNewNamespaces
): ?Node {
$phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node);
$phpDocNode = $phpDocInfo->getPhpDocNode();
$children = $phpDocNode->children;
foreach ($children as $child) {
if (! $child instanceof PhpDocTagNode) {
continue;
}
$value = $child->value;
if (! in_array($value::class, self::TO_BE_CHANGED, true)) {
continue;
}
/** @var ReturnTagValueNode|VariadicAwareParamTagValueNode|VarTagValueNode $value */
$this->refactorDocblock($value, $oldToNewNamespaces);
}
if (! $phpDocInfo->hasChanged()) {
return null;
}
return $node;
}
/**
* @param array<string, string> $oldToNewNamespaces
*/
private function refactorDocblock(
ReturnTagValueNode|VariadicAwareParamTagValueNode|VarTagValueNode $value,
array $oldToNewNamespaces
): void {
/** @var ReturnTagValueNode|VariadicAwareParamTagValueNode|VarTagValueNode $value */
$types = $value->type instanceof BracketsAwareUnionTypeNode
? $value->type->types
: [$value->type];
foreach ($types as $key => $type) {
if (! $type instanceof IdentifierTypeNode) {
continue;
}
$name = $type->name;
$trimmedName = ltrim($type->name, '\\');
if ($name === $trimmedName) {
continue;
}
$renamedNamespaceValueObject = $this->namespaceMatcher->matchRenamedNamespace(
$trimmedName,
$oldToNewNamespaces
);
if (! $renamedNamespaceValueObject instanceof RenamedNamespace) {
continue;
}
$newType = new IdentifierTypeNode('\\' . $renamedNamespaceValueObject->getNameInNewNamespace());
if ($value->type instanceof BracketsAwareUnionTypeNode) {
$types[$key] = $newType;
} else {
$value->type = $newType;
}
}
if ($value->type instanceof BracketsAwareUnionTypeNode) {
$value->type->types = $types;
}
}
}

View File

@ -0,0 +1,37 @@
<?php
namespace OldNamespace\SubNamespace;
use OldNamespace;
class RenameNamespaceDocblockParam
{
/**
* @param \OldNamespace\SubNamespace\RenameNamespaceDocblockParam $argument
*/
public function run(\OldNamespace\SubNamespace\RenameNamespaceDocblockParam $argument)
{
return $argument;
}
}
?>
-----
<?php
namespace NewNamespace\SubNamespace;
use NewNamespace;
class RenameNamespaceDocblockParam
{
/**
* @param \NewNamespace\SubNamespace\RenameNamespaceDocblockParam $argument
*/
public function run(\NewNamespace\SubNamespace\RenameNamespaceDocblockParam $argument)
{
return $argument;
}
}
?>

View File

@ -0,0 +1,37 @@
<?php
namespace OldNamespace\SubNamespace;
use OldNamespace;
class RenameNamespaceDocblockReturn
{
/**
* @return \OldNamespace\SubNamespace\RenameNamespaceDocblockReturn
*/
public function run(\OldNamespace\SubNamespace\RenameNamespaceDocblockReturn $argument)
{
return $argument;
}
}
?>
-----
<?php
namespace NewNamespace\SubNamespace;
use NewNamespace;
class RenameNamespaceDocblockReturn
{
/**
* @return \NewNamespace\SubNamespace\RenameNamespaceDocblockReturn
*/
public function run(\NewNamespace\SubNamespace\RenameNamespaceDocblockReturn $argument)
{
return $argument;
}
}
?>

View File

@ -0,0 +1,39 @@
<?php
namespace OldNamespace\SubNamespace;
use OldNamespace;
class RenameNamespaceDocblockUnion
{
public function run(?\OldNamespace\SubNamespace\RenameNamespaceDocblockUnion $argument)
{
/**
* @var \OldNamespace\SubNamespace\RenameNamespaceDocblockUnion|null $argument
*/
$var = $argument;
return $var;
}
}
?>
-----
<?php
namespace NewNamespace\SubNamespace;
use NewNamespace;
class RenameNamespaceDocblockUnion
{
public function run(?\NewNamespace\SubNamespace\RenameNamespaceDocblockUnion $argument)
{
/**
* @var \NewNamespace\SubNamespace\RenameNamespaceDocblockUnion|null $argument
*/
$var = $argument;
return $var;
}
}
?>

View File

@ -0,0 +1,39 @@
<?php
namespace OldNamespace\SubNamespace;
use OldNamespace;
class RenameNamespaceDocblockVar
{
public function run(\OldNamespace\SubNamespace\RenameNamespaceDocblockVar $argument)
{
/**
* @var \OldNamespace\SubNamespace\RenameNamespaceDocblockVar $argument
*/
$var = $argument;
return $var;
}
}
?>
-----
<?php
namespace NewNamespace\SubNamespace;
use NewNamespace;
class RenameNamespaceDocblockVar
{
public function run(\NewNamespace\SubNamespace\RenameNamespaceDocblockVar $argument)
{
/**
* @var \NewNamespace\SubNamespace\RenameNamespaceDocblockVar $argument
*/
$var = $argument;
return $var;
}
}
?>

View File

@ -8,13 +8,20 @@ use PhpParser\Node;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Expression;
use PhpParser\Node\Stmt\Function_;
use PhpParser\Node\Stmt\Namespace_;
use PhpParser\Node\Stmt\Property;
use PhpParser\Node\Stmt\Use_;
use PhpParser\Node\Stmt\UseUse;
use Rector\Core\Contract\Rector\ConfigurableRectorInterface;
use Rector\Core\PhpParser\Node\CustomNode\FileWithoutNamespace;
use Rector\Core\Rector\AbstractRector;
use Rector\Naming\NamespaceMatcher;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockNamespaceRenamer;
use Rector\Renaming\ValueObject\RenamedNamespace;
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
@ -25,6 +32,18 @@ use Webmozart\Assert\Assert;
*/
final class RenameNamespaceRector extends AbstractRector implements ConfigurableRectorInterface
{
/**
* @var array<class-string<Node>>
*/
private const ONLY_CHANGE_DOCBLOCK_NODE = [
Property::class,
ClassMethod::class,
Function_::class,
Expression::class,
ClassLike::class,
FileWithoutNamespace::class,
];
/**
* @var array<string, string>
*/
@ -36,7 +55,8 @@ final class RenameNamespaceRector extends AbstractRector implements Configurable
private array $isChangedInNamespaces = [];
public function __construct(
private readonly NamespaceMatcher $namespaceMatcher
private readonly NamespaceMatcher $namespaceMatcher,
private readonly DocBlockNamespaceRenamer $docBlockNamespaceRenamer
) {
}
@ -58,14 +78,20 @@ final class RenameNamespaceRector extends AbstractRector implements Configurable
*/
public function getNodeTypes(): array
{
return [Namespace_::class, Use_::class, Name::class];
return [Namespace_::class, Use_::class, Name::class, ...self::ONLY_CHANGE_DOCBLOCK_NODE];
}
/**
* @param Namespace_|Use_|Name $node
* @param Namespace_|Use_|Name|Property|ClassMethod|Function_|Expression|ClassLike|FileWithoutNamespace $node
*/
public function refactor(Node $node): ?Node
{
if (in_array($node::class, self::ONLY_CHANGE_DOCBLOCK_NODE, true)) {
/** @var Property|ClassMethod|Function_|Expression|ClassLike|FileWithoutNamespace $node */
return $this->docBlockNamespaceRenamer->renameFullyQualifiedNamespace($node, $this->oldToNewNamespaces);
}
/** @var Namespace_|Use_|Name $node */
$name = $this->getName($node);
if ($name === null) {
return null;