diff --git a/src/Analyser/RuleErrorTransformer.php b/src/Analyser/RuleErrorTransformer.php index ef730cac9f4..6f043096dc0 100644 --- a/src/Analyser/RuleErrorTransformer.php +++ b/src/Analyser/RuleErrorTransformer.php @@ -246,12 +246,14 @@ private function fix(string $fixingFile, array $fileNodes, array $fixes): ?Fixed return null; } - $nameResolver = new NameResolver(new Collecting(), ['replaceNodes' => false]); - /** @var Stmt[] $newStmts */ - $newStmts = (new NodeTraverser( - $nameResolver, - new NewClassNamesVisitor($nameResolver->getNameContext(), $this->importNames, $this->preferSelf), - ))->traverse($newStmts); + if (NewClassNamesVisitor::hasNewNames($replacingVisitor->getNewNodes())) { + $nameResolver = new NameResolver(new Collecting(), ['replaceNodes' => false]); + /** @var Stmt[] $newStmts */ + $newStmts = (new NodeTraverser( + $nameResolver, + new NewClassNamesVisitor($nameResolver->getNameContext(), $this->importNames, $this->preferSelf), + ))->traverse($newStmts); + } $newCode = $this->getPrinter($indent)->printFormatPreserving($newStmts, $fileNodes, $parsedFile['tokens']); if ($parsedFile['code'] === $newCode) { diff --git a/src/Fixable/NewClassNamesVisitor.php b/src/Fixable/NewClassNamesVisitor.php index e114f107c59..c27631354ce 100644 --- a/src/Fixable/NewClassNamesVisitor.php +++ b/src/Fixable/NewClassNamesVisitor.php @@ -95,6 +95,33 @@ public function __construct( $this->phpDocParser = new PhpDocParser($config, new TypeParser($config, $constExprParser), $constExprParser); } + /** + * Whether there is something to write in $newNodes, the nodes returned by fixes. + * The rest of the file is cloned unchanged, so the visitor would not change it. + * Traversing the whole file is skipped when this returns false, + * which matters in files with many fixable errors. + * + * @param Node[] $newNodes + */ + public static function hasNewNames(array $newNodes): bool + { + return (new NodeFinder())->findFirst($newNodes, static function (Node $node): bool { + if ($node instanceof FullyQualified && $node->getAttribute('origNode') === null) { + return true; + } + + $doc = $node->getDocComment(); + if ($doc === null) { + return false; + } + + $originalNode = $node->getAttribute('origNode'); + $originalDoc = $originalNode instanceof Node ? $originalNode->getDocComment() : null; + + return $originalDoc === null || $originalDoc->getText() !== $doc->getText(); + }) !== null; + } + #[Override] public function beforeTraverse(array $nodes): ?array { diff --git a/src/Fixable/ReplacingNodeVisitor.php b/src/Fixable/ReplacingNodeVisitor.php index 99081afc3b2..cf69a5f505e 100644 --- a/src/Fixable/ReplacingNodeVisitor.php +++ b/src/Fixable/ReplacingNodeVisitor.php @@ -33,6 +33,9 @@ final class ReplacingNodeVisitor extends NodeVisitorAbstract private int $appliedCount = 0; + /** @var list */ + private array $newNodes = []; + private bool $failed = false; /** @@ -115,9 +118,21 @@ public function leaveNode(Node $node): Node|int|null } } + $this->newNodes[] = $newNode; + return $newNode; } + /** + * The nodes returned by the fixes, the rest of the file is cloned unchanged. + * + * @return list + */ + public function getNewNodes(): array + { + return $this->newNodes; + } + /** * Whether every node was found and fixed. */