mirror of
https://github.com/rectorphp/rector.git
synced 2024-05-28 23:10:51 +00:00
Fix doc importing on Doctrine annotation class rename (#264)
Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
parent
a8ccfeb3d5
commit
a41384bc00
|
@ -92,8 +92,8 @@ if (! $entityTagValueNode instanceof DoctrineAnnotationTagValueNode) {
|
|||
return null;
|
||||
}
|
||||
|
||||
$annotationClass = $entityTagValueNode->getAnnotationClass();
|
||||
var_dump($annotationClass); // "Doctrine\ORM\Mapping\Entity"
|
||||
$annotationClass = $entityTagValueNode->identifierTypeNode;
|
||||
var_dump($annotationClass); // \PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode("Doctrine\ORM\Mapping\Entity")
|
||||
|
||||
$values = $entityTagValueNode->getValues();
|
||||
var_dump($values); // []
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
"rector/rector-nette": "^0.11.4",
|
||||
"rector/rector-nette-to-symfony": "^0.11.3",
|
||||
"rector/rector-phpunit": "^0.11.1",
|
||||
"rector/rector-symfony": "^0.11.5",
|
||||
"rector/rector-symfony": "^0.11.6",
|
||||
"sebastian/diff": "^4.0.4",
|
||||
"ssch/typo3-rector": "^0.11.14",
|
||||
"symfony/console": "^5.3",
|
||||
|
@ -55,7 +55,7 @@
|
|||
"phpstan/phpstan-nette": "^0.12.19",
|
||||
"phpunit/phpunit": "^9.5",
|
||||
"rector/rector-generator": "^0.1.7",
|
||||
"rector/phpstan-rules": "^0.3",
|
||||
"rector/phpstan-rules": "^0.3.3",
|
||||
"symplify/coding-standard": "^9.3.22",
|
||||
"symplify/easy-ci": "^9.3.22",
|
||||
"symplify/easy-coding-standard": "^9.3.22",
|
||||
|
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace Rector\BetterPhpDocParser\PhpDoc;
|
||||
|
||||
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
|
||||
use Rector\BetterPhpDocParser\ValueObject\PhpDoc\DoctrineAnnotation\AbstractValuesAwareNode;
|
||||
use Stringable;
|
||||
|
||||
|
@ -13,7 +14,7 @@ final class DoctrineAnnotationTagValueNode extends AbstractValuesAwareNode imple
|
|||
* @param array<mixed, mixed> $values
|
||||
*/
|
||||
public function __construct(
|
||||
private string $annotationClass,
|
||||
public IdentifierTypeNode $identifierTypeNode,
|
||||
?string $originalContent = null,
|
||||
array $values = [],
|
||||
?string $silentKey = null
|
||||
|
@ -46,8 +47,8 @@ final class DoctrineAnnotationTagValueNode extends AbstractValuesAwareNode imple
|
|||
return sprintf('(%s)', $itemContents);
|
||||
}
|
||||
|
||||
public function getAnnotationClass(): string
|
||||
public function hasClassName(string $className): bool
|
||||
{
|
||||
return $this->annotationClass;
|
||||
return $this->identifierTypeNode->name === $className;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -258,13 +258,13 @@ final class PhpDocInfo
|
|||
|
||||
$doctrineAnnotationTagValueNode = $phpDocChildNode->value;
|
||||
|
||||
$annotationClass = $doctrineAnnotationTagValueNode->getAnnotationClass();
|
||||
if ($annotationClass === $desiredClass) {
|
||||
if ($doctrineAnnotationTagValueNode->hasClassName($desiredClass)) {
|
||||
return $doctrineAnnotationTagValueNode;
|
||||
}
|
||||
|
||||
// fnmatch
|
||||
if ($this->isFnmatch($annotationClass, $desiredClass)) {
|
||||
$identifierTypeNode = $doctrineAnnotationTagValueNode->identifierTypeNode;
|
||||
if ($this->isFnmatch($identifierTypeNode->name, $desiredClass)) {
|
||||
return $doctrineAnnotationTagValueNode;
|
||||
}
|
||||
}
|
||||
|
@ -348,7 +348,7 @@ final class PhpDocInfo
|
|||
{
|
||||
if ($phpDocTagValueNode instanceof DoctrineAnnotationTagValueNode) {
|
||||
$spacelessPhpDocTagNode = new SpacelessPhpDocTagNode(
|
||||
'@\\' . $phpDocTagValueNode->getAnnotationClass(),
|
||||
'@\\' . $phpDocTagValueNode->identifierTypeNode,
|
||||
$phpDocTagValueNode
|
||||
);
|
||||
$this->addPhpDocTagNode($spacelessPhpDocTagNode);
|
||||
|
|
|
@ -122,11 +122,9 @@ final class PhpDocClassRenamer
|
|||
Node $node,
|
||||
array $oldToNewClasses
|
||||
): void {
|
||||
if ($doctrineAnnotationTagValueNode->getAnnotationClass() === 'Doctrine\ORM\Mapping\Embedded') {
|
||||
$classKey = 'class';
|
||||
} else {
|
||||
$classKey = 'targetEntity';
|
||||
}
|
||||
$classKey = $doctrineAnnotationTagValueNode->hasClassName(
|
||||
'Doctrine\ORM\Mapping\Embedded'
|
||||
) ? 'class' : 'targetEntity';
|
||||
|
||||
$targetEntity = $doctrineAnnotationTagValueNode->getValueWithoutQuotes($classKey);
|
||||
if ($targetEntity === null) {
|
||||
|
|
|
@ -26,8 +26,9 @@ final class PhpDocTagRemover
|
|||
}
|
||||
|
||||
if ($phpDocChildNode->value instanceof DoctrineAnnotationTagValueNode) {
|
||||
$tagClass = $phpDocChildNode->value->getAnnotationClass();
|
||||
if ($tagClass === $name) {
|
||||
$doctrineAnnotationTagValueNode = $phpDocChildNode->value;
|
||||
|
||||
if ($doctrineAnnotationTagValueNode->hasClassName($name)) {
|
||||
unset($phpDocNode->children[$key]);
|
||||
$phpDocInfo->markAsChanged();
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ use PhpParser\Node;
|
|||
use PHPStan\PhpDocParser\Ast\PhpDoc\GenericTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
|
||||
use PHPStan\PhpDocParser\Lexer\Lexer;
|
||||
use Rector\BetterPhpDocParser\Attributes\AttributeMirrorer;
|
||||
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
|
||||
|
@ -155,7 +156,7 @@ final class DoctrineAnnotationDecorator
|
|||
$formerStartEnd = $genericTagValueNode->getAttribute(PhpDocAttributeKey::START_AND_END);
|
||||
|
||||
$doctrineAnnotationTagValueNode = new DoctrineAnnotationTagValueNode(
|
||||
$fullyQualifiedAnnotationClass,
|
||||
new IdentifierTypeNode($fullyQualifiedAnnotationClass),
|
||||
$genericTagValueNode->value,
|
||||
$values,
|
||||
SilentKeyMap::CLASS_NAMES_TO_SILENT_KEYS[$fullyQualifiedAnnotationClass] ?? null
|
||||
|
|
|
@ -8,6 +8,7 @@ use PhpParser\Node;
|
|||
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprFalseNode;
|
||||
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprIntegerNode;
|
||||
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprTrueNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
|
||||
use PHPStan\PhpDocParser\Lexer\Lexer;
|
||||
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
|
||||
use Rector\BetterPhpDocParser\PhpDocParser\ClassAnnotationMatcher;
|
||||
|
@ -109,6 +110,7 @@ final class PlainValueParser
|
|||
$tokenIterator->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL);
|
||||
$tokenIterator->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES);
|
||||
|
||||
return new DoctrineAnnotationTagValueNode($fullyQualifiedAnnotationClass, $annotationShortName, $values);
|
||||
$identifierTypeNode = new IdentifierTypeNode($fullyQualifiedAnnotationClass);
|
||||
return new DoctrineAnnotationTagValueNode($identifierTypeNode, $annotationShortName, $values);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -153,6 +153,11 @@ abstract class AbstractValuesAwareNode implements PhpDocTagValueNode
|
|||
return $explicitKeysValues;
|
||||
}
|
||||
|
||||
public function markAsChanged(): void
|
||||
{
|
||||
$this->hasChanged = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed|string $value
|
||||
* @return mixed|string
|
||||
|
|
|
@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||
|
||||
namespace Rector\BetterPhpDocParser\ValueObjectFactory\PhpDocNode\Symfony;
|
||||
|
||||
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
|
||||
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
|
||||
|
||||
final class SymfonyRouteTagValueNodeFactory
|
||||
|
@ -14,7 +15,7 @@ final class SymfonyRouteTagValueNodeFactory
|
|||
public function createFromItems(array $items): DoctrineAnnotationTagValueNode
|
||||
{
|
||||
return new DoctrineAnnotationTagValueNode(
|
||||
'Symfony\Component\Routing\Annotation\Route',
|
||||
new IdentifierTypeNode('Symfony\Component\Routing\Annotation\Route'),
|
||||
null,
|
||||
$items,
|
||||
'path'
|
||||
|
|
|
@ -11,10 +11,10 @@ use PhpParser\Node\Name\FullyQualified;
|
|||
use PhpParser\Node\Stmt\ClassLike;
|
||||
use PhpParser\Node\Stmt\Namespace_;
|
||||
use Rector\Autodiscovery\Configuration\CategoryNamespaceProvider;
|
||||
use Rector\Core\Configuration\RenamedClassesDataCollector;
|
||||
use Rector\Core\PhpParser\Node\BetterNodeFinder;
|
||||
use Rector\Core\ValueObject\Application\File;
|
||||
use Rector\FileSystemRector\ValueObject\AddedFileWithNodes;
|
||||
use Rector\PSR4\Collector\RenamedClassesCollector;
|
||||
use Rector\PSR4\FileInfoAnalyzer\FileInfoDeletionAnalyzer;
|
||||
use Rector\PSR4\FileRelocationResolver;
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
|
@ -25,7 +25,7 @@ final class AddedFileWithNodesFactory
|
|||
private BetterNodeFinder $betterNodeFinder,
|
||||
private CategoryNamespaceProvider $categoryNamespaceProvider,
|
||||
private FileRelocationResolver $fileRelocationResolver,
|
||||
private RenamedClassesCollector $renamedClassesCollector,
|
||||
private RenamedClassesDataCollector $renamedClassesDataCollector,
|
||||
private FileInfoDeletionAnalyzer $fileInfoDeletionAnalyzer
|
||||
) {
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ final class AddedFileWithNodesFactory
|
|||
$classLike = clone $classLike;
|
||||
$classLike->namespacedName = new FullyQualified($newClassName);
|
||||
|
||||
$this->renamedClassesCollector->addClassRename($oldClassName, $newClassName);
|
||||
$this->renamedClassesDataCollector->addOldToNewClass($oldClassName, $newClassName);
|
||||
|
||||
return new AddedFileWithNodes($newFileDestination, $fileNodes);
|
||||
}
|
||||
|
|
|
@ -469,6 +469,7 @@ final class NodeTypeResolver
|
|||
if (! $renamedObjectType instanceof ObjectType) {
|
||||
return $this->isObjectTypeOfObjectType($resolvedObjectType, $requiredObjectType);
|
||||
}
|
||||
|
||||
if (! $this->isObjectTypeOfObjectType($renamedObjectType, $requiredObjectType)) {
|
||||
return $this->isObjectTypeOfObjectType($resolvedObjectType, $requiredObjectType);
|
||||
}
|
||||
|
|
|
@ -8,6 +8,9 @@ use PhpParser\Node as PhpParserNode;
|
|||
use PHPStan\PhpDocParser\Ast\Node;
|
||||
use PHPStan\PhpDocParser\Ast\PhpDoc\TemplateTagValueNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
|
||||
use PHPStan\Type\ObjectType;
|
||||
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
|
||||
use Rector\BetterPhpDocParser\PhpDoc\SpacelessPhpDocTagNode;
|
||||
use Rector\BetterPhpDocParser\ValueObject\PhpDocAttributeKey;
|
||||
use Rector\CodingStyle\ClassNameImport\ClassNameImportSkipper;
|
||||
use Rector\Core\Configuration\Option;
|
||||
|
@ -39,6 +42,15 @@ final class NameImportingPhpDocNodeVisitor extends AbstractPhpDocNodeVisitor
|
|||
|
||||
public function enterNode(Node $node): ?Node
|
||||
{
|
||||
if ($node instanceof SpacelessPhpDocTagNode) {
|
||||
return $this->enterSpacelessPhpDocTagNode($node);
|
||||
}
|
||||
|
||||
if ($node instanceof DoctrineAnnotationTagValueNode) {
|
||||
$this->processDoctrineAnnotationTagValueNode($node);
|
||||
return $node;
|
||||
}
|
||||
|
||||
if (! $node instanceof IdentifierTypeNode) {
|
||||
return null;
|
||||
}
|
||||
|
@ -89,18 +101,20 @@ final class NameImportingPhpDocNodeVisitor extends AbstractPhpDocNodeVisitor
|
|||
if ($newNode->name !== $identifierTypeNode->name) {
|
||||
return $newNode;
|
||||
}
|
||||
|
||||
return $identifierTypeNode;
|
||||
}
|
||||
|
||||
return $identifierTypeNode;
|
||||
}
|
||||
|
||||
$this->useNodesToAddCollector->addUseImport($phpParserNode, $fullyQualifiedObjectType);
|
||||
$this->useNodesToAddCollector->addUseImport($fullyQualifiedObjectType);
|
||||
|
||||
$newNode = new IdentifierTypeNode($fullyQualifiedObjectType->getShortName());
|
||||
if ($newNode->name !== $identifierTypeNode->name) {
|
||||
return $newNode;
|
||||
}
|
||||
|
||||
return $identifierTypeNode;
|
||||
}
|
||||
|
||||
|
@ -113,4 +127,73 @@ final class NameImportingPhpDocNodeVisitor extends AbstractPhpDocNodeVisitor
|
|||
|
||||
return substr_count($fullyQualifiedObjectType->getClassName(), '\\') === 0;
|
||||
}
|
||||
|
||||
private function processDoctrineAnnotationTagValueNode(
|
||||
DoctrineAnnotationTagValueNode $doctrineAnnotationTagValueNode
|
||||
): void {
|
||||
$identifierTypeNode = $doctrineAnnotationTagValueNode->identifierTypeNode;
|
||||
|
||||
$staticType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType(
|
||||
$identifierTypeNode,
|
||||
$this->currentPhpParserNode
|
||||
);
|
||||
|
||||
if (! $staticType instanceof FullyQualifiedObjectType) {
|
||||
if (! $staticType instanceof ObjectType) {
|
||||
return;
|
||||
}
|
||||
|
||||
$staticType = new FullyQualifiedObjectType($staticType->getClassName());
|
||||
}
|
||||
|
||||
$shortentedIdentifierTypeNode = $this->processFqnNameImport(
|
||||
$this->currentPhpParserNode,
|
||||
$identifierTypeNode,
|
||||
$staticType
|
||||
);
|
||||
|
||||
if (! $shortentedIdentifierTypeNode instanceof IdentifierTypeNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
$doctrineAnnotationTagValueNode->identifierTypeNode = $shortentedIdentifierTypeNode;
|
||||
$doctrineAnnotationTagValueNode->markAsChanged();
|
||||
}
|
||||
|
||||
private function enterSpacelessPhpDocTagNode(
|
||||
SpacelessPhpDocTagNode $spacelessPhpDocTagNode
|
||||
): SpacelessPhpDocTagNode | null {
|
||||
if (! $spacelessPhpDocTagNode->value instanceof DoctrineAnnotationTagValueNode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// special case for doctrine annotation
|
||||
if (! str_starts_with($spacelessPhpDocTagNode->name, '@')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$attributeClass = ltrim($spacelessPhpDocTagNode->name, '@\\');
|
||||
$identifierTypeNode = new IdentifierTypeNode($attributeClass);
|
||||
|
||||
$staticType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType(
|
||||
new IdentifierTypeNode($attributeClass),
|
||||
$this->currentPhpParserNode
|
||||
);
|
||||
|
||||
if (! $staticType instanceof FullyQualifiedObjectType) {
|
||||
if (! $staticType instanceof ObjectType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$staticType = new FullyQualifiedObjectType($staticType->getClassName());
|
||||
}
|
||||
|
||||
$importedName = $this->processFqnNameImport($this->currentPhpParserNode, $identifierTypeNode, $staticType);
|
||||
if ($importedName !== null) {
|
||||
$spacelessPhpDocTagNode->name = '@' . $importedName->name;
|
||||
return $spacelessPhpDocTagNode;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ namespace Rector\PhpAttribute\Printer;
|
|||
use PhpParser\Node\Arg;
|
||||
use PhpParser\Node\Attribute;
|
||||
use PhpParser\Node\Scalar\String_;
|
||||
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
|
||||
use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode;
|
||||
use Rector\Core\PhpParser\Printer\BetterStandardPrinter;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
|
@ -21,7 +22,9 @@ final class DoctrineAnnotationFactory
|
|||
public function createFromAttribute(Attribute $attribute, string $className): DoctrineAnnotationTagValueNode
|
||||
{
|
||||
$items = $this->createItemsFromArgs($attribute->args);
|
||||
return new DoctrineAnnotationTagValueNode($className, null, $items);
|
||||
|
||||
$identifierTypeNode = new IdentifierTypeNode($className);
|
||||
return new DoctrineAnnotationTagValueNode($identifierTypeNode, null, $items);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -45,7 +45,7 @@ final class UseNodesToAddCollector implements NodeCollectorInterface
|
|||
/**
|
||||
* @param FullyQualifiedObjectType|AliasedObjectType $objectType
|
||||
*/
|
||||
public function addUseImport(Node $positionNode, ObjectType $objectType): void
|
||||
public function addUseImport(ObjectType $objectType): void
|
||||
{
|
||||
$file = $this->currentFileProvider->getFile();
|
||||
$smartFileInfo = $file->getSmartFileInfo();
|
||||
|
|
|
@ -5,7 +5,7 @@ declare(strict_types=1);
|
|||
namespace Rector\PostRector\Rector;
|
||||
|
||||
use PhpParser\Node;
|
||||
use Rector\PSR4\Collector\RenamedClassesCollector;
|
||||
use Rector\Core\Configuration\RenamedClassesDataCollector;
|
||||
use Rector\Renaming\NodeManipulator\ClassRenamer;
|
||||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample;
|
||||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
||||
|
@ -14,7 +14,7 @@ final class ClassRenamingPostRector extends AbstractPostRector
|
|||
{
|
||||
public function __construct(
|
||||
private ClassRenamer $classRenamer,
|
||||
private RenamedClassesCollector $renamedClassesCollector
|
||||
private RenamedClassesDataCollector $renamedClassesDataCollector
|
||||
) {
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ final class ClassRenamingPostRector extends AbstractPostRector
|
|||
|
||||
public function enterNode(Node $node): ?Node
|
||||
{
|
||||
$oldToNewClasses = $this->renamedClassesCollector->getOldToNewClasses();
|
||||
$oldToNewClasses = $this->renamedClassesDataCollector->getOldToNewClasses();
|
||||
if ($oldToNewClasses === []) {
|
||||
return $node;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ use PhpParser\Node\Stmt;
|
|||
use PhpParser\Node\Stmt\Namespace_;
|
||||
use Rector\CodingStyle\Application\UseImportsAdder;
|
||||
use Rector\CodingStyle\Application\UseImportsRemover;
|
||||
use Rector\Core\Configuration\RenamedClassesDataCollector;
|
||||
use Rector\Core\PhpParser\Node\BetterNodeFinder;
|
||||
use Rector\Core\PhpParser\Node\CustomNode\FileWithoutNamespace;
|
||||
use Rector\Core\Provider\CurrentFileProvider;
|
||||
|
@ -25,7 +26,8 @@ final class UseAddingPostRector extends AbstractPostRector
|
|||
private UseImportsAdder $useImportsAdder,
|
||||
private UseImportsRemover $useImportsRemover,
|
||||
private UseNodesToAddCollector $useNodesToAddCollector,
|
||||
private CurrentFileProvider $currentFileProvider
|
||||
private CurrentFileProvider $currentFileProvider,
|
||||
private RenamedClassesDataCollector $renamedClassesDataCollector
|
||||
) {
|
||||
}
|
||||
|
||||
|
@ -47,8 +49,10 @@ final class UseAddingPostRector extends AbstractPostRector
|
|||
$functionUseImportTypes = $this->useNodesToAddCollector->getFunctionImportsByFileInfo($smartFileInfo);
|
||||
$removedShortUses = $this->useNodesToAddCollector->getShortUsesByFileInfo($smartFileInfo);
|
||||
|
||||
$oldToNewClasses = $this->renamedClassesDataCollector->getOldToNewClasses();
|
||||
|
||||
// nothing to import or remove
|
||||
if ($useImportTypes === [] && $functionUseImportTypes === [] && $removedShortUses === []) {
|
||||
if ($useImportTypes === [] && $functionUseImportTypes === [] && $removedShortUses === [] && $oldToNewClasses === []) {
|
||||
return $nodes;
|
||||
}
|
||||
|
||||
|
@ -73,6 +77,8 @@ final class UseAddingPostRector extends AbstractPostRector
|
|||
$nodes = $firstNode->stmts;
|
||||
}
|
||||
|
||||
$removedShortUses = array_merge($removedShortUses, $this->renamedClassesDataCollector->getOldClasses());
|
||||
|
||||
// B. no namespace? add in the top
|
||||
// first clean
|
||||
$nodes = $this->useImportsRemover->removeImportsFromStmts($nodes, $removedShortUses);
|
||||
|
|
|
@ -17,15 +17,15 @@ use PHPStan\Type\StaticType;
|
|||
use PHPStan\Type\StringType;
|
||||
use PHPStan\Type\ThisType;
|
||||
use PHPStan\Type\Type;
|
||||
use Rector\Core\Configuration\RenamedClassesDataCollector;
|
||||
use Rector\NodeTypeResolver\Node\AttributeKey;
|
||||
use Rector\PSR4\Collector\RenamedClassesCollector;
|
||||
use Rector\StaticTypeMapper\Contract\PhpParser\PhpParserNodeMapperInterface;
|
||||
use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType;
|
||||
|
||||
final class NameNodeMapper implements PhpParserNodeMapperInterface
|
||||
{
|
||||
public function __construct(
|
||||
private RenamedClassesCollector $renamedClassesCollector,
|
||||
private RenamedClassesDataCollector $renamedClassesDataCollector,
|
||||
private ReflectionProvider $reflectionProvider
|
||||
) {
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ final class NameNodeMapper implements PhpParserNodeMapperInterface
|
|||
}
|
||||
|
||||
// to be existing class names
|
||||
$oldToNewClasses = $this->renamedClassesCollector->getOldToNewClasses();
|
||||
$oldToNewClasses = $this->renamedClassesDataCollector->getOldToNewClasses();
|
||||
|
||||
return in_array($name, $oldToNewClasses, true);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Rector\Core\Configuration\Option;
|
||||
use Rector\Php72\Rector\FuncCall\GetClassOnNullRector;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$parameters = $containerConfigurator->parameters();
|
||||
$parameters->set(Option::AUTO_IMPORT_NAMES, true);
|
||||
|
||||
$services = $containerConfigurator->services();
|
||||
$services->set(GetClassOnNullRector::class);
|
||||
};
|
|
@ -59,11 +59,13 @@ final class UseImportsRemover
|
|||
{
|
||||
foreach ($use->uses as $usesKey => $useUse) {
|
||||
foreach ($removedShortUses as $removedShortUse) {
|
||||
if ($useUse->name->getLast() !== $removedShortUse) {
|
||||
continue;
|
||||
if ($useUse->name->getLast() === $removedShortUse) {
|
||||
unset($use->uses[$usesKey]);
|
||||
}
|
||||
|
||||
unset($use->uses[$usesKey]);
|
||||
if ($useUse->name->toString() === $removedShortUse) {
|
||||
unset($use->uses[$usesKey]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ namespace Rector\CodingStyle\ClassNameImport\ClassNameImportSkipVoter;
|
|||
|
||||
use PhpParser\Node;
|
||||
use Rector\CodingStyle\Contract\ClassNameImport\ClassNameImportSkipVoterInterface;
|
||||
use Rector\Core\Configuration\RenamedClassesDataCollector;
|
||||
use Rector\PostRector\Collector\UseNodesToAddCollector;
|
||||
use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType;
|
||||
|
||||
|
@ -19,7 +20,8 @@ use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType;
|
|||
final class UsesClassNameImportSkipVoter implements ClassNameImportSkipVoterInterface
|
||||
{
|
||||
public function __construct(
|
||||
private UseNodesToAddCollector $useNodesToAddCollector
|
||||
private UseNodesToAddCollector $useNodesToAddCollector,
|
||||
private RenamedClassesDataCollector $renamedClassesDataCollector
|
||||
) {
|
||||
}
|
||||
|
||||
|
@ -28,10 +30,14 @@ final class UsesClassNameImportSkipVoter implements ClassNameImportSkipVoterInte
|
|||
$useImportTypes = $this->useNodesToAddCollector->getUseImportTypesByNode($node);
|
||||
|
||||
foreach ($useImportTypes as $useImportType) {
|
||||
// if the class is renamed, the use import is no longer blocker
|
||||
if ($this->renamedClassesDataCollector->hasOldClass($useImportType->getClassName())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! $useImportType->equals($fullyQualifiedObjectType) && $useImportType->areShortNamesEqual(
|
||||
$fullyQualifiedObjectType
|
||||
)
|
||||
) {
|
||||
)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ use PhpParser\Node\Name;
|
|||
use PhpParser\Node\Stmt\Use_;
|
||||
use PhpParser\Node\Stmt\UseUse;
|
||||
use Rector\CodingStyle\Contract\ClassNameImport\ClassNameImportSkipVoterInterface;
|
||||
use Rector\PSR4\Collector\RenamedClassesCollector;
|
||||
use Rector\Core\Configuration\RenamedClassesDataCollector;
|
||||
use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType;
|
||||
|
||||
final class ClassNameImportSkipper
|
||||
|
@ -19,7 +19,7 @@ final class ClassNameImportSkipper
|
|||
*/
|
||||
public function __construct(
|
||||
private array $classNameImportSkipVoters,
|
||||
private RenamedClassesCollector $renamedClassesCollector
|
||||
private RenamedClassesDataCollector $renamedClassesDataCollector
|
||||
) {
|
||||
}
|
||||
|
||||
|
@ -90,7 +90,7 @@ final class ClassNameImportSkipper
|
|||
private function isJustRenamedClass(Name $name, UseUse $useUse): bool
|
||||
{
|
||||
// is in renamed classes? skip it
|
||||
foreach ($this->renamedClassesCollector->getOldToNewClasses() as $oldClass => $newClass) {
|
||||
foreach ($this->renamedClassesDataCollector->getOldToNewClasses() as $oldClass => $newClass) {
|
||||
// is class being renamed in use imports?
|
||||
if ($name->toString() !== $newClass) {
|
||||
continue;
|
||||
|
|
|
@ -178,7 +178,7 @@ final class NameImporter
|
|||
if ($parentNode instanceof FuncCall) {
|
||||
$this->useNodesToAddCollector->addFunctionUseImport($name, $fullyQualifiedObjectType);
|
||||
} else {
|
||||
$this->useNodesToAddCollector->addUseImport($name, $fullyQualifiedObjectType);
|
||||
$this->useNodesToAddCollector->addUseImport($fullyQualifiedObjectType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\PSR4\Collector;
|
||||
|
||||
use Rector\Core\Configuration\RenamedClassesDataCollector;
|
||||
|
||||
/**
|
||||
* @deprecated Merge with RenamedClassesDataCollector
|
||||
*/
|
||||
final class RenamedClassesCollector
|
||||
{
|
||||
/**
|
||||
* @var array<string, string>
|
||||
*/
|
||||
private array $oldToNewClass = [];
|
||||
|
||||
public function __construct(
|
||||
private RenamedClassesDataCollector $renamedClassesDataCollector
|
||||
) {
|
||||
}
|
||||
|
||||
public function addClassRename(string $oldClass, string $newClass): void
|
||||
{
|
||||
$this->oldToNewClass[$oldClass] = $newClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function getOldToNewClasses(): array
|
||||
{
|
||||
return array_merge($this->oldToNewClass, $this->renamedClassesDataCollector->getOldToNewClasses());
|
||||
}
|
||||
}
|
|
@ -13,6 +13,16 @@ final class RenamedClassesDataCollector
|
|||
*/
|
||||
private array $oldToNewClasses = [];
|
||||
|
||||
public function addOldToNewClass(string $oldClass, string $newClass): void
|
||||
{
|
||||
$this->oldToNewClasses[$oldClass] = $newClass;
|
||||
}
|
||||
|
||||
public function hasOldClass(string $oldClass): bool
|
||||
{
|
||||
return isset($this->oldToNewClasses[$oldClass]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, string> $oldToNewClasses
|
||||
*/
|
||||
|
@ -40,4 +50,12 @@ final class RenamedClassesDataCollector
|
|||
|
||||
return new ObjectType($renamedClassName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getOldClasses(): array
|
||||
{
|
||||
return array_keys($this->oldToNewClasses);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ use Nette\Utils\Strings;
|
|||
use Rector\Core\Configuration\RenamedClassesDataCollector;
|
||||
use Rector\Core\Contract\Rector\ConfigurableRectorInterface;
|
||||
use Rector\Core\Contract\Rector\NonPhpRectorInterface;
|
||||
use Rector\PSR4\Collector\RenamedClassesCollector;
|
||||
use Symplify\RuleDocGenerator\Contract\ConfigurableRuleInterface;
|
||||
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
|
||||
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
|
||||
|
@ -41,7 +40,6 @@ final class RenameClassNonPhpRector implements NonPhpRectorInterface, Configurab
|
|||
|
||||
public function __construct(
|
||||
private RenamedClassesDataCollector $renamedClassesDataCollector,
|
||||
private RenamedClassesCollector $renamedClassesCollector
|
||||
) {
|
||||
}
|
||||
|
||||
|
@ -133,11 +131,7 @@ CODE_SAMPLE
|
|||
*/
|
||||
private function getRenameClasses(): array
|
||||
{
|
||||
return array_merge(
|
||||
$this->renameClasses,
|
||||
$this->renamedClassesDataCollector->getOldToNewClasses(),
|
||||
$this->renamedClassesCollector->getOldToNewClasses()
|
||||
);
|
||||
return array_merge($this->renameClasses, $this->renamedClassesDataCollector->getOldToNewClasses());
|
||||
}
|
||||
|
||||
private function createOldClassRegex(string $oldClass): string
|
||||
|
|
14
stubs/Symfony/Component/Routing/Annotation/Route.php
Normal file
14
stubs/Symfony/Component/Routing/Annotation/Route.php
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Symfony\Component\Routing\Annotation;
|
||||
|
||||
if (class_exists('Symfony\Component\Routing\Annotation\Route')) {
|
||||
return;
|
||||
}
|
||||
|
||||
class Route
|
||||
{
|
||||
|
||||
}
|
33
tests/Issues/Issue6496/AutoImportDocBlockTest.php
Normal file
33
tests/Issues/Issue6496/AutoImportDocBlockTest.php
Normal file
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\Core\Tests\Issues\Issue6496;
|
||||
|
||||
use Iterator;
|
||||
use Rector\Testing\PHPUnit\AbstractRectorTestCase;
|
||||
use Symplify\SmartFileSystem\SmartFileInfo;
|
||||
|
||||
final class AutoImportDocBlockTest extends AbstractRectorTestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider provideData()
|
||||
*/
|
||||
public function test(SmartFileInfo $fileInfo): void
|
||||
{
|
||||
$this->doTestFileInfo($fileInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Iterator<SmartFileInfo>
|
||||
*/
|
||||
public function provideData(): Iterator
|
||||
{
|
||||
return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture');
|
||||
}
|
||||
|
||||
public function provideConfigFilePath(): string
|
||||
{
|
||||
return __DIR__ . '/config/configure_rule.php';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
||||
|
||||
/**
|
||||
* @Route("/someUrl")
|
||||
*/
|
||||
class SomeController extends Controller
|
||||
{
|
||||
}
|
||||
|
||||
?>
|
||||
-----
|
||||
<?php
|
||||
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
|
||||
|
||||
/**
|
||||
* @Route("/someUrl")
|
||||
*/
|
||||
class SomeController extends Controller
|
||||
{
|
||||
}
|
||||
|
||||
?>
|
16
tests/Issues/Issue6496/config/configure_rule.php
Normal file
16
tests/Issues/Issue6496/config/configure_rule.php
Normal file
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Rector\Core\Configuration\Option;
|
||||
use Rector\Symfony\Rector\ClassMethod\ReplaceSensioRouteAnnotationWithSymfonyRector;
|
||||
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
|
||||
|
||||
return static function (ContainerConfigurator $containerConfigurator): void {
|
||||
$parameters = $containerConfigurator->parameters();
|
||||
$parameters->set(Option::AUTO_IMPORT_NAMES, true);
|
||||
$parameters->set(Option::IMPORT_DOC_BLOCKS, true);
|
||||
|
||||
$services = $containerConfigurator->services();
|
||||
$services->set(ReplaceSensioRouteAnnotationWithSymfonyRector::class);
|
||||
};
|
Loading…
Reference in New Issue
Block a user