From 61fc1f925849ea789820932006d744b42a971c97 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sat, 20 Sep 2025 10:19:34 +0700 Subject: [PATCH 01/26] [WIP] Add PostRectorInterface instances to list of applied rectors --- .../PHPUnit/ValueObject/RectorTestResult.php | 8 ++--- src/Util/RectorClassesSorter.php | 30 +++++++++++++++++++ src/ValueObject/Reporting/FileDiff.php | 19 +++--------- 3 files changed, 38 insertions(+), 19 deletions(-) create mode 100644 src/Util/RectorClassesSorter.php diff --git a/src/Testing/PHPUnit/ValueObject/RectorTestResult.php b/src/Testing/PHPUnit/ValueObject/RectorTestResult.php index c399788a1dc..d831f22be64 100644 --- a/src/Testing/PHPUnit/ValueObject/RectorTestResult.php +++ b/src/Testing/PHPUnit/ValueObject/RectorTestResult.php @@ -5,6 +5,8 @@ namespace Rector\Testing\PHPUnit\ValueObject; use Rector\Contract\Rector\RectorInterface; +use Rector\PostRector\Contract\Rector\PostRectorInterface; +use Rector\Util\RectorClassesSorter; use Rector\ValueObject\ProcessResult; /** @@ -24,7 +26,7 @@ public function getChangedContents(): string } /** - * @return array> + * @return array> */ public function getAppliedRectorClasses(): array { @@ -34,8 +36,6 @@ public function getAppliedRectorClasses(): array $rectorClasses = array_merge($rectorClasses, $fileDiff->getRectorClasses()); } - sort($rectorClasses); - - return array_unique($rectorClasses); + return RectorClassesSorter::sort($rectorClasses); } } diff --git a/src/Util/RectorClassesSorter.php b/src/Util/RectorClassesSorter.php new file mode 100644 index 00000000000..407ebf60c45 --- /dev/null +++ b/src/Util/RectorClassesSorter.php @@ -0,0 +1,30 @@ +> $rectorClasses + * @return array> + */ + public static function sort(array $rectorClasses): array + { + $rectorClasses = array_unique($rectorClasses); + + $mainRector = array_filter($rectorClasses, fn (string $rectorClass): bool => is_a($rectorClass, RectorInterface::class, true)); + sort($mainRector); + $postRector = array_filter($rectorClasses, fn (string $rectorClass): bool => is_a($rectorClass, PostRectorInterface::class, true)); + sort($postRector); + + return array_merge($mainRector, $postRector); + } +} diff --git a/src/ValueObject/Reporting/FileDiff.php b/src/ValueObject/Reporting/FileDiff.php index c9c48592a11..2b6928a86d6 100644 --- a/src/ValueObject/Reporting/FileDiff.php +++ b/src/ValueObject/Reporting/FileDiff.php @@ -8,6 +8,8 @@ use Rector\ChangesReporting\ValueObject\RectorWithLineChange; use Rector\Contract\Rector\RectorInterface; use Rector\Parallel\ValueObject\BridgeItem; +use Rector\PostRector\Contract\Rector\PostRectorInterface; +use Rector\Util\RectorClassesSorter; use Symplify\EasyParallel\Contract\SerializableInterface; use Webmozart\Assert\Assert; @@ -81,7 +83,7 @@ public function getRectorShortClasses(): array } /** - * @return array> + * @return array> */ public function getRectorClasses(): array { @@ -91,7 +93,7 @@ public function getRectorClasses(): array $rectorClasses[] = $rectorWithLineChange->getRectorClass(); } - return $this->sortClasses($rectorClasses); + return RectorClassesSorter::sort($rectorClasses); } public function getFirstLineNumber(): ?int @@ -158,17 +160,4 @@ public static function decode(array $json): self $rectorWithLineChanges, ); } - - /** - * @template TType as object - * @param array> $rectorClasses - * @return array> - */ - private function sortClasses(array $rectorClasses): array - { - $rectorClasses = array_unique($rectorClasses); - sort($rectorClasses); - - return $rectorClasses; - } } From b204d733e12988d491670f43a0402316fbecc998 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sat, 20 Sep 2025 10:34:54 +0700 Subject: [PATCH 02/26] make RectorWithLineChange allow PostRectorInterface class-string type --- .../ValueObject/RectorWithLineChange.php | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/ChangesReporting/ValueObject/RectorWithLineChange.php b/src/ChangesReporting/ValueObject/RectorWithLineChange.php index b2edf707152..81ed87937a5 100644 --- a/src/ChangesReporting/ValueObject/RectorWithLineChange.php +++ b/src/ChangesReporting/ValueObject/RectorWithLineChange.php @@ -5,6 +5,7 @@ namespace Rector\ChangesReporting\ValueObject; use Rector\Contract\Rector\RectorInterface; +use Rector\PostRector\Contract\Rector\PostRectorInterface; use Symplify\EasyParallel\Contract\SerializableInterface; use Webmozart\Assert\Assert; @@ -21,26 +22,22 @@ private const KEY_LINE = 'line'; /** - * @var class-string + * @var class-string */ private string $rectorClass; /** - * @param class-string|RectorInterface $rectorClass + * @param class-string $rectorClass */ public function __construct( - string|RectorInterface $rectorClass, + string $rectorClass, private int $line ) { - if ($rectorClass instanceof RectorInterface) { - $rectorClass = $rectorClass::class; - } - $this->rectorClass = $rectorClass; } /** - * @return class-string + * @return class-string */ public function getRectorClass(): string { From cbc2f2f8bbaeb66efeb56eaa9779b580028dcd12 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sat, 20 Sep 2025 10:36:14 +0700 Subject: [PATCH 03/26] Rename getRectorClasses() to getMainRectorClasses() to differentiate use of PostRectorIntercace instance --- src/Bridge/SetRectorsResolver.php | 2 +- src/Config/RectorConfig.php | 2 +- src/Configuration/RectorConfigBuilder.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Bridge/SetRectorsResolver.php b/src/Bridge/SetRectorsResolver.php index f50457965bc..c54c7240302 100644 --- a/src/Bridge/SetRectorsResolver.php +++ b/src/Bridge/SetRectorsResolver.php @@ -46,7 +46,7 @@ public function resolveFromFilePathIncludingConfiguration(string $configFilePath { $rectorConfig = $this->loadRectorConfigFromFilePath($configFilePath); - $rectorClassesWithOptionalConfiguration = $rectorConfig->getRectorClasses(); + $rectorClassesWithOptionalConfiguration = $rectorConfig->getMainRectorClasses(); foreach ($rectorConfig->getRuleConfigurations() as $rectorClass => $configuration) { // remove from non-configurable, if added again with better config diff --git a/src/Config/RectorConfig.php b/src/Config/RectorConfig.php index 129556ee82c..a4710622791 100644 --- a/src/Config/RectorConfig.php +++ b/src/Config/RectorConfig.php @@ -439,7 +439,7 @@ public function getRuleConfigurations(): array * @internal Used only for bridge * @return array> */ - public function getRectorClasses(): array + public function getMainRectorClasses(): array { return $this->tags[RectorInterface::class] ?? []; } diff --git a/src/Configuration/RectorConfigBuilder.php b/src/Configuration/RectorConfigBuilder.php index 64c5eb3a944..ae16e45f9bb 100644 --- a/src/Configuration/RectorConfigBuilder.php +++ b/src/Configuration/RectorConfigBuilder.php @@ -250,7 +250,7 @@ public function __invoke(RectorConfig $rectorConfig): void } // log rules from sets and compare them with explicit rules - $setRegisteredRectorClasses = $rectorConfig->getRectorClasses(); + $setRegisteredRectorClasses = $rectorConfig->getMainRectorClasses(); SimpleParameterProvider::addParameter(Option::SET_REGISTERED_RULES, $setRegisteredRectorClasses); if ($this->paths !== []) { From 3893d197773c05dff54f0648353c50df57c4f2d9 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sat, 20 Sep 2025 10:37:28 +0700 Subject: [PATCH 04/26] Fix phpstan --- src/ChangesReporting/ValueObject/RectorWithLineChange.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ChangesReporting/ValueObject/RectorWithLineChange.php b/src/ChangesReporting/ValueObject/RectorWithLineChange.php index 81ed87937a5..f1d62a37c67 100644 --- a/src/ChangesReporting/ValueObject/RectorWithLineChange.php +++ b/src/ChangesReporting/ValueObject/RectorWithLineChange.php @@ -60,7 +60,7 @@ public static function decode(array $json): self } /** - * @return array{rector_class: class-string, line: int} + * @return array{rector_class: class-string, line: int} */ public function jsonSerialize(): array { From f6cc117ba232f0dc197381bbc27d2221cf948156 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sat, 20 Sep 2025 03:40:06 +0000 Subject: [PATCH 05/26] [ci-review] Rector Rectify --- .../ValueObject/RectorWithLineChange.php | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/ChangesReporting/ValueObject/RectorWithLineChange.php b/src/ChangesReporting/ValueObject/RectorWithLineChange.php index f1d62a37c67..7f98ad8b7f6 100644 --- a/src/ChangesReporting/ValueObject/RectorWithLineChange.php +++ b/src/ChangesReporting/ValueObject/RectorWithLineChange.php @@ -21,19 +21,11 @@ */ private const KEY_LINE = 'line'; - /** - * @var class-string - */ - private string $rectorClass; - /** * @param class-string $rectorClass */ - public function __construct( - string $rectorClass, - private int $line - ) { - $this->rectorClass = $rectorClass; + public function __construct(private string $rectorClass, private int $line) + { } /** From 63fa81ee6fcce624f7556862c23b88cd06b07b3c Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sat, 20 Sep 2025 10:42:30 +0700 Subject: [PATCH 06/26] grammar fix --- src/Util/RectorClassesSorter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Util/RectorClassesSorter.php b/src/Util/RectorClassesSorter.php index 407ebf60c45..133f89c2457 100644 --- a/src/Util/RectorClassesSorter.php +++ b/src/Util/RectorClassesSorter.php @@ -8,7 +8,7 @@ use Rector\PostRector\Contract\Rector\PostRectorInterface; /** - * This classes ensure the PostRectorInterface instance listed after main RectorInterface instance + * This class ensure the PostRectorInterface instances listed after main RectorInterface instances */ final class RectorClassesSorter { From 72dd1d35022a44c31897a0d5be2d90e7d0f89efa Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sat, 20 Sep 2025 10:42:54 +0700 Subject: [PATCH 07/26] grammar fix --- src/Util/RectorClassesSorter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Util/RectorClassesSorter.php b/src/Util/RectorClassesSorter.php index 133f89c2457..c7a0eb86fd4 100644 --- a/src/Util/RectorClassesSorter.php +++ b/src/Util/RectorClassesSorter.php @@ -8,7 +8,7 @@ use Rector\PostRector\Contract\Rector\PostRectorInterface; /** - * This class ensure the PostRectorInterface instances listed after main RectorInterface instances + * This class ensure the PostRectorInterface class names listed after main RectorInterface class names */ final class RectorClassesSorter { From 7b1d0654bf011ce35891639ae986ae1cdbea6ab2 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sat, 20 Sep 2025 10:48:46 +0700 Subject: [PATCH 08/26] part 1: apply line with change on ClassRenamingPostRector --- .../Application/UseImportsRemover.php | 16 ++++++++++------ src/PostRector/Rector/AbstractPostRector.php | 10 ++++++++++ .../Rector/ClassRenamingPostRector.php | 4 +++- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/rules/CodingStyle/Application/UseImportsRemover.php b/rules/CodingStyle/Application/UseImportsRemover.php index 78446eb71e6..b8deab34aea 100644 --- a/rules/CodingStyle/Application/UseImportsRemover.php +++ b/rules/CodingStyle/Application/UseImportsRemover.php @@ -5,7 +5,9 @@ namespace Rector\CodingStyle\Application; use PhpParser\Node\Stmt; +use PhpParser\Node\Stmt\Namespace_; use PhpParser\Node\Stmt\Use_; +use Rector\PhpParser\Node\CustomNode\FileWithoutNamespace; use Rector\Renaming\Collector\RenamedNameCollector; final readonly class UseImportsRemover @@ -16,14 +18,12 @@ public function __construct( } /** - * @param Stmt[] $stmts * @param string[] $removedUses - * @return Stmt[] */ - public function removeImportsFromStmts(array $stmts, array $removedUses): array + public function removeImportsFromStmts(FileWithoutNamespace|Namespace_ $node, array $removedUses): bool { $hasRemoved = false; - foreach ($stmts as $key => $stmt) { + foreach ($node->stmts as $key => $stmt) { if (! $stmt instanceof Use_) { continue; } @@ -32,12 +32,16 @@ public function removeImportsFromStmts(array $stmts, array $removedUses): array // remove empty uses if ($stmt->uses === []) { - unset($stmts[$key]); + unset($node->stmts[$key]); $hasRemoved = true; } } - return $hasRemoved ? array_values($stmts) : $stmts; + if ($hasRemoved) { + $node->stmts = array_values($node->stmts); + } + + return $hasRemoved; } /** diff --git a/src/PostRector/Rector/AbstractPostRector.php b/src/PostRector/Rector/AbstractPostRector.php index 77632fbbb03..341100666b9 100644 --- a/src/PostRector/Rector/AbstractPostRector.php +++ b/src/PostRector/Rector/AbstractPostRector.php @@ -4,8 +4,10 @@ namespace Rector\PostRector\Rector; +use PhpParser\Node; use PhpParser\Node\Stmt; use PhpParser\NodeVisitorAbstract; +use Rector\ChangesReporting\ValueObject\RectorWithLineChange; use Rector\PostRector\Contract\Rector\PostRectorInterface; use Rector\ValueObject\Application\File; use Webmozart\Assert\Assert; @@ -33,4 +35,12 @@ public function getFile(): File return $this->file; } + + protected function addRectorClassWithLine(Node $node): void + { + Assert::isInstanceOf($this->file, File::class); + + $rectorWithLineChange = new RectorWithLineChange(static::class, $node->getStartLine()); + $this->file->addRectorClassWithLine($rectorWithLineChange); + } } diff --git a/src/PostRector/Rector/ClassRenamingPostRector.php b/src/PostRector/Rector/ClassRenamingPostRector.php index 7767b6370a7..50f91212e44 100644 --- a/src/PostRector/Rector/ClassRenamingPostRector.php +++ b/src/PostRector/Rector/ClassRenamingPostRector.php @@ -44,7 +44,9 @@ public function beforeTraverse(array $nodes): array foreach ($nodes as $node) { if ($node instanceof FileWithoutNamespace || $node instanceof Namespace_) { $removedUses = $this->renamedClassesDataCollector->getOldClasses(); - $node->stmts = $this->useImportsRemover->removeImportsFromStmts($node->stmts, $removedUses); + if ($this->useImportsRemover->removeImportsFromStmts($node, $removedUses)) { + $this->addRectorClassWithLine($node); + } break; } From 4072c4b679c7a29b034042f0654b6eaaffeb043b Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sat, 20 Sep 2025 10:50:27 +0700 Subject: [PATCH 09/26] part 2: apply line with change on DocblockNameImportingPostRector --- src/PostRector/Rector/DocblockNameImportingPostRector.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/PostRector/Rector/DocblockNameImportingPostRector.php b/src/PostRector/Rector/DocblockNameImportingPostRector.php index d60bdfea9c4..407f8de47db 100644 --- a/src/PostRector/Rector/DocblockNameImportingPostRector.php +++ b/src/PostRector/Rector/DocblockNameImportingPostRector.php @@ -39,6 +39,7 @@ public function enterNode(Node $node): Node|null return null; } + $this->addRectorClassWithLine($node); $this->docBlockUpdater->updateRefactoredNodeWithPhpDocInfo($node); return $node; } From 57331376e53dbde02b2c31c6f99af9c037558387 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Sat, 20 Sep 2025 03:51:24 +0000 Subject: [PATCH 10/26] [ci-review] Rector Rectify --- rules/CodingStyle/Application/UseImportsRemover.php | 1 - 1 file changed, 1 deletion(-) diff --git a/rules/CodingStyle/Application/UseImportsRemover.php b/rules/CodingStyle/Application/UseImportsRemover.php index b8deab34aea..21140f81d31 100644 --- a/rules/CodingStyle/Application/UseImportsRemover.php +++ b/rules/CodingStyle/Application/UseImportsRemover.php @@ -4,7 +4,6 @@ namespace Rector\CodingStyle\Application; -use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Namespace_; use PhpParser\Node\Stmt\Use_; use Rector\PhpParser\Node\CustomNode\FileWithoutNamespace; From a3e2248a0d2e8b99ddd44058b91bc75432824293 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sat, 20 Sep 2025 10:52:12 +0700 Subject: [PATCH 11/26] Fix phpstan --- phpstan.neon | 1 + 1 file changed, 1 insertion(+) diff --git a/phpstan.neon b/phpstan.neon index 947e43b5a06..c1b8e3aa3b2 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -238,6 +238,7 @@ parameters: paths: # caching and message of specific child Rector rules - src/Rector/AbstractRector.php + - src/PostRector/Rector/AbstractPostRector.php # for cache - src/Testing/PHPUnit/AbstractRectorTestCase.php From 274f01ef35cebe7c69939e22a6c5e3dd0da620cb Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sat, 20 Sep 2025 10:54:44 +0700 Subject: [PATCH 12/26] part 3: apply line with change on UnusedImportRemovingPostRector --- src/PostRector/Rector/UnusedImportRemovingPostRector.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/PostRector/Rector/UnusedImportRemovingPostRector.php b/src/PostRector/Rector/UnusedImportRemovingPostRector.php index 35395d7aab9..b06f2ad7191 100644 --- a/src/PostRector/Rector/UnusedImportRemovingPostRector.php +++ b/src/PostRector/Rector/UnusedImportRemovingPostRector.php @@ -93,6 +93,7 @@ public function enterNode(Node $node): ?Node return null; } + $this->addRectorClassWithLine($node); $node->stmts = array_values($node->stmts); return $node; } From b2f5e3b1bb0cd5057ebec86f0aef7c67cd339f8e Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sat, 20 Sep 2025 14:00:04 +0700 Subject: [PATCH 13/26] part 4: ensure partial remove use detected --- .../CodingStyle/Application/UseImportsRemover.php | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/rules/CodingStyle/Application/UseImportsRemover.php b/rules/CodingStyle/Application/UseImportsRemover.php index 21140f81d31..f1b44d06c9e 100644 --- a/rules/CodingStyle/Application/UseImportsRemover.php +++ b/rules/CodingStyle/Application/UseImportsRemover.php @@ -27,12 +27,13 @@ public function removeImportsFromStmts(FileWithoutNamespace|Namespace_ $node, ar continue; } - $stmt = $this->removeUseFromUse($removedUses, $stmt); + if ($this->removeUseFromUse($removedUses, $stmt)) { + $hasRemoved = true; + } // remove empty uses if ($stmt->uses === []) { unset($node->stmts[$key]); - $hasRemoved = true; } } @@ -46,8 +47,9 @@ public function removeImportsFromStmts(FileWithoutNamespace|Namespace_ $node, ar /** * @param string[] $removedUses */ - private function removeUseFromUse(array $removedUses, Use_ $use): Use_ + private function removeUseFromUse(array $removedUses, Use_ $use): bool { + $hasChanged = false; foreach ($use->uses as $usesKey => $useUse) { $useName = $useUse->name->toString(); if (! in_array($useName, $removedUses, true)) { @@ -59,8 +61,13 @@ private function removeUseFromUse(array $removedUses, Use_ $use): Use_ } unset($use->uses[$usesKey]); + $hasChanged = true; + } + + if ($hasChanged) { + $use->uses = array_values($use->uses); } - return $use; + return $hasChanged; } } From 2c4f72f715c143ad9db046bb61983cef4a46acc9 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sat, 20 Sep 2025 14:00:51 +0700 Subject: [PATCH 14/26] part 4: ensure partial remove use detected --- rules/CodingStyle/Application/UseImportsRemover.php | 1 + 1 file changed, 1 insertion(+) diff --git a/rules/CodingStyle/Application/UseImportsRemover.php b/rules/CodingStyle/Application/UseImportsRemover.php index f1b44d06c9e..d20c6bf1eb4 100644 --- a/rules/CodingStyle/Application/UseImportsRemover.php +++ b/rules/CodingStyle/Application/UseImportsRemover.php @@ -28,6 +28,7 @@ public function removeImportsFromStmts(FileWithoutNamespace|Namespace_ $node, ar } if ($this->removeUseFromUse($removedUses, $stmt)) { + $node->stmts[$key] = $stmt; $hasRemoved = true; } From 2f8774621e13680774647ee0d7e24441e88e6e42 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sat, 20 Sep 2025 14:17:32 +0700 Subject: [PATCH 15/26] part 5: add rector clas with line on NameImportingPostRector --- src/PostRector/Rector/NameImportingPostRector.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/PostRector/Rector/NameImportingPostRector.php b/src/PostRector/Rector/NameImportingPostRector.php index 9c51238b31f..66ada94a3ba 100644 --- a/src/PostRector/Rector/NameImportingPostRector.php +++ b/src/PostRector/Rector/NameImportingPostRector.php @@ -5,6 +5,7 @@ namespace Rector\PostRector\Rector; use PhpParser\Node; +use PhpParser\Node\Name; use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\GroupUse; @@ -36,13 +37,19 @@ public function beforeTraverse(array $nodes): array return $nodes; } - public function enterNode(Node $node): Node|null + public function enterNode(Node $node): Name|null { if (! $node instanceof FullyQualified) { return null; } - return $this->nameImporter->importName($node, $this->getFile(), $this->currentUses); + $name = $this->nameImporter->importName($node, $this->getFile(), $this->currentUses); + if (! $name instanceof Name) { + return null; + } + + $this->addRectorClassWithLine($node); + return $name; } /** From e819d8a2022fac4f051fe3629487cb8bab8d85ea Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sat, 20 Sep 2025 14:40:47 +0700 Subject: [PATCH 16/26] Add OriginalNameImportSkipVoter for auto import --- .../OriginalNameImportSkipVoter.php | 33 +++++++++++++++++++ .../LazyContainerFactory.php | 2 ++ 2 files changed, 35 insertions(+) create mode 100644 rules/CodingStyle/ClassNameImport/ClassNameImportSkipVoter/OriginalNameImportSkipVoter.php diff --git a/rules/CodingStyle/ClassNameImport/ClassNameImportSkipVoter/OriginalNameImportSkipVoter.php b/rules/CodingStyle/ClassNameImport/ClassNameImportSkipVoter/OriginalNameImportSkipVoter.php new file mode 100644 index 00000000000..e8569ab0572 --- /dev/null +++ b/rules/CodingStyle/ClassNameImport/ClassNameImportSkipVoter/OriginalNameImportSkipVoter.php @@ -0,0 +1,33 @@ +toCodeString(), '\\') === 1) { + return false; + } + + // verify long name, as short name verify may conflict + // see test PR: https://github.com/rectorphp/rector-src/pull/6208 + // ref https://3v4l.org/21H5j vs https://3v4l.org/GIHSB + $originalName = $node->getAttribute(AttributeKey::ORIGINAL_NAME); + return $originalName instanceof Name && $originalName->getLast() === $originalName->toString(); + } +} diff --git a/src/DependencyInjection/LazyContainerFactory.php b/src/DependencyInjection/LazyContainerFactory.php index be2a741911b..19d8e424c75 100644 --- a/src/DependencyInjection/LazyContainerFactory.php +++ b/src/DependencyInjection/LazyContainerFactory.php @@ -45,6 +45,7 @@ use Rector\CodingStyle\ClassNameImport\ClassNameImportSkipVoter\AliasClassNameImportSkipVoter; use Rector\CodingStyle\ClassNameImport\ClassNameImportSkipVoter\ClassLikeNameClassNameImportSkipVoter; use Rector\CodingStyle\ClassNameImport\ClassNameImportSkipVoter\FullyQualifiedNameClassNameImportSkipVoter; +use Rector\CodingStyle\ClassNameImport\ClassNameImportSkipVoter\OriginalNameImportSkipVoter; use Rector\CodingStyle\ClassNameImport\ClassNameImportSkipVoter\ReservedClassNameImportSkipVoter; use Rector\CodingStyle\ClassNameImport\ClassNameImportSkipVoter\ShortClassImportSkipVoter; use Rector\CodingStyle\ClassNameImport\ClassNameImportSkipVoter\UsesClassNameImportSkipVoter; @@ -266,6 +267,7 @@ final class LazyContainerFactory UsesClassNameImportSkipVoter::class, ReservedClassNameImportSkipVoter::class, ShortClassImportSkipVoter::class, + OriginalNameImportSkipVoter::class, ]; /** From 1dbced4f51bb01d711bc6ecac18bebb46abd0eef Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sat, 20 Sep 2025 21:08:54 +0700 Subject: [PATCH 17/26] Fix no change handling --- rules/CodingStyle/Node/NameImporter.php | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/rules/CodingStyle/Node/NameImporter.php b/rules/CodingStyle/Node/NameImporter.php index 945255bc378..3532a597ab9 100644 --- a/rules/CodingStyle/Node/NameImporter.php +++ b/rules/CodingStyle/Node/NameImporter.php @@ -108,7 +108,29 @@ private function importNameAndCollectNewUseStatement( } $this->addUseImport($file, $fullyQualified, $fullyQualifiedObjectType); - return $fullyQualifiedObjectType->getShortNameNode(); + $name = $fullyQualifiedObjectType->getShortNameNode(); + + $oldTokens = $file->getOldTokens(); + $startTokenPos = $fullyQualified->getStartTokenPos(); + + if (! isset($oldTokens[$startTokenPos])) { + return $name; + } + + $tokenShortName = $oldTokens[$startTokenPos]; + if (str_starts_with($tokenShortName->text, '\\')) { + return $name; + } + + if (str_contains($tokenShortName->text, '\\')) { + return $name; + } + + if ($name->toString() !== $tokenShortName->text) { + return $name; + } + + return null; } private function addUseImport( From 0e842d80c923a006a4623065f5a5e2e60e85e650 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sat, 20 Sep 2025 21:08:58 +0700 Subject: [PATCH 18/26] Fix cs --- .../ValueObject/RectorWithLineChange.php | 6 ++++-- src/Util/RectorClassesSorter.php | 10 ++++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/ChangesReporting/ValueObject/RectorWithLineChange.php b/src/ChangesReporting/ValueObject/RectorWithLineChange.php index 7f98ad8b7f6..f5c0a71b6bf 100644 --- a/src/ChangesReporting/ValueObject/RectorWithLineChange.php +++ b/src/ChangesReporting/ValueObject/RectorWithLineChange.php @@ -24,8 +24,10 @@ /** * @param class-string $rectorClass */ - public function __construct(private string $rectorClass, private int $line) - { + public function __construct( + private string $rectorClass, + private int $line + ) { } /** diff --git a/src/Util/RectorClassesSorter.php b/src/Util/RectorClassesSorter.php index c7a0eb86fd4..6ff448fce1d 100644 --- a/src/Util/RectorClassesSorter.php +++ b/src/Util/RectorClassesSorter.php @@ -20,9 +20,15 @@ public static function sort(array $rectorClasses): array { $rectorClasses = array_unique($rectorClasses); - $mainRector = array_filter($rectorClasses, fn (string $rectorClass): bool => is_a($rectorClass, RectorInterface::class, true)); + $mainRector = array_filter( + $rectorClasses, + fn (string $rectorClass): bool => is_a($rectorClass, RectorInterface::class, true) + ); sort($mainRector); - $postRector = array_filter($rectorClasses, fn (string $rectorClass): bool => is_a($rectorClass, PostRectorInterface::class, true)); + $postRector = array_filter( + $rectorClasses, + fn (string $rectorClass): bool => is_a($rectorClass, PostRectorInterface::class, true) + ); sort($postRector); return array_merge($mainRector, $postRector); From 946764c7e88ca4ebe6a04e80197255f0af8bbf2a Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sat, 20 Sep 2025 21:24:30 +0700 Subject: [PATCH 19/26] Add e2e test --- .github/workflows/e2e.yaml | 1 + e2e/applied-auto-import/.gitignore | 1 + e2e/applied-auto-import/composer.json | 7 +++++ e2e/applied-auto-import/expected-output.diff | 31 +++++++++++++++++++ e2e/applied-auto-import/rector.php | 19 ++++++++++++ .../src/RenameDocblock.php | 12 +++++++ 6 files changed, 71 insertions(+) create mode 100644 e2e/applied-auto-import/.gitignore create mode 100644 e2e/applied-auto-import/composer.json create mode 100644 e2e/applied-auto-import/expected-output.diff create mode 100644 e2e/applied-auto-import/rector.php create mode 100644 e2e/applied-auto-import/src/RenameDocblock.php diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index 5e1492b416e..8aab0d0f9f7 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -24,6 +24,7 @@ jobs: matrix: php_version: ['8.2'] directory: + - 'e2e/applied-auto-import' - 'e2e/applied-polyfill-php80' - 'e2e/applied-rule-change-docblock' - 'e2e/applied-rule-removed-node' diff --git a/e2e/applied-auto-import/.gitignore b/e2e/applied-auto-import/.gitignore new file mode 100644 index 00000000000..61ead86667c --- /dev/null +++ b/e2e/applied-auto-import/.gitignore @@ -0,0 +1 @@ +/vendor diff --git a/e2e/applied-auto-import/composer.json b/e2e/applied-auto-import/composer.json new file mode 100644 index 00000000000..5468cd74606 --- /dev/null +++ b/e2e/applied-auto-import/composer.json @@ -0,0 +1,7 @@ +{ + "require": { + "php": "^8.1" + }, + "minimum-stability": "dev", + "prefer-stable": true +} diff --git a/e2e/applied-auto-import/expected-output.diff b/e2e/applied-auto-import/expected-output.diff new file mode 100644 index 00000000000..37ebef5fc5f --- /dev/null +++ b/e2e/applied-auto-import/expected-output.diff @@ -0,0 +1,31 @@ +1 file with changes +=================== + +1) src/RenameDocblock.php:2 + + ---------- begin diff ---------- +@@ @@ + + namespace App; + +-use SomeUnusedClass; ++use DateTimeInterface; + + /** +- * @param \DateTime $someOldClass ++ * @param DateTimeInterface $someOldClass + */ +-function someFunction(\DateTime $someOldClass) ++function someFunction(DateTimeInterface $someOldClass) + { + } + ----------- end diff ----------- + +Applied rules: + * RenameClassRector + * DocblockNameImportingPostRector + * NameImportingPostRector + * UnusedImportRemovingPostRector + + + [OK] 1 file would have been changed (dry-run) by Rector diff --git a/e2e/applied-auto-import/rector.php b/e2e/applied-auto-import/rector.php new file mode 100644 index 00000000000..e6de67b6c3e --- /dev/null +++ b/e2e/applied-auto-import/rector.php @@ -0,0 +1,19 @@ +paths([ + __DIR__ . '/src', + ]); + + $rectorConfig->ruleWithConfiguration(RenameClassRector::class, [ + 'DateTime' => 'DateTimeInterface' + ]); + + $rectorConfig->importNames(); + $rectorConfig->removeUnusedImports(); +}; diff --git a/e2e/applied-auto-import/src/RenameDocblock.php b/e2e/applied-auto-import/src/RenameDocblock.php new file mode 100644 index 00000000000..25c59f18bca --- /dev/null +++ b/e2e/applied-auto-import/src/RenameDocblock.php @@ -0,0 +1,12 @@ + Date: Sat, 20 Sep 2025 21:47:14 +0700 Subject: [PATCH 20/26] Final touch: add UseAddingPostRector that NameImportingPostRector is rely when NameImportingPostRector exists --- e2e/applied-auto-import/expected-output.diff | 1 + src/Util/RectorClassesSorter.php | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/e2e/applied-auto-import/expected-output.diff b/e2e/applied-auto-import/expected-output.diff index 37ebef5fc5f..5ab0dad5769 100644 --- a/e2e/applied-auto-import/expected-output.diff +++ b/e2e/applied-auto-import/expected-output.diff @@ -26,6 +26,7 @@ Applied rules: * DocblockNameImportingPostRector * NameImportingPostRector * UnusedImportRemovingPostRector + * UseAddingPostRector [OK] 1 file would have been changed (dry-run) by Rector diff --git a/src/Util/RectorClassesSorter.php b/src/Util/RectorClassesSorter.php index 6ff448fce1d..0af30e4309d 100644 --- a/src/Util/RectorClassesSorter.php +++ b/src/Util/RectorClassesSorter.php @@ -6,6 +6,8 @@ use Rector\Contract\Rector\RectorInterface; use Rector\PostRector\Contract\Rector\PostRectorInterface; +use Rector\PostRector\Rector\NameImportingPostRector; +use Rector\PostRector\Rector\UseAddingPostRector; /** * This class ensure the PostRectorInterface class names listed after main RectorInterface class names @@ -25,10 +27,16 @@ public static function sort(array $rectorClasses): array fn (string $rectorClass): bool => is_a($rectorClass, RectorInterface::class, true) ); sort($mainRector); + $postRector = array_filter( $rectorClasses, fn (string $rectorClass): bool => is_a($rectorClass, PostRectorInterface::class, true) ); + + if (in_array(NameImportingPostRector::class, $postRector, true)) { + $postRector[] = UseAddingPostRector::class; + } + sort($postRector); return array_merge($mainRector, $postRector); From 62aad255bff0a49740dd4752c72d3713b8e5330b Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sat, 20 Sep 2025 21:48:51 +0700 Subject: [PATCH 21/26] Final touch: add UseAddingPostRector that NameImportingPostRector is rely when NameImportingPostRector exists --- src/Util/RectorClassesSorter.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Util/RectorClassesSorter.php b/src/Util/RectorClassesSorter.php index 0af30e4309d..b554d3acc62 100644 --- a/src/Util/RectorClassesSorter.php +++ b/src/Util/RectorClassesSorter.php @@ -33,6 +33,8 @@ public static function sort(array $rectorClasses): array fn (string $rectorClass): bool => is_a($rectorClass, PostRectorInterface::class, true) ); + // when NameImportingPostRector is applied + // the UseAddingPostRector mostly applied since it add into UseAddingPostRector if (in_array(NameImportingPostRector::class, $postRector, true)) { $postRector[] = UseAddingPostRector::class; } From 56cb8e34ab6ca973069a3c6488e83ed2ff10a35e Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sat, 20 Sep 2025 21:49:55 +0700 Subject: [PATCH 22/26] final touch: fix grammar --- src/Util/RectorClassesSorter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Util/RectorClassesSorter.php b/src/Util/RectorClassesSorter.php index b554d3acc62..f131c6d100b 100644 --- a/src/Util/RectorClassesSorter.php +++ b/src/Util/RectorClassesSorter.php @@ -34,7 +34,7 @@ public static function sort(array $rectorClasses): array ); // when NameImportingPostRector is applied - // the UseAddingPostRector mostly applied since it add into UseAddingPostRector + // the UseAddingPostRector mostly applied since it mostly add to use statement via UseAddingPostRector if (in_array(NameImportingPostRector::class, $postRector, true)) { $postRector[] = UseAddingPostRector::class; } From d3979d06b64a24c326b14b4554d9bd60059b1c6f Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sun, 21 Sep 2025 00:40:39 +0700 Subject: [PATCH 23/26] Really final touch: ensure UseAddingPostRector correctly added when exactly applied --- rules/CodingStyle/Application/UseImportsAdder.php | 13 +++++++++++-- src/PostRector/Rector/AbstractPostRector.php | 2 +- src/PostRector/Rector/UseAddingPostRector.php | 6 ++++-- src/Util/RectorClassesSorter.php | 8 -------- 4 files changed, 16 insertions(+), 13 deletions(-) diff --git a/rules/CodingStyle/Application/UseImportsAdder.php b/rules/CodingStyle/Application/UseImportsAdder.php index 2a888870573..aab797f5b01 100644 --- a/rules/CodingStyle/Application/UseImportsAdder.php +++ b/rules/CodingStyle/Application/UseImportsAdder.php @@ -15,6 +15,7 @@ use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory; use Rector\PhpParser\Node\CustomNode\FileWithoutNamespace; +use Rector\PostRector\Rector\UseAddingPostRector; use Rector\StaticTypeMapper\ValueObject\Type\AliasedObjectType; use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType; @@ -38,7 +39,8 @@ public function addImportsToStmts( array $stmts, array $useImportTypes, array $constantUseImportTypes, - array $functionUseImportTypes + array $functionUseImportTypes, + UseAddingPostRector $useAddingPostRector ): array { $usedImports = $this->usedImportsResolver->resolveForStmts($stmts); $existingUseImportTypes = $usedImports->getUseImports(); @@ -94,6 +96,8 @@ public function addImportsToStmts( $fileWithoutNamespace->stmts = $stmts; $fileWithoutNamespace->stmts = array_values($fileWithoutNamespace->stmts); + $useAddingPostRector->addRectorClassWithLine($fileWithoutNamespace); + return [$fileWithoutNamespace]; } @@ -103,6 +107,8 @@ public function addImportsToStmts( $fileWithoutNamespace->stmts = array_merge($newUses, $this->resolveInsertNop($fileWithoutNamespace), $stmts); $fileWithoutNamespace->stmts = array_values($fileWithoutNamespace->stmts); + $useAddingPostRector->addRectorClassWithLine($fileWithoutNamespace); + return [$fileWithoutNamespace]; } @@ -115,7 +121,8 @@ public function addImportsToNamespace( Namespace_ $namespace, array $useImportTypes, array $constantUseImportTypes, - array $functionUseImportTypes + array $functionUseImportTypes, + UseAddingPostRector $useAddingPostRector ): void { $namespaceName = $this->getNamespaceName($namespace); @@ -148,6 +155,8 @@ public function addImportsToNamespace( $namespace->stmts = array_merge($newUses, $this->resolveInsertNop($namespace), $namespace->stmts); $namespace->stmts = array_values($namespace->stmts); + + $useAddingPostRector->addRectorClassWithLine($namespace); } /** diff --git a/src/PostRector/Rector/AbstractPostRector.php b/src/PostRector/Rector/AbstractPostRector.php index 341100666b9..9dc5175faa9 100644 --- a/src/PostRector/Rector/AbstractPostRector.php +++ b/src/PostRector/Rector/AbstractPostRector.php @@ -36,7 +36,7 @@ public function getFile(): File return $this->file; } - protected function addRectorClassWithLine(Node $node): void + public function addRectorClassWithLine(Node $node): void { Assert::isInstanceOf($this->file, File::class); diff --git a/src/PostRector/Rector/UseAddingPostRector.php b/src/PostRector/Rector/UseAddingPostRector.php index 35f66e57852..daf28b27cbb 100644 --- a/src/PostRector/Rector/UseAddingPostRector.php +++ b/src/PostRector/Rector/UseAddingPostRector.php @@ -104,7 +104,8 @@ private function resolveNodesWithImportedUses( $namespace, $useImportTypes, $constantUseImportTypes, - $functionUseImportTypes + $functionUseImportTypes, + $this ); return $nodes; @@ -119,7 +120,8 @@ private function resolveNodesWithImportedUses( $nodes, $useImportTypes, $constantUseImportTypes, - $functionUseImportTypes + $functionUseImportTypes, + $this ); } diff --git a/src/Util/RectorClassesSorter.php b/src/Util/RectorClassesSorter.php index f131c6d100b..54ba593beab 100644 --- a/src/Util/RectorClassesSorter.php +++ b/src/Util/RectorClassesSorter.php @@ -6,8 +6,6 @@ use Rector\Contract\Rector\RectorInterface; use Rector\PostRector\Contract\Rector\PostRectorInterface; -use Rector\PostRector\Rector\NameImportingPostRector; -use Rector\PostRector\Rector\UseAddingPostRector; /** * This class ensure the PostRectorInterface class names listed after main RectorInterface class names @@ -33,12 +31,6 @@ public static function sort(array $rectorClasses): array fn (string $rectorClass): bool => is_a($rectorClass, PostRectorInterface::class, true) ); - // when NameImportingPostRector is applied - // the UseAddingPostRector mostly applied since it mostly add to use statement via UseAddingPostRector - if (in_array(NameImportingPostRector::class, $postRector, true)) { - $postRector[] = UseAddingPostRector::class; - } - sort($postRector); return array_merge($mainRector, $postRector); From b85dd22bc7fe5ad3fced4f250f7562f4acb14202 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sun, 21 Sep 2025 00:41:54 +0700 Subject: [PATCH 24/26] Really final touch: clean up --- src/Util/RectorClassesSorter.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Util/RectorClassesSorter.php b/src/Util/RectorClassesSorter.php index 54ba593beab..6a1777eb47e 100644 --- a/src/Util/RectorClassesSorter.php +++ b/src/Util/RectorClassesSorter.php @@ -30,7 +30,6 @@ public static function sort(array $rectorClasses): array $rectorClasses, fn (string $rectorClass): bool => is_a($rectorClass, PostRectorInterface::class, true) ); - sort($postRector); return array_merge($mainRector, $postRector); From 1206bbc3b3c6b12e22b57584ec0bb19cb8e0c67e Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sun, 21 Sep 2025 01:10:42 +0700 Subject: [PATCH 25/26] Really Really final touch: use call addRectorClassWithLine() itself on UseAddingPostRector when there is add to use --- .../Application/UseImportsAdder.php | 26 +++++++----------- src/PostRector/Rector/AbstractPostRector.php | 2 +- src/PostRector/Rector/UseAddingPostRector.php | 27 ++++++++++--------- 3 files changed, 25 insertions(+), 30 deletions(-) diff --git a/rules/CodingStyle/Application/UseImportsAdder.php b/rules/CodingStyle/Application/UseImportsAdder.php index aab797f5b01..7396ac4f327 100644 --- a/rules/CodingStyle/Application/UseImportsAdder.php +++ b/rules/CodingStyle/Application/UseImportsAdder.php @@ -15,7 +15,6 @@ use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory; use Rector\PhpParser\Node\CustomNode\FileWithoutNamespace; -use Rector\PostRector\Rector\UseAddingPostRector; use Rector\StaticTypeMapper\ValueObject\Type\AliasedObjectType; use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType; @@ -32,16 +31,14 @@ public function __construct( * @param array $useImportTypes * @param array $constantUseImportTypes * @param array $functionUseImportTypes - * @return Stmt[] */ public function addImportsToStmts( FileWithoutNamespace $fileWithoutNamespace, array $stmts, array $useImportTypes, array $constantUseImportTypes, - array $functionUseImportTypes, - UseAddingPostRector $useAddingPostRector - ): array { + array $functionUseImportTypes + ): bool { $usedImports = $this->usedImportsResolver->resolveForStmts($stmts); $existingUseImportTypes = $usedImports->getUseImports(); $existingConstantUseImports = $usedImports->getConstantImports(); @@ -59,7 +56,7 @@ public function addImportsToStmts( $newUses = $this->createUses($useImportTypes, $constantUseImportTypes, $functionUseImportTypes, null); if ($newUses === []) { - return [$fileWithoutNamespace]; + return false; } $stmts = array_values(array_filter($stmts, static function (Stmt $stmt): bool { @@ -96,9 +93,7 @@ public function addImportsToStmts( $fileWithoutNamespace->stmts = $stmts; $fileWithoutNamespace->stmts = array_values($fileWithoutNamespace->stmts); - $useAddingPostRector->addRectorClassWithLine($fileWithoutNamespace); - - return [$fileWithoutNamespace]; + return true; } $this->mirrorUseComments($stmts, $newUses); @@ -107,9 +102,7 @@ public function addImportsToStmts( $fileWithoutNamespace->stmts = array_merge($newUses, $this->resolveInsertNop($fileWithoutNamespace), $stmts); $fileWithoutNamespace->stmts = array_values($fileWithoutNamespace->stmts); - $useAddingPostRector->addRectorClassWithLine($fileWithoutNamespace); - - return [$fileWithoutNamespace]; + return true; } /** @@ -121,9 +114,8 @@ public function addImportsToNamespace( Namespace_ $namespace, array $useImportTypes, array $constantUseImportTypes, - array $functionUseImportTypes, - UseAddingPostRector $useAddingPostRector - ): void { + array $functionUseImportTypes + ): bool { $namespaceName = $this->getNamespaceName($namespace); $existingUsedImports = $this->usedImportsResolver->resolveForStmts($namespace->stmts); @@ -148,7 +140,7 @@ public function addImportsToNamespace( $newUses = $this->createUses($useImportTypes, $constantUseImportTypes, $functionUseImportTypes, $namespaceName); if ($newUses === []) { - return; + return false; } $this->mirrorUseComments($namespace->stmts, $newUses); @@ -156,7 +148,7 @@ public function addImportsToNamespace( $namespace->stmts = array_merge($newUses, $this->resolveInsertNop($namespace), $namespace->stmts); $namespace->stmts = array_values($namespace->stmts); - $useAddingPostRector->addRectorClassWithLine($namespace); + return true; } /** diff --git a/src/PostRector/Rector/AbstractPostRector.php b/src/PostRector/Rector/AbstractPostRector.php index 9dc5175faa9..341100666b9 100644 --- a/src/PostRector/Rector/AbstractPostRector.php +++ b/src/PostRector/Rector/AbstractPostRector.php @@ -36,7 +36,7 @@ public function getFile(): File return $this->file; } - public function addRectorClassWithLine(Node $node): void + protected function addRectorClassWithLine(Node $node): void { Assert::isInstanceOf($this->file, File::class); diff --git a/src/PostRector/Rector/UseAddingPostRector.php b/src/PostRector/Rector/UseAddingPostRector.php index daf28b27cbb..33f9d1ccb7a 100644 --- a/src/PostRector/Rector/UseAddingPostRector.php +++ b/src/PostRector/Rector/UseAddingPostRector.php @@ -61,13 +61,21 @@ public function beforeTraverse(array $nodes): array return $nodes; } - return $this->resolveNodesWithImportedUses( + if ($this->processNodesWithImportedUses( $nodes, $useImportTypes, $constantUseImportTypes, $functionUseImportTypes, $rootNode - ); + )) { + $this->addRectorClassWithLine($rootNode); + } + + if ($rootNode instanceof FileWithoutNamespace) { + return [$rootNode]; + } + + return $nodes; } public function enterNode(Node $node): int @@ -88,27 +96,23 @@ public function enterNode(Node $node): int * @param FullyQualifiedObjectType[] $useImportTypes * @param FullyQualifiedObjectType[] $constantUseImportTypes * @param FullyQualifiedObjectType[] $functionUseImportTypes - * @return Stmt[] */ - private function resolveNodesWithImportedUses( + private function processNodesWithImportedUses( array $nodes, array $useImportTypes, array $constantUseImportTypes, array $functionUseImportTypes, FileWithoutNamespace|Namespace_ $namespace - ): array { + ): bool { // A. has namespace? add under it if ($namespace instanceof Namespace_) { // then add, to prevent adding + removing false positive of same short use - $this->useImportsAdder->addImportsToNamespace( + return $this->useImportsAdder->addImportsToNamespace( $namespace, $useImportTypes, $constantUseImportTypes, - $functionUseImportTypes, - $this + $functionUseImportTypes ); - - return $nodes; } // B. no namespace? add in the top @@ -120,8 +124,7 @@ private function resolveNodesWithImportedUses( $nodes, $useImportTypes, $constantUseImportTypes, - $functionUseImportTypes, - $this + $functionUseImportTypes ); } From c245f802f89f9b2ea0e41e393c18034d255c95cc Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Sun, 21 Sep 2025 01:22:58 +0700 Subject: [PATCH 26/26] Really final touch: clean up logic --- src/PostRector/Rector/UseAddingPostRector.php | 28 +++++++------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/src/PostRector/Rector/UseAddingPostRector.php b/src/PostRector/Rector/UseAddingPostRector.php index 33f9d1ccb7a..2927abba72b 100644 --- a/src/PostRector/Rector/UseAddingPostRector.php +++ b/src/PostRector/Rector/UseAddingPostRector.php @@ -35,6 +35,9 @@ public function beforeTraverse(array $nodes): array } $rootNode = $this->resolveRootNode($nodes); + if (! $rootNode instanceof FileWithoutNamespace && ! $rootNode instanceof Namespace_) { + return $nodes; + } $useImportTypes = $this->useNodesToAddCollector->getObjectImportsByFilePath($this->getFile()->getFilePath()); $constantUseImportTypes = $this->useNodesToAddCollector->getConstantImportsByFilePath( @@ -52,17 +55,10 @@ public function beforeTraverse(array $nodes): array /** @var FullyQualifiedObjectType[] $useImportTypes */ $useImportTypes = $this->typeFactory->uniquateTypes($useImportTypes); + $stmts = $rootNode instanceof FileWithoutNamespace ? $rootNode->stmts : $nodes; - if ($rootNode instanceof FileWithoutNamespace) { - $nodes = $rootNode->stmts; - } - - if (! $rootNode instanceof FileWithoutNamespace && ! $rootNode instanceof Namespace_) { - return $nodes; - } - - if ($this->processNodesWithImportedUses( - $nodes, + if ($this->processStmtsWithImportedUses( + $stmts, $useImportTypes, $constantUseImportTypes, $functionUseImportTypes, @@ -71,10 +67,6 @@ public function beforeTraverse(array $nodes): array $this->addRectorClassWithLine($rootNode); } - if ($rootNode instanceof FileWithoutNamespace) { - return [$rootNode]; - } - return $nodes; } @@ -92,13 +84,13 @@ public function enterNode(Node $node): int } /** - * @param Stmt[] $nodes + * @param Stmt[] $stmts * @param FullyQualifiedObjectType[] $useImportTypes * @param FullyQualifiedObjectType[] $constantUseImportTypes * @param FullyQualifiedObjectType[] $functionUseImportTypes */ - private function processNodesWithImportedUses( - array $nodes, + private function processStmtsWithImportedUses( + array $stmts, array $useImportTypes, array $constantUseImportTypes, array $functionUseImportTypes, @@ -121,7 +113,7 @@ private function processNodesWithImportedUses( // then add, to prevent adding + removing false positive of same short use return $this->useImportsAdder->addImportsToStmts( $namespace, - $nodes, + $stmts, $useImportTypes, $constantUseImportTypes, $functionUseImportTypes