diff --git a/bench/unused_import_removing_bench.php b/bench/unused_import_removing_bench.php index 2cb5e010edc..503a789e5b7 100644 --- a/bench/unused_import_removing_bench.php +++ b/bench/unused_import_removing_bench.php @@ -52,6 +52,8 @@ }; $file = $testingParser->parseFilePathToFile($sourceFilePath); +$postRector->setFile($file); + $fileNode = $file->getFileNode(); assert($fileNode instanceof FileNode); diff --git a/src/PostRector/Rector/UnusedImportRemovingPostRector.php b/src/PostRector/Rector/UnusedImportRemovingPostRector.php index f45fc2f640a..d3ef58d3c3d 100644 --- a/src/PostRector/Rector/UnusedImportRemovingPostRector.php +++ b/src/PostRector/Rector/UnusedImportRemovingPostRector.php @@ -21,9 +21,17 @@ use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\PhpDocParser\NodeTraverser\SimpleCallableNodeTraverser; use Rector\PhpParser\Node\FileNode; +use Rector\ValueObject\Application\File; final class UnusedImportRemovingPostRector extends AbstractPostRector { + private ?File $usedNamesFile = null; + + /** + * @var array + */ + private array $usedNamesCache = []; + public function __construct( private readonly SimpleCallableNodeTraverser $simpleCallableNodeTraverser, private readonly PhpDocInfoFactory $phpDocInfoFactory, @@ -198,11 +206,32 @@ private function findNamesInDocBlocks(Namespace_|FileNode $rootNode): array */ private function resolveUsedPhpAndDocNames(Namespace_|FileNode $rootNode): array { + // the referenced names only change with a new stmts version, while the post-rector chain + // re-runs this node across convergence iterations, so cache per (file, version) + $file = $this->getFile(); + if ($file !== $this->usedNamesFile) { + $this->usedNamesFile = $file; + $this->usedNamesCache = []; + } + + $version = $file->getNewStmtsVersion(); + $objectId = spl_object_id($rootNode); + + $cached = $this->usedNamesCache[$objectId] ?? null; + if ($cached !== null && $cached['version'] === $version) { + return $cached['names']; + } + $phpNames = $this->findNonUseImportNames($rootNode); $docBlockNames = $this->findNamesInDocBlocks($rootNode); - $names = [...$phpNames, ...$docBlockNames]; - return array_unique($names); + $names = array_unique([...$phpNames, ...$docBlockNames]); + $this->usedNamesCache[$objectId] = [ + 'version' => $version, + 'names' => $names, + ]; + + return $names; } /**