fix RenameClassRector to change only direct class names, not children [closes #1514]

This commit is contained in:
Tomas Votruba 2019-05-31 19:46:43 +03:00
parent a69d08bac5
commit 6854f3ef1e
4 changed files with 54 additions and 20 deletions

View File

@ -38,7 +38,6 @@ services:
Psr\SimpleCache\CacheInterface: 'Symfony\Contracts\Cache\CacheInterface'
# EventDispatcher
Symfony\Component\EventDispatcher\Event: 'Symfony\Contracts\EventDispatcher\Event'
Symfony\Component\HttpKernel\Event\FilterControllerArgumentsEvent: 'Symfony\Component\HttpKernel\Event\ControllerArgumentsEvent'
Symfony\Component\HttpKernel\Event\FilterControllerEvent: 'Symfony\Component\HttpKernel\Event\ControllerArgumentsEvent'
Symfony\Component\HttpKernel\Event\FilterResponseEvent: 'Symfony\Component\HttpKernel\Event\ResponseEvent'
@ -46,6 +45,8 @@ services:
Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent: 'Symfony\Component\HttpKernel\Event\ViewEvent'
Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent: 'Symfony\Component\HttpKernel\Event\ExceptionEvent'
Symfony\Component\HttpKernel\Event\PostResponseEvent: 'Symfony\Component\HttpKernel\Event\TerminateEvent'
# has lowest priority, have to be last
Symfony\Component\EventDispatcher\Event: 'Symfony\Contracts\EventDispatcher\Event'
# MimeType
Symfony\Component\HttpFoundation\File\MimeType\MimeTypeGuesserInterface: 'Symfony\Component\Mime\MimeTypesInterface'

View File

@ -87,7 +87,7 @@ CODE_SAMPLE
[$oldType, $newType] = $match;
$this->docBlockManipulator->changeType($property, $oldType, $newType);
$this->docBlockManipulator->changeTypeIncludingChildren($property, $oldType, $newType);
}
private function refactorNullableType(NullableType $nullableType): void
@ -123,7 +123,7 @@ CODE_SAMPLE
return;
}
$this->docBlockManipulator->changeType($node, $oldType, $newType);
$this->docBlockManipulator->changeTypeIncludingChildren($node, $oldType, $newType);
}
private function processParamNode(NullableType $nullableType, Param $param, string $newType): void
@ -135,6 +135,6 @@ CODE_SAMPLE
$oldType = $this->namespaceAnalyzer->resolveTypeToFullyQualified((string) $nullableType->type, $nullableType);
$this->docBlockManipulator->changeType($classMethodNode, $oldType, $newType);
$this->docBlockManipulator->changeTypeIncludingChildren($classMethodNode, $oldType, $newType);
}
}

View File

@ -5,7 +5,6 @@ namespace Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer;
use Nette\Utils\Strings;
use PhpParser\Comment\Doc;
use PhpParser\Node;
use PhpParser\Node\Name;
use PHPStan\PhpDocParser\Ast\PhpDoc\InvalidTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocChildNode;
@ -158,19 +157,24 @@ final class DocBlockManipulator
$this->updateNodeWithPhpDocInfo($node, $phpDocInfo);
}
public function changeType(Node $node, string $oldType, string $newType): void
public function changeType(Node $node, string $oldType, string $newType, bool $includeChildren = false): void
{
if ($node->getDocComment() === null) {
if (! $this->hasNodeTypeChangeableTags($node)) {
return;
}
$phpDocInfo = $this->createPhpDocInfoFromNode($node);
$this->replacePhpDocTypeByAnother($phpDocInfo->getPhpDocNode(), $oldType, $newType, $node);
$this->replacePhpDocTypeByAnother($phpDocInfo->getPhpDocNode(), $oldType, $newType, $node, $includeChildren);
$this->updateNodeWithPhpDocInfo($node, $phpDocInfo);
}
public function changeTypeIncludingChildren(Node $node, string $oldType, string $newType): void
{
$this->changeType($node, $oldType, $newType, true);
}
public function replaceAnnotationInNode(Node $node, string $oldAnnotation, string $newAnnotation): void
{
if ($node->getDocComment() === null) {
@ -378,7 +382,8 @@ final class DocBlockManipulator
AttributeAwarePhpDocNode $attributeAwarePhpDocNode,
string $oldType,
string $newType,
Node $node
Node $node,
bool $includeChildren = false
): AttributeAwarePhpDocNode {
foreach ($attributeAwarePhpDocNode->children as $phpDocChildNode) {
if (! $phpDocChildNode instanceof PhpDocTagNode) {
@ -392,7 +397,12 @@ final class DocBlockManipulator
/** @var VarTagValueNode|ParamTagValueNode|ReturnTagValueNode $tagValueNode */
$tagValueNode = $phpDocChildNode->value;
$phpDocChildNode->value->type = $this->replaceTypeNode($tagValueNode->type, $oldType, $newType);
$phpDocChildNode->value->type = $this->replaceTypeNode(
$tagValueNode->type,
$oldType,
$newType,
$includeChildren
);
$this->stringsTypePhpDocNodeDecorator->decorate($attributeAwarePhpDocNode, $node);
}
@ -481,6 +491,21 @@ final class DocBlockManipulator
$this->importedNames = [];
}
/**
* For better performance
*/
public function hasNodeTypeChangeableTags(Node $node): bool
{
$docComment = $node->getDocComment();
if ($docComment === null) {
return false;
}
$text = $docComment->getText();
return (bool) Strings::match($text, '#\@(param|throws|return|var)\b#');
}
private function addTypeSpecificTag(Node $node, string $name, string $type): void
{
// there might be no phpdoc at all
@ -504,26 +529,28 @@ final class DocBlockManipulator
}
}
private function updateNodeWithPhpDocInfo(Node $node, PhpDocInfo $phpDocInfo): void
private function updateNodeWithPhpDocInfo(Node $node, PhpDocInfo $phpDocInfo): bool
{
// skip if has no doc comment
if ($node->getDocComment() === null) {
return;
return false;
}
$phpDoc = $this->phpDocInfoPrinter->printFormatPreserving($phpDocInfo);
if ($phpDoc !== '') {
// no change, don't save it
if ($node->getDocComment()->getText() === $phpDoc) {
return;
return false;
}
$node->setDocComment(new Doc($phpDoc));
return;
return true;
}
// no comments, null
$node->setAttribute('comments', null);
return true;
}
private function createPhpDocInfoFromNode(Node $node): PhpDocInfo
@ -546,14 +573,20 @@ final class DocBlockManipulator
$phpDocTagNode->value instanceof ThrowsTagValueNode;
}
private function replaceTypeNode(TypeNode $typeNode, string $oldType, string $newType): TypeNode
{
private function replaceTypeNode(
TypeNode $typeNode,
string $oldType,
string $newType,
bool $includeChildren = false
): TypeNode {
// @todo use $this->nodeTraverser->traverseWithCallable here matching "AttributeAwareIdentifierTypeNode"
if ($typeNode instanceof AttributeAwareIdentifierTypeNode) {
$nodeType = $this->resolveNodeType($typeNode);
if (is_a($nodeType, $oldType, true) || ltrim($nodeType, '\\') === $oldType) {
// by default do not override subtypes, can actually use parent type (race condition), which is not desired
// see: $includeChildren
if (($includeChildren && is_a($nodeType, $oldType, true)) || ltrim($nodeType, '\\') === $oldType) {
$newType = $this->forceFqnPrefix($newType);
return new AttributeAwareIdentifierTypeNode($newType);
@ -562,12 +595,12 @@ final class DocBlockManipulator
if ($typeNode instanceof UnionTypeNode) {
foreach ($typeNode->types as $key => $subTypeNode) {
$typeNode->types[$key] = $this->replaceTypeNode($subTypeNode, $oldType, $newType);
$typeNode->types[$key] = $this->replaceTypeNode($subTypeNode, $oldType, $newType, $includeChildren);
}
}
if ($typeNode instanceof ArrayTypeNode) {
$typeNode->type = $this->replaceTypeNode($typeNode->type, $oldType, $newType);
$typeNode->type = $this->replaceTypeNode($typeNode->type, $oldType, $newType, $includeChildren);
return $typeNode;
}

View File

@ -115,7 +115,7 @@ CODE_SAMPLE
public function refactor(Node $node): ?Node
{
// replace on @var/@param/@return/@throws
if ($node->getDocComment()) {
if ($this->docBlockManipulator->hasNodeTypeChangeableTags($node)) {
foreach ($this->oldToNewClasses as $oldClass => $newClass) {
$this->docBlockManipulator->changeType($node, $oldClass, $newClass);
}