From b9ea5fd9961997cecbd872cb345e88de582d6aca Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Thu, 22 Feb 2024 19:48:37 +0700 Subject: [PATCH] [PostRector] Skip remove use statement on used in annotation on removeUnusedImports() (#5657) * [PostRector] Skip remove use statement on used in annotation on removeUnusedImports() * Fixed :tada: * [ci-review] Rector Rectify * phpstan fix --------- Co-authored-by: GitHub Action --- .../PhpDocInfo/PhpDocInfo.php | 29 ++++++++ .../ArrayItemClassNameDecorator.php | 67 +++++++++++++++++++ .../LazyContainerFactory.php | 2 + .../Rector/UnusedImportRemovingPostRector.php | 3 + .../Fixture/skip_used_in_annotation.php.inc | 16 +++++ 5 files changed, 117 insertions(+) create mode 100644 src/BetterPhpDocParser/PhpDocParser/ArrayItemClassNameDecorator.php create mode 100644 tests/Issues/NoNamespaced/Fixture/skip_used_in_annotation.php.inc diff --git a/src/BetterPhpDocParser/PhpDocInfo/PhpDocInfo.php b/src/BetterPhpDocParser/PhpDocInfo/PhpDocInfo.php index a55b64b91dd..e703e925b96 100644 --- a/src/BetterPhpDocParser/PhpDocInfo/PhpDocInfo.php +++ b/src/BetterPhpDocParser/PhpDocInfo/PhpDocInfo.php @@ -24,6 +24,7 @@ use PHPStan\Type\MixedType; use PHPStan\Type\Type; use Rector\BetterPhpDocParser\Annotation\AnnotationNaming; +use Rector\BetterPhpDocParser\PhpDoc\ArrayItemNode; use Rector\BetterPhpDocParser\PhpDoc\DoctrineAnnotationTagValueNode; use Rector\BetterPhpDocParser\PhpDoc\SpacelessPhpDocTagNode; use Rector\BetterPhpDocParser\PhpDocNodeFinder\PhpDocNodeByTypeFinder; @@ -456,6 +457,34 @@ public function getConstFetchNodeClassNames(): array return $classNames; } + /** + * @return string[] + */ + public function getArrayItemNodeClassNames(): array + { + $phpDocNodeTraverser = new PhpDocNodeTraverser(); + + $classNames = []; + + $phpDocNodeTraverser->traverseWithCallable($this->phpDocNode, '', static function (Node $node) use ( + &$classNames, + ): ?ArrayItemNode { + if (! $node instanceof ArrayItemNode) { + return null; + } + + $resolvedClass = $node->getAttribute(PhpDocAttributeKey::RESOLVED_CLASS); + if ($resolvedClass === null) { + return null; + } + + $classNames[] = $resolvedClass; + return $node; + }); + + return $classNames; + } + /** * @param class-string $desiredClass * @return DoctrineAnnotationTagValueNode[] diff --git a/src/BetterPhpDocParser/PhpDocParser/ArrayItemClassNameDecorator.php b/src/BetterPhpDocParser/PhpDocParser/ArrayItemClassNameDecorator.php new file mode 100644 index 00000000000..c2e4f7056cf --- /dev/null +++ b/src/BetterPhpDocParser/PhpDocParser/ArrayItemClassNameDecorator.php @@ -0,0 +1,67 @@ +__toString(), '::')) { + return; + } + + $this->phpDocNodeTraverser->traverseWithCallable($phpDocNode, '', function (Node $node) use ( + $phpNode + ): Node|null { + if (! $node instanceof ArrayItemNode) { + return null; + } + + if (! is_string($node->value)) { + return null; + } + + $splitScopeResolution = explode('::', $node->value); + if (count($splitScopeResolution) !== 2) { + return null; + } + + $firstName = $splitScopeResolution[0]; + $constFetchNode = new ConstFetchNode($firstName, $firstName); + + $className = $this->resolveFullyQualifiedClass($constFetchNode, $phpNode); + $node->setAttribute(PhpDocAttributeKey::RESOLVED_CLASS, $className); + + return $node; + }); + } + + private function resolveFullyQualifiedClass(ConstFetchNode $constFetchNode, PhpNode $phpNode): string + { + $nameScope = $this->nameScopeFactory->createNameScopeFromNodeWithoutTemplateTypes($phpNode); + return $nameScope->resolveStringName($constFetchNode->className); + } +} diff --git a/src/DependencyInjection/LazyContainerFactory.php b/src/DependencyInjection/LazyContainerFactory.php index 3300f1998f6..3c2901ae4c1 100644 --- a/src/DependencyInjection/LazyContainerFactory.php +++ b/src/DependencyInjection/LazyContainerFactory.php @@ -28,6 +28,7 @@ use Rector\BetterPhpDocParser\PhpDocNodeVisitor\IntersectionTypeNodePhpDocNodeVisitor; use Rector\BetterPhpDocParser\PhpDocNodeVisitor\TemplatePhpDocNodeVisitor; use Rector\BetterPhpDocParser\PhpDocNodeVisitor\UnionTypeNodePhpDocNodeVisitor; +use Rector\BetterPhpDocParser\PhpDocParser\ArrayItemClassNameDecorator; use Rector\BetterPhpDocParser\PhpDocParser\BetterPhpDocParser; use Rector\BetterPhpDocParser\PhpDocParser\BetterTypeParser; use Rector\BetterPhpDocParser\PhpDocParser\ConstExprClassNameDecorator; @@ -313,6 +314,7 @@ final class LazyContainerFactory private const PHP_DOC_NODE_DECORATOR_CLASSES = [ ConstExprClassNameDecorator::class, DoctrineAnnotationDecorator::class, + ArrayItemClassNameDecorator::class, ]; /** diff --git a/src/PostRector/Rector/UnusedImportRemovingPostRector.php b/src/PostRector/Rector/UnusedImportRemovingPostRector.php index 87a27bf8498..fab2b761bdf 100644 --- a/src/PostRector/Rector/UnusedImportRemovingPostRector.php +++ b/src/PostRector/Rector/UnusedImportRemovingPostRector.php @@ -143,6 +143,9 @@ private function findNamesInDocBlocks(Namespace_|FileWithoutNamespace $namespace $genericTagClassNames = $phpDocInfo->getGenericTagClassNames(); $names = [...$names, ...$genericTagClassNames]; + + $arrayItemTagClassNames = $phpDocInfo->getArrayItemNodeClassNames(); + $names = [...$names, ...$arrayItemTagClassNames]; } }); diff --git a/tests/Issues/NoNamespaced/Fixture/skip_used_in_annotation.php.inc b/tests/Issues/NoNamespaced/Fixture/skip_used_in_annotation.php.inc new file mode 100644 index 00000000000..681a04a374b --- /dev/null +++ b/tests/Issues/NoNamespaced/Fixture/skip_used_in_annotation.php.inc @@ -0,0 +1,16 @@ +