From 7556383dcf9a3aa035cf92276a71d8dcfda2f3aa Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Sun, 21 Dec 2025 23:57:05 +0100 Subject: [PATCH 1/2] [experiment] skip beforeTraverse() and afterTraverse() as never used --- .../SimplifyEmptyCheckOnEmptyArrayRector.php | 2 +- .../CompleteMissingIfElseBracketRector.php | 1 + .../Rector/NotEqual/CommonNotEqualRector.php | 3 +- ...rapEncapsedVariableInCurlyBracesRector.php | 3 +- .../Concat/RemoveConcatAutocastRector.php | 3 +- .../Rector/Ternary/TernaryToElvisRector.php | 3 +- .../Array_/LongArrayToShortArrayRector.php | 3 +- .../CurlyToSquareBracketArrayStringRector.php | 2 +- .../ParenthesizeNestedTernaryRector.php | 2 +- ...riableInStringInterpolationFixerRector.php | 3 +- .../NewMethodCallWithoutParenthesesRector.php | 3 +- .../ArrayDimFetch/ArrayFirstLastRector.php | 3 ++ .../Switch_/ColonAfterSwitchCaseRector.php | 3 +- .../TypedPropertyFromAssignsRector.php | 2 +- .../DeclareStrictTypesRector.php | 2 +- src/PostRector/Rector/AbstractPostRector.php | 3 +- src/Rector/AbstractRector.php | 49 ++++++++++++------- 17 files changed, 59 insertions(+), 31 deletions(-) diff --git a/rules/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector.php b/rules/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector.php index 0bed5b73a6c..0f36b480e0f 100644 --- a/rules/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector.php +++ b/rules/CodeQuality/Rector/Empty_/SimplifyEmptyCheckOnEmptyArrayRector.php @@ -152,7 +152,7 @@ private function isAllowedExpr(Expr $expr, Scope $scope): bool return false; } - $type = $this->allAssignNodePropertyTypeInferer->inferProperty($property, $classReflection, $this->file); + $type = $this->allAssignNodePropertyTypeInferer->inferProperty($property, $classReflection, $this->getFile()); if (! $type instanceof Type) { return false; } diff --git a/rules/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector.php b/rules/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector.php index 243b71cc2b8..8eaf9cfb1b0 100644 --- a/rules/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector.php +++ b/rules/CodeQuality/Rector/If_/CompleteMissingIfElseBracketRector.php @@ -70,6 +70,7 @@ public function refactor(Node $node): ?Node } $oldTokens = $this->file->getOldTokens(); + if ($this->isIfConditionFollowedByOpeningCurlyBracket($node, $oldTokens)) { return null; } diff --git a/rules/CodeQuality/Rector/NotEqual/CommonNotEqualRector.php b/rules/CodeQuality/Rector/NotEqual/CommonNotEqualRector.php index f443f49b641..cea0b7c876d 100644 --- a/rules/CodeQuality/Rector/NotEqual/CommonNotEqualRector.php +++ b/rules/CodeQuality/Rector/NotEqual/CommonNotEqualRector.php @@ -62,7 +62,8 @@ public function refactor(Node $node): ?NotEqual $tokenEndPos = $node->getEndTokenPos(); for ($i = $tokenStartPos; $i < $tokenEndPos; ++$i) { - $token = $this->file->getOldTokens()[$i]; + $token = $this->getFile() + ->getOldTokens()[$i]; if ((string) $token === '<>') { $token->text = '!='; diff --git a/rules/CodingStyle/Rector/Encapsed/WrapEncapsedVariableInCurlyBracesRector.php b/rules/CodingStyle/Rector/Encapsed/WrapEncapsedVariableInCurlyBracesRector.php index 160abb10e17..3d039823d08 100644 --- a/rules/CodingStyle/Rector/Encapsed/WrapEncapsedVariableInCurlyBracesRector.php +++ b/rules/CodingStyle/Rector/Encapsed/WrapEncapsedVariableInCurlyBracesRector.php @@ -52,7 +52,8 @@ public function getNodeTypes(): array public function refactor(Node $node): ?Node { $hasVariableBeenWrapped = false; - $oldTokens = $this->file->getOldTokens(); + $oldTokens = $this->getFile() + ->getOldTokens(); foreach ($node->parts as $index => $nodePart) { if ($nodePart instanceof Variable && $nodePart->getStartTokenPos() >= 0) { diff --git a/rules/DeadCode/Rector/Concat/RemoveConcatAutocastRector.php b/rules/DeadCode/Rector/Concat/RemoveConcatAutocastRector.php index 2ab1a6b09f7..6205fe4c468 100644 --- a/rules/DeadCode/Rector/Concat/RemoveConcatAutocastRector.php +++ b/rules/DeadCode/Rector/Concat/RemoveConcatAutocastRector.php @@ -79,7 +79,8 @@ private function removeStringCast(Expr $expr): Expr } $targetExpr = $expr->expr; - $tokens = $this->file->getOldTokens(); + $tokens = $this->getFile() + ->getOldTokens(); if ($expr->expr instanceof BinaryOp) { $castStartTokenPos = $expr->getStartTokenPos(); diff --git a/rules/Php53/Rector/Ternary/TernaryToElvisRector.php b/rules/Php53/Rector/Ternary/TernaryToElvisRector.php index 15e04e21f06..83b5ead1a5b 100644 --- a/rules/Php53/Rector/Ternary/TernaryToElvisRector.php +++ b/rules/Php53/Rector/Ternary/TernaryToElvisRector.php @@ -77,7 +77,8 @@ public function provideMinPhpVersion(): int private function isParenthesized(Expr $ifExpr, Expr $elseExpr): bool { - $tokens = $this->file->getOldTokens(); + $tokens = $this->getFile() + ->getOldTokens(); $ifExprTokenEnd = $ifExpr->getEndTokenPos(); $elseExprTokenStart = $elseExpr->getStartTokenPos(); diff --git a/rules/Php54/Rector/Array_/LongArrayToShortArrayRector.php b/rules/Php54/Rector/Array_/LongArrayToShortArrayRector.php index 59a5d8b3c9c..a0409e3af0a 100644 --- a/rules/Php54/Rector/Array_/LongArrayToShortArrayRector.php +++ b/rules/Php54/Rector/Array_/LongArrayToShortArrayRector.php @@ -78,7 +78,8 @@ public function refactor(Node $node): ?Node $node->setAttribute(AttributeKey::KIND, Array_::KIND_SHORT); - $tokens = $this->file->getOldTokens(); + $tokens = $this->getFile() + ->getOldTokens(); $startTokenPos = $node->getStartTokenPos(); $endTokenPos = $node->getEndTokenPos(); diff --git a/rules/Php74/Rector/ArrayDimFetch/CurlyToSquareBracketArrayStringRector.php b/rules/Php74/Rector/ArrayDimFetch/CurlyToSquareBracketArrayStringRector.php index 9e3bed6a019..a83d0a2c69e 100644 --- a/rules/Php74/Rector/ArrayDimFetch/CurlyToSquareBracketArrayStringRector.php +++ b/rules/Php74/Rector/ArrayDimFetch/CurlyToSquareBracketArrayStringRector.php @@ -63,7 +63,7 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { - if (! $this->isFollowedByCurlyBracket($this->file, $node)) { + if (! $this->isFollowedByCurlyBracket($this->getFile(), $node)) { return null; } diff --git a/rules/Php74/Rector/Ternary/ParenthesizeNestedTernaryRector.php b/rules/Php74/Rector/Ternary/ParenthesizeNestedTernaryRector.php index 2752fad0cfc..f84e0e31f24 100644 --- a/rules/Php74/Rector/Ternary/ParenthesizeNestedTernaryRector.php +++ b/rules/Php74/Rector/Ternary/ParenthesizeNestedTernaryRector.php @@ -68,7 +68,7 @@ public function refactor(Node $node): ?Node return null; } - if ($this->parenthesizedNestedTernaryAnalyzer->isParenthesized($this->file, $node)) { + if ($this->parenthesizedNestedTernaryAnalyzer->isParenthesized($this->getFile(), $node)) { return null; } diff --git a/rules/Php82/Rector/Encapsed/VariableInStringInterpolationFixerRector.php b/rules/Php82/Rector/Encapsed/VariableInStringInterpolationFixerRector.php index 354a989f4fe..3adf2a8cf47 100644 --- a/rules/Php82/Rector/Encapsed/VariableInStringInterpolationFixerRector.php +++ b/rules/Php82/Rector/Encapsed/VariableInStringInterpolationFixerRector.php @@ -51,7 +51,8 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { - $oldTokens = $this->file->getOldTokens(); + $oldTokens = $this->getFile() + ->getOldTokens(); $hasChanged = false; foreach ($node->parts as $part) { diff --git a/rules/Php84/Rector/MethodCall/NewMethodCallWithoutParenthesesRector.php b/rules/Php84/Rector/MethodCall/NewMethodCallWithoutParenthesesRector.php index bcc1d2ce61e..b559a7b1539 100644 --- a/rules/Php84/Rector/MethodCall/NewMethodCallWithoutParenthesesRector.php +++ b/rules/Php84/Rector/MethodCall/NewMethodCallWithoutParenthesesRector.php @@ -53,7 +53,8 @@ public function refactor(Node $node): ?Node return null; } - $oldTokens = $this->file->getOldTokens(); + $oldTokens = $this->getFile() + ->getOldTokens(); $loop = 1; while (isset($oldTokens[$node->var->getStartTokenPos() + $loop])) { diff --git a/rules/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector.php b/rules/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector.php index 067d5463399..11251db7b56 100644 --- a/rules/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector.php +++ b/rules/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector.php @@ -179,6 +179,7 @@ private function shouldSkip(ArrayDimFetch $arrayDimFetch, Node $scopeNode): bool if ($scope->isInExpressionAssign($arrayDimFetch)) { return true; } +<<<<<<< HEAD if ($arrayDimFetch->getAttribute(AttributeKey::IS_BEING_ASSIGNED) === true) { return true; @@ -188,6 +189,8 @@ private function shouldSkip(ArrayDimFetch $arrayDimFetch, Node $scopeNode): bool return true; } +======= +>>>>>>> ce85ccbfcc (fixup! [experiment] skip beforeTraverse() and afterTraverse() as never used) return (bool) $arrayDimFetch->getAttribute(AttributeKey::IS_UNSET_VAR); } } diff --git a/rules/Php85/Rector/Switch_/ColonAfterSwitchCaseRector.php b/rules/Php85/Rector/Switch_/ColonAfterSwitchCaseRector.php index 910f961640e..da74a4273ef 100644 --- a/rules/Php85/Rector/Switch_/ColonAfterSwitchCaseRector.php +++ b/rules/Php85/Rector/Switch_/ColonAfterSwitchCaseRector.php @@ -50,7 +50,8 @@ public function getNodeTypes(): array public function refactor(Node $node): ?Node { $hasChanged = false; - $oldTokens = $this->file->getOldTokens(); + $oldTokens = $this->getFile() + ->getOldTokens(); foreach ($node->cases as $key => $case) { $cond = $case->cond; diff --git a/rules/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector.php b/rules/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector.php index bbfe3fbf536..076f5a00b35 100644 --- a/rules/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector.php +++ b/rules/TypeDeclaration/Rector/Property/TypedPropertyFromAssignsRector.php @@ -157,7 +157,7 @@ public function refactor(Node $node): ?Node $inferredType = $this->allAssignNodePropertyTypeInferer->inferProperty( $property, $classReflection, - $this->file + $this->getFile() ); if (! $inferredType instanceof Type) { continue; diff --git a/rules/TypeDeclaration/Rector/StmtsAwareInterface/DeclareStrictTypesRector.php b/rules/TypeDeclaration/Rector/StmtsAwareInterface/DeclareStrictTypesRector.php index 7cb04dfe14e..db8e89ffd3a 100644 --- a/rules/TypeDeclaration/Rector/StmtsAwareInterface/DeclareStrictTypesRector.php +++ b/rules/TypeDeclaration/Rector/StmtsAwareInterface/DeclareStrictTypesRector.php @@ -63,7 +63,7 @@ function someFunction(int $number) public function refactor(Node $node): ?FileNode { // shebang files cannot have declare strict types - if ($this->file->hasShebang()) { + if ($this->getFile()->hasShebang()) { return null; } diff --git a/src/PostRector/Rector/AbstractPostRector.php b/src/PostRector/Rector/AbstractPostRector.php index 341100666b9..18736f9fbb3 100644 --- a/src/PostRector/Rector/AbstractPostRector.php +++ b/src/PostRector/Rector/AbstractPostRector.php @@ -41,6 +41,7 @@ protected function addRectorClassWithLine(Node $node): void Assert::isInstanceOf($this->file, File::class); $rectorWithLineChange = new RectorWithLineChange(static::class, $node->getStartLine()); - $this->file->addRectorClassWithLine($rectorWithLineChange); + $this->getFile() + ->addRectorClassWithLine($rectorWithLineChange); } } diff --git a/src/Rector/AbstractRector.php b/src/Rector/AbstractRector.php index 57ec3ea5774..bf1644cd54c 100644 --- a/src/Rector/AbstractRector.php +++ b/src/Rector/AbstractRector.php @@ -35,6 +35,9 @@ use Rector\Skipper\Skipper\Skipper; use Rector\ValueObject\Application\File; +/** + * @property-read File $file @deprecated Use $this->getFile() instead + */ abstract class AbstractRector extends NodeVisitorAbstract implements RectorInterface { private const string EMPTY_NODE_ARRAY_MESSAGE = <<getFile(); + } + + // fallback to default behavior + return $this->{$name}; + } + public function autowire( NodeNameResolver $nodeNameResolver, NodeTypeResolver $nodeTypeResolver, @@ -97,23 +108,12 @@ public function autowire( /** * @final Avoid override to prevent unintended side-effects. Use enterNode() or @see \Rector\Contract\PhpParser\DecoratingNodeVisitorInterface instead. - * * @internal * * @return Node[]|null */ public function beforeTraverse(array $nodes): ?array { - // workaround for file around refactor() - $file = $this->currentFileProvider->getFile(); - if (! $file instanceof File) { - throw new ShouldNotHappenException( - 'File object is missing. Make sure you call $this->currentFileProvider->setFile(...) before traversing.' - ); - } - - $this->file = $file; - return null; } @@ -122,11 +122,12 @@ public function beforeTraverse(array $nodes): ?array */ final public function enterNode(Node $node): int|Node|null|array { - if (is_a($this, HTMLAverseRectorInterface::class, true) && $this->file->containsHTML()) { + if (is_a($this, HTMLAverseRectorInterface::class, true) && $this->getFile()->containsHTML()) { return null; } - $filePath = $this->file->getFilePath(); + $filePath = $this->getFile() + ->getFilePath(); if ($this->skipper->shouldSkipCurrentNode($this, $filePath, static::class, $node)) { return null; } @@ -159,7 +160,8 @@ final public function enterNode(Node $node): int|Node|null|array // notify this rule changed code $rectorWithLineChange = new RectorWithLineChange(static::class, $originalNode->getStartLine()); - $this->file->addRectorClassWithLine($rectorWithLineChange); + $this->getFile() + ->addRectorClassWithLine($rectorWithLineChange); return $refactoredNodeOrState; } @@ -175,6 +177,18 @@ final public function leaveNode(Node $node): array|int|Node|null return null; } + protected function getFile(): File + { + $file = $this->currentFileProvider->getFile(); + if (! $file instanceof File) { + throw new ShouldNotHappenException( + 'File object is missing. Make sure you call $this->currentFileProvider->setFile(...) before traversing.' + ); + } + + return $file; + } + protected function isName(Node $node, string $name): bool { return $this->nodeNameResolver->isName($node, $name); @@ -257,7 +271,8 @@ private function postRefactorProcess( $this->createdByRuleDecorator->decorate($refactoredNode, $originalNode, static::class); $rectorWithLineChange = new RectorWithLineChange(static::class, $originalNode->getStartLine()); - $this->file->addRectorClassWithLine($rectorWithLineChange); + $this->getFile() + ->addRectorClassWithLine($rectorWithLineChange); /** @var MutatingScope|null $currentScope */ $currentScope = $node->getAttribute(AttributeKey::SCOPE); From b8c95deec7547c8cc60aa47b1042a5b8f52f107f Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Thu, 5 Feb 2026 00:34:44 +0100 Subject: [PATCH 2/2] remove array item scope, as handled in array --- .../Rector/Coalesce/CoalesceToTernaryRector.php | 6 +++--- .../AddOverrideAttributeToOverriddenMethodsRector.php | 3 ++- .../Rector/ArrayDimFetch/ArrayFirstLastRector.php | 3 --- src/Console/Command/WorkerCommand.php | 11 +++++++++-- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/rules/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector.php b/rules/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector.php index e582396dcbb..2c8c95f00e2 100644 --- a/rules/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector.php +++ b/rules/CodeQuality/Rector/Coalesce/CoalesceToTernaryRector.php @@ -4,18 +4,18 @@ namespace Rector\CodeQuality\Rector\Coalesce; -use PHPStan\Type\UnionType; -use PHPStan\Type\NullType; use PhpParser\Node; use PhpParser\Node\Expr\ArrayDimFetch; use PhpParser\Node\Expr\BinaryOp\Coalesce; use PhpParser\Node\Expr\Ternary; use PHPStan\Type\ErrorType; use PHPStan\Type\MixedType; +use PHPStan\Type\NullType; +use PHPStan\Type\UnionType; +use Rector\PHPStan\ScopeFetcher; use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; -use Rector\PHPStan\ScopeFetcher; /** * @see \Rector\Tests\CodeQuality\Rector\Coalesce\CoalesceToTernaryRector\CoalesceToTernaryRectorTest diff --git a/rules/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector.php b/rules/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector.php index 9138ae4ddba..4d98e6200af 100644 --- a/rules/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector.php +++ b/rules/Php83/Rector/ClassMethod/AddOverrideAttributeToOverriddenMethodsRector.php @@ -4,6 +4,7 @@ namespace Rector\Php83\Rector\ClassMethod; +use PhpParser\Node\Name; use PhpParser\Node; use PhpParser\Node\Attribute; use PhpParser\Node\AttributeGroup; @@ -376,7 +377,7 @@ private function resolveClassMethodFromTraitUse(ClassLike $classLike, string $me // early return for the class if it does not extend anything private function shouldSkipNode(Class_ $class): bool { - if ($class->extends !== null) { + if ($class->extends instanceof Name) { return false; } diff --git a/rules/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector.php b/rules/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector.php index 11251db7b56..067d5463399 100644 --- a/rules/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector.php +++ b/rules/Php85/Rector/ArrayDimFetch/ArrayFirstLastRector.php @@ -179,7 +179,6 @@ private function shouldSkip(ArrayDimFetch $arrayDimFetch, Node $scopeNode): bool if ($scope->isInExpressionAssign($arrayDimFetch)) { return true; } -<<<<<<< HEAD if ($arrayDimFetch->getAttribute(AttributeKey::IS_BEING_ASSIGNED) === true) { return true; @@ -189,8 +188,6 @@ private function shouldSkip(ArrayDimFetch $arrayDimFetch, Node $scopeNode): bool return true; } -======= ->>>>>>> ce85ccbfcc (fixup! [experiment] skip beforeTraverse() and afterTraverse() as never used) return (bool) $arrayDimFetch->getAttribute(AttributeKey::IS_UNSET_VAR); } } diff --git a/src/Console/Command/WorkerCommand.php b/src/Console/Command/WorkerCommand.php index 5bffbae23a6..b86320a9cd8 100644 --- a/src/Console/Command/WorkerCommand.php +++ b/src/Console/Command/WorkerCommand.php @@ -131,7 +131,12 @@ private function runWorker( $encoder->on(ReactEvent::ERROR, $handleErrorCallback); // 2. collect diffs + errors from file processor - $decoder->on(ReactEvent::DATA, function (array $json) use ($preFileCallback, $encoder, $configuration, $input): void { + $decoder->on(ReactEvent::DATA, function (array $json) use ( + $preFileCallback, + $encoder, + $configuration, + $input + ): void { $action = $json[ReactCommand::ACTION]; if ($action !== Action::MAIN) { return; @@ -154,7 +159,9 @@ private function runWorker( $encoder->write([ ReactCommand::ACTION => Action::RESULT, self::RESULT => [ - Bridge::FILE_DIFFS => $processResult->getFileDiffs($input->getOption(Option::OUTPUT_FORMAT) !== 'json'), + Bridge::FILE_DIFFS => $processResult->getFileDiffs( + $input->getOption(Option::OUTPUT_FORMAT) !== 'json' + ), Bridge::FILES_COUNT => count($filePaths), Bridge::SYSTEM_ERRORS => $processResult->getSystemErrors(), Bridge::SYSTEM_ERRORS_COUNT => count($processResult->getSystemErrors()),