From 55bfdc4078cfd43f43828992ec3ce94023ba07b2 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Wed, 19 Nov 2025 12:37:02 +0000 Subject: [PATCH 01/10] [stmts-aware] kick of custom node parser to use StmtsAwareNode --- config/phpstan/parser.neon | 6 ++++ .../Fixture/fixture5.php | 12 +++++++ .../ClassMethod/Php4ConstructorRector.php | 22 +++++++++++++ .../StmtsAwareWrappingNodeVisitor.php | 25 +++++++++++++++ .../Node/CustomNode/StmtsAwareNode.php | 31 +++++++++++++++++++ 5 files changed, 96 insertions(+) create mode 100644 rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/fixture5.php create mode 100644 src/PHPStan/NodeVisitor/StmtsAwareWrappingNodeVisitor.php create mode 100644 src/PhpParser/Node/CustomNode/StmtsAwareNode.php diff --git a/config/phpstan/parser.neon b/config/phpstan/parser.neon index 3b0f385dce2..65f55e07598 100644 --- a/config/phpstan/parser.neon +++ b/config/phpstan/parser.neon @@ -25,3 +25,9 @@ services: arguments: parser: @currentPhpVersionPhpParser autowired: no + + # use StmtsAwareNode + - + class: Rector\PHPStan\NodeVisitor\StmtsAwareWrappingNodeVisitor + tags: + - phpstan.parser.richParserNodeVisitor diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/fixture5.php b/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/fixture5.php new file mode 100644 index 00000000000..bd59aeb28f7 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/fixture5.php @@ -0,0 +1,12 @@ +getContent(), "\n")) { + return false; + } + + return true; +} + +?> diff --git a/rules/Php70/Rector/ClassMethod/Php4ConstructorRector.php b/rules/Php70/Rector/ClassMethod/Php4ConstructorRector.php index 976454d3423..66261e4d19e 100644 --- a/rules/Php70/Rector/ClassMethod/Php4ConstructorRector.php +++ b/rules/Php70/Rector/ClassMethod/Php4ConstructorRector.php @@ -97,6 +97,7 @@ public function refactor(Node $node): Class_|null return null; } +<<<<<<< HEAD $className = $this->getName($node); if (! is_string($className)) { return null; @@ -105,6 +106,27 @@ public function refactor(Node $node): Class_|null foreach ($node->stmts as $classStmtKey => $classStmt) { if (! $classStmt instanceof ClassMethod) { continue; +======= + // process parent call references first + $this->processClassMethodStatementsForParentConstructorCalls($psr4ConstructorMethod, $scope); + + // does it already have a "__construct" method? + if (! $node->getMethod(MethodName::CONSTRUCT) instanceof ClassMethod) { + $psr4ConstructorMethod->name = new Identifier(MethodName::CONSTRUCT); + } + + $classMethodStmts = $psr4ConstructorMethod->stmts; + if ($classMethodStmts === null) { + return null; + } + + $parentClassName = $classReflection->getParentClass() instanceof ClassReflection + ? $classReflection->getParentClass()->getName() : ''; + + foreach ($classMethodStmts as $classMethodStmt) { + if (! $classMethodStmt instanceof Expression) { + return null; +>>>>>>> ea2ce07c12 (fixup! fixup! fixup! fixup! [stmts-aware] kick of custom node parser to use StmtsAwareNode) } if (! $this->php4ConstructorClassMethodAnalyzer->detect($classStmt, $classReflection)) { diff --git a/src/PHPStan/NodeVisitor/StmtsAwareWrappingNodeVisitor.php b/src/PHPStan/NodeVisitor/StmtsAwareWrappingNodeVisitor.php new file mode 100644 index 00000000000..8c92f655d71 --- /dev/null +++ b/src/PHPStan/NodeVisitor/StmtsAwareWrappingNodeVisitor.php @@ -0,0 +1,25 @@ + Date: Wed, 19 Nov 2025 15:47:30 +0000 Subject: [PATCH 02/10] fix printer --- .../Fixture/constant_scalar_type_.php | 24 +++++++++++++++++++ .../Node/CustomNode/StmtsAwareNode.php | 5 ++++ .../Printer/BetterStandardPrinter.php | 14 +++++++++++ 3 files changed, 43 insertions(+) create mode 100644 rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/constant_scalar_type_.php diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/constant_scalar_type_.php b/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/constant_scalar_type_.php new file mode 100644 index 00000000000..47d93537803 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/constant_scalar_type_.php @@ -0,0 +1,24 @@ +getValue()) { + return false; + } + + return true; + } +} + +?> diff --git a/src/PhpParser/Node/CustomNode/StmtsAwareNode.php b/src/PhpParser/Node/CustomNode/StmtsAwareNode.php index 3dd14fc5c3e..2c4de7aa308 100644 --- a/src/PhpParser/Node/CustomNode/StmtsAwareNode.php +++ b/src/PhpParser/Node/CustomNode/StmtsAwareNode.php @@ -28,4 +28,9 @@ public function getSubNodeNames(): array // empty on purpose to avoid infinity loop return []; } + + public function getOriginalNode(): \PhpParser\Node + { + return $this->originalNode; + } } diff --git a/src/PhpParser/Printer/BetterStandardPrinter.php b/src/PhpParser/Printer/BetterStandardPrinter.php index 7b0adc67a78..d25efbad844 100644 --- a/src/PhpParser/Printer/BetterStandardPrinter.php +++ b/src/PhpParser/Printer/BetterStandardPrinter.php @@ -37,6 +37,7 @@ use Rector\NodeAnalyzer\ExprAnalyzer; use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\PhpParser\Node\CustomNode\FileWithoutNamespace; +use Rector\PhpParser\Node\CustomNode\StmtsAwareNode; use Rector\Util\NewLineSplitter; use Rector\Util\Reflection\PrivatesAccessor; use Rector\Util\StringUtils; @@ -273,6 +274,19 @@ protected function pArray( // reindex positions for printer $nodes = array_values($nodes); + // unwrap StmtsAwareNode to make their printing work + foreach ($nodes as $key => $node) { + if ($node instanceof StmtsAwareNode) { + $nodes[$key] = $node->getOriginalNode(); + } + } + + foreach ($origNodes as $key => $origNode) { + if ($origNode instanceof StmtsAwareNode) { + $origNodes[$key] = $origNode->getOriginalNode(); + } + } + $content = parent::pArray($nodes, $origNodes, $pos, $indentAdjustment, $parentNodeClass, $subNodeName, $fixup); if ($content === null) { return $content; From 81925499f7df332140f4404067fed29f799c9141 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Wed, 19 Nov 2025 15:48:04 +0000 Subject: [PATCH 03/10] array --- config/phpstan/parser.neon | 8 ++--- .../Fixture/constant_scalar_type_.php | 24 --------------- .../Fixture/fixture5.php | 12 -------- .../Rector/If_/SimplifyIfReturnBoolRector.php | 4 +++ ...UnusedNonEmptyArrayBeforeForeachRector.php | 6 ++-- .../Node/CustomNode/StmtsAwareNode.php | 19 ++++++++---- src/PhpParser/NodeGroups.php | 30 +++++++++++++++++++ .../AbstractImmutableNodeTraverser.php | 1 + .../NodeTraverser/RectorNodeTraverser.php | 17 +++++++++++ src/Rector/AbstractRector.php | 7 +++++ 10 files changed, 80 insertions(+), 48 deletions(-) delete mode 100644 rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/constant_scalar_type_.php delete mode 100644 rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/fixture5.php create mode 100644 src/PhpParser/NodeGroups.php diff --git a/config/phpstan/parser.neon b/config/phpstan/parser.neon index 65f55e07598..bf053eec374 100644 --- a/config/phpstan/parser.neon +++ b/config/phpstan/parser.neon @@ -27,7 +27,7 @@ services: autowired: no # use StmtsAwareNode - - - class: Rector\PHPStan\NodeVisitor\StmtsAwareWrappingNodeVisitor - tags: - - phpstan.parser.richParserNodeVisitor +# - +# class: Rector\PHPStan\NodeVisitor\StmtsAwareWrappingNodeVisitor +# tags: +# - phpstan.parser.richParserNodeVisitor diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/constant_scalar_type_.php b/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/constant_scalar_type_.php deleted file mode 100644 index 47d93537803..00000000000 --- a/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/constant_scalar_type_.php +++ /dev/null @@ -1,24 +0,0 @@ -getValue()) { - return false; - } - - return true; - } -} - -?> diff --git a/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/fixture5.php b/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/fixture5.php deleted file mode 100644 index bd59aeb28f7..00000000000 --- a/rules-tests/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector/Fixture/fixture5.php +++ /dev/null @@ -1,12 +0,0 @@ -getContent(), "\n")) { - return false; - } - - return true; -} - -?> diff --git a/rules/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector.php b/rules/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector.php index 12d21ee35f6..65d9ffaacf5 100644 --- a/rules/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector.php +++ b/rules/CodeQuality/Rector/If_/SimplifyIfReturnBoolRector.php @@ -76,6 +76,10 @@ public function refactor(Node $node): ?Node } foreach ($node->stmts as $key => $stmt) { + if ($key === 0) { + continue; + } + if (! $stmt instanceof Return_) { continue; } diff --git a/rules/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php b/rules/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php index d328de2e17c..26071716ec3 100644 --- a/rules/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php +++ b/rules/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php @@ -83,12 +83,12 @@ public function run() */ public function getNodeTypes(): array { - return [If_::class, StmtsAwareInterface::class]; + return [StmtsAwareInterface::class]; } /** - * @param If_|StmtsAwareInterface $node - * @return Foreach_|StmtsAwareInterface|null + * @param StmtsAwareInterface $node + * @return StmtsAwareInterface|null */ public function refactor(Node $node): Node|null { diff --git a/src/PhpParser/Node/CustomNode/StmtsAwareNode.php b/src/PhpParser/Node/CustomNode/StmtsAwareNode.php index 2c4de7aa308..3d9a3aaa3ab 100644 --- a/src/PhpParser/Node/CustomNode/StmtsAwareNode.php +++ b/src/PhpParser/Node/CustomNode/StmtsAwareNode.php @@ -5,23 +5,32 @@ namespace Rector\PhpParser\Node\CustomNode; use PhpParser\Node\Stmt; -use PhpParser\NodeAbstract; +use Rector\Contract\PhpParser\Node\StmtsAwareInterface; -final class StmtsAwareNode extends Stmt +final class StmtsAwareNode extends Stmt implements StmtsAwareInterface { public function __construct( - public \PhpParser\Node $originalNode + public Stmt\If_ $originalNode ) { parent::__construct(); } public function getType(): string { - return 'Node_StmtsAware'; + return 'Rector_Node_StmtsAware'; + } + + public function &__get($property) + { + if ($property === 'stmts') { + return $this->originalNode->stmts; + } + + return $this->{$property}; } /** - * @var string + * @var string[] */ public function getSubNodeNames(): array { diff --git a/src/PhpParser/NodeGroups.php b/src/PhpParser/NodeGroups.php new file mode 100644 index 00000000000..8c539d0d9ca --- /dev/null +++ b/src/PhpParser/NodeGroups.php @@ -0,0 +1,30 @@ +> + */ + public const STMTS_AWARE_NODES = [ + \PhpParser\Node\Expr\Closure::class, + \PhpParser\Node\Stmt\Block::class, + \PhpParser\Node\Stmt\Case_::class, + \PhpParser\Node\Stmt\Catch_::class, + \PhpParser\Node\Stmt\ClassMethod::class, + \PhpParser\Node\Stmt\Do_::class, + \PhpParser\Node\Stmt\ElseIf_::class, + \PhpParser\Node\Stmt\Else_::class, + \PhpParser\Node\Stmt\Finally_::class, + \PhpParser\Node\Stmt\For_::class, + \PhpParser\Node\Stmt\Foreach_::class, + \PhpParser\Node\Stmt\Function_::class, + \PhpParser\Node\Stmt\If_::class, + \PhpParser\Node\Stmt\Namespace_::class, + \PhpParser\Node\Stmt\TryCatch::class, + \PhpParser\Node\Stmt\While_::class, + ]; +} diff --git a/src/PhpParser/NodeTraverser/AbstractImmutableNodeTraverser.php b/src/PhpParser/NodeTraverser/AbstractImmutableNodeTraverser.php index 0aeea1d7918..66953e2b65d 100644 --- a/src/PhpParser/NodeTraverser/AbstractImmutableNodeTraverser.php +++ b/src/PhpParser/NodeTraverser/AbstractImmutableNodeTraverser.php @@ -210,6 +210,7 @@ protected function traverseArray(array $nodes): array $traverseChildren = true; $visitorIndex = -1; $currentNodeVisitors = $this->getVisitorsForNode($node); + foreach ($currentNodeVisitors as $visitorIndex => $visitor) { $return = $visitor->enterNode($node); if ($return !== null) { diff --git a/src/PhpParser/NodeTraverser/RectorNodeTraverser.php b/src/PhpParser/NodeTraverser/RectorNodeTraverser.php index f80922a89bc..90f5622452a 100644 --- a/src/PhpParser/NodeTraverser/RectorNodeTraverser.php +++ b/src/PhpParser/NodeTraverser/RectorNodeTraverser.php @@ -8,6 +8,7 @@ use PhpParser\Node\Stmt; use PhpParser\NodeVisitor; use Rector\Configuration\ConfigurationRuleFilter; +use Rector\Contract\PhpParser\Node\StmtsAwareInterface; use Rector\Contract\Rector\RectorInterface; use Rector\VersionBonding\PhpVersionedFilter; @@ -67,9 +68,25 @@ public function getVisitorsForNode(Node $node): array if (! isset($this->visitorsPerNodeClass[$nodeClass])) { $this->visitorsPerNodeClass[$nodeClass] = []; + /** @var RectorInterface $visitor */ + foreach ($this->visitors as $visitor) { foreach ($visitor->getNodeTypes() as $nodeType) { + // special case for StmtsAwareInterface + if ($nodeType === StmtsAwareInterface::class) { + if (property_exists($node, 'stmts')) { + // all stmts are node classes + foreach ([Stmt\If_::class, Stmt\Function_::class, Stmt\ClassMethod::class, Stmt\For_::class, Stmt\While_::class] as $stmtsAwareNodeClass) { + dump($stmtsAwareNodeClass); + dump($visitor::class); + + $this->visitorsPerNodeClass[$stmtsAwareNodeClass][] = $visitor; + } + continue 2; + } + } + if (is_a($nodeClass, $nodeType, true)) { $this->visitorsPerNodeClass[$nodeClass][] = $visitor; continue 2; diff --git a/src/Rector/AbstractRector.php b/src/Rector/AbstractRector.php index 789560ff739..03c36231525 100644 --- a/src/Rector/AbstractRector.php +++ b/src/Rector/AbstractRector.php @@ -23,6 +23,7 @@ use Rector\Application\Provider\CurrentFileProvider; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; use Rector\ChangesReporting\ValueObject\RectorWithLineChange; +use Rector\Contract\PhpParser\Node\StmtsAwareInterface; use Rector\Contract\Rector\HTMLAverseRectorInterface; use Rector\Contract\Rector\RectorInterface; use Rector\Exception\ShouldNotHappenException; @@ -375,6 +376,12 @@ private function isMatchingNodeType(Node $node): bool { $nodeClass = $node::class; foreach ($this->getNodeTypes() as $nodeType) { + if ($nodeType === StmtsAwareInterface::class) { + if ($node instanceof ClassMethod) { + return true; + } + } + if (is_a($nodeClass, $nodeType, true)) { return true; } From 817dcd99c52c86e1dca3873dea6c0a5290679f16 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Wed, 19 Nov 2025 16:18:04 +0000 Subject: [PATCH 04/10] working prototype for ClassMethod --- src/PhpParser/NodeGroups.php | 4 ++++ .../NodeTraverser/RectorNodeTraverser.php | 17 +++++++---------- src/Rector/AbstractRector.php | 7 +++++-- 3 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/PhpParser/NodeGroups.php b/src/PhpParser/NodeGroups.php index 8c539d0d9ca..a1b4250a9d6 100644 --- a/src/PhpParser/NodeGroups.php +++ b/src/PhpParser/NodeGroups.php @@ -4,6 +4,8 @@ namespace Rector\PhpParser; +use Rector\PhpParser\Node\CustomNode\FileWithoutNamespace; + final class NodeGroups { /** @@ -26,5 +28,7 @@ final class NodeGroups \PhpParser\Node\Stmt\Namespace_::class, \PhpParser\Node\Stmt\TryCatch::class, \PhpParser\Node\Stmt\While_::class, + // custom Rector node + FileWithoutNamespace::class, ]; } diff --git a/src/PhpParser/NodeTraverser/RectorNodeTraverser.php b/src/PhpParser/NodeTraverser/RectorNodeTraverser.php index 90f5622452a..8fb4afda29d 100644 --- a/src/PhpParser/NodeTraverser/RectorNodeTraverser.php +++ b/src/PhpParser/NodeTraverser/RectorNodeTraverser.php @@ -10,6 +10,7 @@ use Rector\Configuration\ConfigurationRuleFilter; use Rector\Contract\PhpParser\Node\StmtsAwareInterface; use Rector\Contract\Rector\RectorInterface; +use Rector\PhpParser\NodeGroups; use Rector\VersionBonding\PhpVersionedFilter; /** @@ -74,17 +75,13 @@ public function getVisitorsForNode(Node $node): array foreach ($this->visitors as $visitor) { foreach ($visitor->getNodeTypes() as $nodeType) { // special case for StmtsAwareInterface - if ($nodeType === StmtsAwareInterface::class) { - if (property_exists($node, 'stmts')) { - // all stmts are node classes - foreach ([Stmt\If_::class, Stmt\Function_::class, Stmt\ClassMethod::class, Stmt\For_::class, Stmt\While_::class] as $stmtsAwareNodeClass) { - dump($stmtsAwareNodeClass); - dump($visitor::class); - - $this->visitorsPerNodeClass[$stmtsAwareNodeClass][] = $visitor; - } - continue 2; + if ($nodeType === StmtsAwareInterface::class && property_exists($node, 'stmts')) { + // all stmts are node classes + foreach (NodeGroups::STMTS_AWARE_NODES as $stmtsAwareNodeClass) { + $this->visitorsPerNodeClass[$stmtsAwareNodeClass][] = $visitor; } + + continue 2; } if (is_a($nodeClass, $nodeType, true)) { diff --git a/src/Rector/AbstractRector.php b/src/Rector/AbstractRector.php index 03c36231525..cd8fcd2b05d 100644 --- a/src/Rector/AbstractRector.php +++ b/src/Rector/AbstractRector.php @@ -34,6 +34,7 @@ use Rector\PhpDocParser\NodeTraverser\SimpleCallableNodeTraverser; use Rector\PhpParser\Comparing\NodeComparator; use Rector\PhpParser\Node\NodeFactory; +use Rector\PhpParser\NodeGroups; use Rector\Skipper\Skipper\Skipper; use Rector\ValueObject\Application\File; @@ -377,8 +378,10 @@ private function isMatchingNodeType(Node $node): bool $nodeClass = $node::class; foreach ($this->getNodeTypes() as $nodeType) { if ($nodeType === StmtsAwareInterface::class) { - if ($node instanceof ClassMethod) { - return true; + foreach (NodeGroups::STMTS_AWARE_NODES as $stmtsAwareNodeClass) { + if (is_a($nodeClass, $stmtsAwareNodeClass, true)) { + return true; + } } } From 2ac4572b08d356fa00369658d33aa2af23ba4a17 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Wed, 19 Nov 2025 16:20:45 +0000 Subject: [PATCH 05/10] fixes --- composer.json | 17 ------- .../SimplifyUselessVariableRector.php | 9 +++- .../If_/SimplifyIfNotNullReturnRector.php | 3 +- .../ClassMethod/Php4ConstructorRector.php | 3 +- src/NodeManipulator/StmtsManipulator.php | 11 ++--- .../StmtsAwareWrappingNodeVisitor.php | 25 ----------- .../Node/CustomNode/StmtsAwareNode.php | 45 ------------------- .../Printer/BetterStandardPrinter.php | 14 ------ 8 files changed, 17 insertions(+), 110 deletions(-) delete mode 100644 src/PHPStan/NodeVisitor/StmtsAwareWrappingNodeVisitor.php delete mode 100644 src/PhpParser/Node/CustomNode/StmtsAwareNode.php diff --git a/composer.json b/composer.json index ca495d81f38..a9096fe472e 100644 --- a/composer.json +++ b/composer.json @@ -118,23 +118,6 @@ "patches": { "illuminate/container": [ "https://raw.githubusercontent.com/rectorphp/vendor-patches/main/patches/illuminate-container-container-php.patch" - ], - "nikic/php-parser": [ - "https://raw.githubusercontent.com/rectorphp/vendor-patches/main/patches/nikic-php-parser-lib-phpparser-node-expr-closure-php.patch", - "https://raw.githubusercontent.com/rectorphp/vendor-patches/main/patches/nikic-php-parser-lib-phpparser-node-stmt-finally-php.patch", - "https://raw.githubusercontent.com/rectorphp/vendor-patches/main/patches/nikic-php-parser-lib-phpparser-node-stmt-function-php.patch", - "https://raw.githubusercontent.com/rectorphp/vendor-patches/main/patches/nikic-php-parser-lib-phpparser-node-stmt-do-php.patch", - "https://raw.githubusercontent.com/rectorphp/vendor-patches/main/patches/nikic-php-parser-lib-phpparser-node-stmt-catch-php.patch", - "https://raw.githubusercontent.com/rectorphp/vendor-patches/main/patches/nikic-php-parser-lib-phpparser-node-stmt-trycatch-php.patch", - "https://raw.githubusercontent.com/rectorphp/vendor-patches/main/patches/nikic-php-parser-lib-phpparser-node-stmt-for-php.patch", - "https://raw.githubusercontent.com/rectorphp/vendor-patches/main/patches/nikic-php-parser-lib-phpparser-node-stmt-classmethod-php.patch", - "https://raw.githubusercontent.com/rectorphp/vendor-patches/main/patches/nikic-php-parser-lib-phpparser-node-stmt-else-php.patch", - "https://raw.githubusercontent.com/rectorphp/vendor-patches/main/patches/nikic-php-parser-lib-phpparser-node-stmt-while-php.patch", - "https://raw.githubusercontent.com/rectorphp/vendor-patches/main/patches/nikic-php-parser-lib-phpparser-node-stmt-foreach-php.patch", - "https://raw.githubusercontent.com/rectorphp/vendor-patches/main/patches/nikic-php-parser-lib-phpparser-node-stmt-if-php.patch", - "https://raw.githubusercontent.com/rectorphp/vendor-patches/main/patches/nikic-php-parser-lib-phpparser-node-stmt-case-php.patch", - "https://raw.githubusercontent.com/rectorphp/vendor-patches/main/patches/nikic-php-parser-lib-phpparser-node-stmt-elseif-php.patch", - "https://raw.githubusercontent.com/rectorphp/vendor-patches/main/patches/nikic-php-parser-lib-phpparser-node-stmt-namespace-php.patch" ] }, "composer-exit-on-patch-failure": true, diff --git a/rules/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector.php b/rules/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector.php index 0f9714edbac..27029e36f4e 100644 --- a/rules/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector.php +++ b/rules/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector.php @@ -8,6 +8,7 @@ use PhpParser\Node; use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\AssignOp; +use PhpParser\Node\Expr\Closure; use PhpParser\Node\Expr\Ternary; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Stmt; @@ -151,12 +152,16 @@ public function refactor(Node $node): ?Node return null; } + /** + * @param StmtsAwareInterface $stmtsAware + * @return StmtsAwareInterface + */ private function processSimplifyUselessVariable( - StmtsAwareInterface $stmtsAware, + Stmt|Closure $stmtsAware, Return_ $return, Assign|AssignOp $assign, int $key - ): ?StmtsAwareInterface { + ): Stmt|Closure|null { if (! $assign instanceof Assign) { $binaryClass = $this->assignAndBinaryMap->getAlternative($assign); if ($binaryClass === null) { diff --git a/rules/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector.php b/rules/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector.php index 48c0b83880f..b2c2670a8f4 100644 --- a/rules/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector.php +++ b/rules/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector.php @@ -6,6 +6,7 @@ use PhpParser\Node; use PhpParser\Node\Expr; +use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Else_; use PhpParser\Node\Stmt\If_; use PhpParser\Node\Stmt\Return_; @@ -62,7 +63,7 @@ public function getNodeTypes(): array /** * @param StmtsAwareInterface $node */ - public function refactor(Node $node): ?StmtsAwareInterface + public function refactor(Node $node): ?Stmt { foreach ((array) $node->stmts as $key => $stmt) { if (! $stmt instanceof If_) { diff --git a/rules/Php70/Rector/ClassMethod/Php4ConstructorRector.php b/rules/Php70/Rector/ClassMethod/Php4ConstructorRector.php index 66261e4d19e..0d2b84c63bb 100644 --- a/rules/Php70/Rector/ClassMethod/Php4ConstructorRector.php +++ b/rules/Php70/Rector/ClassMethod/Php4ConstructorRector.php @@ -121,7 +121,8 @@ public function refactor(Node $node): Class_|null } $parentClassName = $classReflection->getParentClass() instanceof ClassReflection - ? $classReflection->getParentClass()->getName() : ''; + ? $classReflection->getParentClass() + ->getName() : ''; foreach ($classMethodStmts as $classMethodStmt) { if (! $classMethodStmt instanceof Expression) { diff --git a/src/NodeManipulator/StmtsManipulator.php b/src/NodeManipulator/StmtsManipulator.php index eb018b2c1b1..916814a4bff 100644 --- a/src/NodeManipulator/StmtsManipulator.php +++ b/src/NodeManipulator/StmtsManipulator.php @@ -6,6 +6,7 @@ use PhpParser\Node; use PhpParser\Node\Expr; +use PhpParser\Node\Expr\Closure; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\ClassMethod; @@ -73,11 +74,11 @@ function (Node $node) use (&$stmts): null { return $stmts; } - public function isVariableUsedInNextStmt( - StmtsAwareInterface $stmtsAware, - int $jumpToKey, - string $variableName - ): bool { + /** + * @param StmtsAwareInterface $stmtsAware + */ + public function isVariableUsedInNextStmt(Stmt|Closure $stmtsAware, int $jumpToKey, string $variableName): bool + { if ($stmtsAware->stmts === null) { return false; } diff --git a/src/PHPStan/NodeVisitor/StmtsAwareWrappingNodeVisitor.php b/src/PHPStan/NodeVisitor/StmtsAwareWrappingNodeVisitor.php deleted file mode 100644 index 8c92f655d71..00000000000 --- a/src/PHPStan/NodeVisitor/StmtsAwareWrappingNodeVisitor.php +++ /dev/null @@ -1,25 +0,0 @@ -originalNode->stmts; - } - - return $this->{$property}; - } - - /** - * @var string[] - */ - public function getSubNodeNames(): array - { - // empty on purpose to avoid infinity loop - return []; - } - - public function getOriginalNode(): \PhpParser\Node - { - return $this->originalNode; - } -} diff --git a/src/PhpParser/Printer/BetterStandardPrinter.php b/src/PhpParser/Printer/BetterStandardPrinter.php index d25efbad844..7b0adc67a78 100644 --- a/src/PhpParser/Printer/BetterStandardPrinter.php +++ b/src/PhpParser/Printer/BetterStandardPrinter.php @@ -37,7 +37,6 @@ use Rector\NodeAnalyzer\ExprAnalyzer; use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\PhpParser\Node\CustomNode\FileWithoutNamespace; -use Rector\PhpParser\Node\CustomNode\StmtsAwareNode; use Rector\Util\NewLineSplitter; use Rector\Util\Reflection\PrivatesAccessor; use Rector\Util\StringUtils; @@ -274,19 +273,6 @@ protected function pArray( // reindex positions for printer $nodes = array_values($nodes); - // unwrap StmtsAwareNode to make their printing work - foreach ($nodes as $key => $node) { - if ($node instanceof StmtsAwareNode) { - $nodes[$key] = $node->getOriginalNode(); - } - } - - foreach ($origNodes as $key => $origNode) { - if ($origNode instanceof StmtsAwareNode) { - $origNodes[$key] = $origNode->getOriginalNode(); - } - } - $content = parent::pArray($nodes, $origNodes, $pos, $indentAdjustment, $parentNodeClass, $subNodeName, $fixup); if ($content === null) { return $content; From aea9099f26688e87dee936f08d844e44132ea03b Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Wed, 19 Nov 2025 16:29:50 +0000 Subject: [PATCH 06/10] static fixes --- phpstan.neon | 9 +++++++++ .../FunctionLike/SimplifyUselessVariableRector.php | 7 +++++-- .../Rector/If_/SimplifyIfNotNullReturnRector.php | 2 +- src/NodeManipulator/StmtsManipulator.php | 5 ++++- 4 files changed, 19 insertions(+), 4 deletions(-) diff --git a/phpstan.neon b/phpstan.neon index 0258b5d11d2..7688fe53939 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -376,3 +376,12 @@ parameters: - message: '#Property Rector\\PhpParser\\NodeTraverser\\AbstractImmutableNodeTraverser\:\:\$visitors \(list\) does not accept array#' path: src/PhpParser/NodeTraverser/AbstractImmutableNodeTraverser.php + + # special check for stmts aware virtual interface + - + path: src/PhpParser/NodeTraverser/RectorNodeTraverser.php + identifier: symplify.forbiddenFuncCall + - '#Parameter 1 should use "Rector\\Contract\\PhpParser\\Node\\StmtsAwareInterface" type as the only type passed to this method#' + - '#Parameter \#1 \$stmtsAware of method (.*?) expects Rector\\Contract\\PhpParser\\Node\\StmtsAwareInterface#' + + - '#Method (.*?) should return Rector\\Contract\\PhpParser\\Node\\StmtsAwareInterface\|null but returns (.*?)\|null#' diff --git a/rules/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector.php b/rules/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector.php index 27029e36f4e..38251121828 100644 --- a/rules/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector.php +++ b/rules/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector.php @@ -25,6 +25,7 @@ use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; +use Webmozart\Assert\Assert; /** * @see \Rector\Tests\CodeQuality\Rector\FunctionLike\SimplifyUselessVariableRector\SimplifyUselessVariableRectorTest @@ -157,11 +158,13 @@ public function refactor(Node $node): ?Node * @return StmtsAwareInterface */ private function processSimplifyUselessVariable( - Stmt|Closure $stmtsAware, + \PhpParser\Node $stmtsAware, Return_ $return, Assign|AssignOp $assign, int $key - ): Stmt|Closure|null { + ): \PhpParser\Node|null { + Assert::propertyExists($stmtsAware, 'stmts'); + if (! $assign instanceof Assign) { $binaryClass = $this->assignAndBinaryMap->getAlternative($assign); if ($binaryClass === null) { diff --git a/rules/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector.php b/rules/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector.php index b2c2670a8f4..5cdff371c9e 100644 --- a/rules/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector.php +++ b/rules/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector.php @@ -63,7 +63,7 @@ public function getNodeTypes(): array /** * @param StmtsAwareInterface $node */ - public function refactor(Node $node): ?Stmt + public function refactor(Node $node): ?\PhpParser\Node { foreach ((array) $node->stmts as $key => $stmt) { if (! $stmt instanceof If_) { diff --git a/src/NodeManipulator/StmtsManipulator.php b/src/NodeManipulator/StmtsManipulator.php index 916814a4bff..8a55c695f26 100644 --- a/src/NodeManipulator/StmtsManipulator.php +++ b/src/NodeManipulator/StmtsManipulator.php @@ -19,6 +19,7 @@ use Rector\PhpDocParser\NodeTraverser\SimpleCallableNodeTraverser; use Rector\PhpParser\Comparing\NodeComparator; use Rector\PhpParser\Node\BetterNodeFinder; +use Webmozart\Assert\Assert; final readonly class StmtsManipulator { @@ -77,8 +78,10 @@ function (Node $node) use (&$stmts): null { /** * @param StmtsAwareInterface $stmtsAware */ - public function isVariableUsedInNextStmt(Stmt|Closure $stmtsAware, int $jumpToKey, string $variableName): bool + public function isVariableUsedInNextStmt(Node $stmtsAware, int $jumpToKey, string $variableName): bool { + Assert::propertyExists($stmtsAware, 'stmts'); + if ($stmtsAware->stmts === null) { return false; } From b5ea28c9a72c8c30c18e7cc611b064f75be5918f Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Wed, 19 Nov 2025 16:32:00 +0000 Subject: [PATCH 07/10] update custom vistors --- .../FunctionLike/SimplifyUselessVariableRector.php | 1 - .../Rector/If_/SimplifyIfNotNullReturnRector.php | 1 - src/NodeManipulator/StmtsManipulator.php | 1 - .../Scope/NodeVisitor/GlobalVariableNodeVisitor.php | 7 +++++-- .../Scope/NodeVisitor/StaticVariableNodeVisitor.php | 7 +++++-- src/PhpParser/NodeGroups.php | 11 +++++++++++ 6 files changed, 21 insertions(+), 7 deletions(-) diff --git a/rules/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector.php b/rules/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector.php index 38251121828..2b116c739de 100644 --- a/rules/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector.php +++ b/rules/CodeQuality/Rector/FunctionLike/SimplifyUselessVariableRector.php @@ -8,7 +8,6 @@ use PhpParser\Node; use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\AssignOp; -use PhpParser\Node\Expr\Closure; use PhpParser\Node\Expr\Ternary; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Stmt; diff --git a/rules/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector.php b/rules/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector.php index 5cdff371c9e..2644290c482 100644 --- a/rules/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector.php +++ b/rules/CodeQuality/Rector/If_/SimplifyIfNotNullReturnRector.php @@ -6,7 +6,6 @@ use PhpParser\Node; use PhpParser\Node\Expr; -use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Else_; use PhpParser\Node\Stmt\If_; use PhpParser\Node\Stmt\Return_; diff --git a/src/NodeManipulator/StmtsManipulator.php b/src/NodeManipulator/StmtsManipulator.php index 8a55c695f26..fadbf2339af 100644 --- a/src/NodeManipulator/StmtsManipulator.php +++ b/src/NodeManipulator/StmtsManipulator.php @@ -6,7 +6,6 @@ use PhpParser\Node; use PhpParser\Node\Expr; -use PhpParser\Node\Expr\Closure; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\ClassMethod; diff --git a/src/NodeTypeResolver/PHPStan/Scope/NodeVisitor/GlobalVariableNodeVisitor.php b/src/NodeTypeResolver/PHPStan/Scope/NodeVisitor/GlobalVariableNodeVisitor.php index 1e3c096b2f3..893dcdeb787 100644 --- a/src/NodeTypeResolver/PHPStan/Scope/NodeVisitor/GlobalVariableNodeVisitor.php +++ b/src/NodeTypeResolver/PHPStan/Scope/NodeVisitor/GlobalVariableNodeVisitor.php @@ -12,10 +12,11 @@ use PhpParser\Node\Stmt\Global_; use PhpParser\NodeVisitor; use PhpParser\NodeVisitorAbstract; -use Rector\Contract\PhpParser\Node\StmtsAwareInterface; use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\NodeTypeResolver\PHPStan\Scope\Contract\NodeVisitor\ScopeResolverNodeVisitorInterface; use Rector\PhpDocParser\NodeTraverser\SimpleCallableNodeTraverser; +use Rector\PhpParser\NodeGroups; +use Webmozart\Assert\Assert; final class GlobalVariableNodeVisitor extends NodeVisitorAbstract implements ScopeResolverNodeVisitorInterface { @@ -26,10 +27,12 @@ public function __construct( public function enterNode(Node $node): ?Node { - if (! $node instanceof StmtsAwareInterface) { + if (! NodeGroups::matchesStmtsAware($node)) { return null; } + Assert::propertyExists($node, 'stmts'); + if ($node->stmts === null) { return null; } diff --git a/src/NodeTypeResolver/PHPStan/Scope/NodeVisitor/StaticVariableNodeVisitor.php b/src/NodeTypeResolver/PHPStan/Scope/NodeVisitor/StaticVariableNodeVisitor.php index fa3556bf986..7f8688ec52f 100644 --- a/src/NodeTypeResolver/PHPStan/Scope/NodeVisitor/StaticVariableNodeVisitor.php +++ b/src/NodeTypeResolver/PHPStan/Scope/NodeVisitor/StaticVariableNodeVisitor.php @@ -12,10 +12,11 @@ use PhpParser\Node\Stmt\Static_; use PhpParser\NodeVisitor; use PhpParser\NodeVisitorAbstract; -use Rector\Contract\PhpParser\Node\StmtsAwareInterface; use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\NodeTypeResolver\PHPStan\Scope\Contract\NodeVisitor\ScopeResolverNodeVisitorInterface; use Rector\PhpDocParser\NodeTraverser\SimpleCallableNodeTraverser; +use Rector\PhpParser\NodeGroups; +use Webmozart\Assert\Assert; final class StaticVariableNodeVisitor extends NodeVisitorAbstract implements ScopeResolverNodeVisitorInterface { @@ -26,10 +27,12 @@ public function __construct( public function enterNode(Node $node): ?Node { - if (! $node instanceof StmtsAwareInterface) { + if (! NodeGroups::matchesStmtsAware($node)) { return null; } + Assert::propertyExists($node, 'stmts'); + if ($node->stmts === null) { return null; } diff --git a/src/PhpParser/NodeGroups.php b/src/PhpParser/NodeGroups.php index a1b4250a9d6..b99ed2dd01d 100644 --- a/src/PhpParser/NodeGroups.php +++ b/src/PhpParser/NodeGroups.php @@ -31,4 +31,15 @@ final class NodeGroups // custom Rector node FileWithoutNamespace::class, ]; + + public static function matchesStmtsAware(\PhpParser\Node $node): bool + { + foreach (self::STMTS_AWARE_NODES as $stmtAwareNodeClass) { + if (is_a($node, $stmtAwareNodeClass)) { + return true; + } + } + + return false; + } } From 990d5b18d967ddfb8f49dcfc68abd5d040425bc4 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Wed, 19 Nov 2025 16:35:12 +0000 Subject: [PATCH 08/10] avoid type casting against stmts aware interface, as no node has it --- config/phpstan/parser.neon | 6 ------ phpstan.neon | 2 +- .../Assign/RemoveUnusedVariableAssignRector.php | 6 +++++- .../Rector/For_/RemoveDeadIfForeachForRector.php | 10 ++++++++-- ...moveUnusedNonEmptyArrayBeforeForeachRector.php | 8 +++++++- .../Return_/PreparedValueToEarlyReturnRector.php | 9 +++++++-- .../Rector/Foreach_/ForeachToArrayAllRector.php | 15 +++++++++++++-- .../Rector/Foreach_/ForeachToArrayAnyRector.php | 15 +++++++++++++-- 8 files changed, 54 insertions(+), 17 deletions(-) diff --git a/config/phpstan/parser.neon b/config/phpstan/parser.neon index bf053eec374..3b0f385dce2 100644 --- a/config/phpstan/parser.neon +++ b/config/phpstan/parser.neon @@ -25,9 +25,3 @@ services: arguments: parser: @currentPhpVersionPhpParser autowired: no - - # use StmtsAwareNode -# - -# class: Rector\PHPStan\NodeVisitor\StmtsAwareWrappingNodeVisitor -# tags: -# - phpstan.parser.richParserNodeVisitor diff --git a/phpstan.neon b/phpstan.neon index 7688fe53939..f075dd87764 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -381,7 +381,7 @@ parameters: - path: src/PhpParser/NodeTraverser/RectorNodeTraverser.php identifier: symplify.forbiddenFuncCall - - '#Parameter 1 should use "Rector\\Contract\\PhpParser\\Node\\StmtsAwareInterface" type as the only type passed to this method#' + - '#Parameter \d should use "Rector\\Contract\\PhpParser\\Node\\StmtsAwareInterface" type as the only type passed to this method#' - '#Parameter \#1 \$stmtsAware of method (.*?) expects Rector\\Contract\\PhpParser\\Node\\StmtsAwareInterface#' - '#Method (.*?) should return Rector\\Contract\\PhpParser\\Node\\StmtsAwareInterface\|null but returns (.*?)\|null#' diff --git a/rules/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector.php b/rules/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector.php index 0417e173d4a..b77d76fcae2 100644 --- a/rules/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector.php +++ b/rules/DeadCode/Rector/Assign/RemoveUnusedVariableAssignRector.php @@ -25,9 +25,11 @@ use Rector\NodeManipulator\StmtsManipulator; use Rector\Php\ReservedKeywordAnalyzer; use Rector\PhpParser\Node\BetterNodeFinder; +use Rector\PhpParser\NodeGroups; use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; +use Webmozart\Assert\Assert; /** * @see \Rector\Tests\DeadCode\Rector\Assign\RemoveUnusedVariableAssignRector\RemoveUnusedVariableAssignRectorTest @@ -164,10 +166,12 @@ private function shouldSkip(array $stmts): bool */ private function collectAssignRefVariableNames(Stmt $stmt, array &$refVariableNames): void { - if (! $stmt instanceof StmtsAwareInterface) { + if (! NodeGroups::matchesStmtsAware($stmt)) { return; } + Assert::propertyExists($stmt, 'stmts'); + $this->traverseNodesWithCallable( $stmt, function (Node $subNode) use (&$refVariableNames): Node { diff --git a/rules/DeadCode/Rector/For_/RemoveDeadIfForeachForRector.php b/rules/DeadCode/Rector/For_/RemoveDeadIfForeachForRector.php index da26f962428..68ca6d3d183 100644 --- a/rules/DeadCode/Rector/For_/RemoveDeadIfForeachForRector.php +++ b/rules/DeadCode/Rector/For_/RemoveDeadIfForeachForRector.php @@ -113,7 +113,10 @@ public function refactor(Node $node): Node|null return null; } - private function processIf(If_ $if, int $key, StmtsAwareInterface $stmtsAware): void + /** + * @param StmtsAwareInterface $stmtsAware + */ + private function processIf(If_ $if, int $key, \PhpParser\Node $stmtsAware): void { if ($if->elseifs !== []) { return; @@ -137,7 +140,10 @@ private function processIf(If_ $if, int $key, StmtsAwareInterface $stmtsAware): $this->hasChanged = true; } - private function processForForeach(For_|Foreach_ $for, int $key, StmtsAwareInterface $stmtsAware): void + /** + * @param StmtsAwareInterface $stmtsAware + */ + private function processForForeach(For_|Foreach_ $for, int $key, \PhpParser\Node $stmtsAware): void { if ($for instanceof For_) { $variables = $this->betterNodeFinder->findInstanceOf( diff --git a/rules/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php b/rules/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php index 26071716ec3..97a8c549ce2 100644 --- a/rules/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php +++ b/rules/DeadCode/Rector/If_/RemoveUnusedNonEmptyArrayBeforeForeachRector.php @@ -25,6 +25,7 @@ use Rector\Rector\AbstractRector; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; +use Webmozart\Assert\Assert; /** * @see \Rector\Tests\DeadCode\Rector\If_\RemoveUnusedNonEmptyArrayBeforeForeachRector\RemoveUnusedNonEmptyArrayBeforeForeachRectorTest @@ -151,8 +152,13 @@ private function isUselessBooleanAnd(BooleanAnd $booleanAnd, Expr $foreachExpr): return $this->countManipulator->isCounterHigherThanOne($booleanAnd->right, $foreachExpr); } - private function refactorStmtsAware(StmtsAwareInterface $stmtsAware): ?StmtsAwareInterface + /** + * @param StmtsAwareInterface $stmtsAware + */ + private function refactorStmtsAware(\PhpParser\Node $stmtsAware): ?StmtsAwareInterface { + Assert::propertyExists($stmtsAware, 'stmts'); + if ($stmtsAware->stmts === null) { return null; } diff --git a/rules/EarlyReturn/Rector/Return_/PreparedValueToEarlyReturnRector.php b/rules/EarlyReturn/Rector/Return_/PreparedValueToEarlyReturnRector.php index 12d07352976..394fcaed586 100644 --- a/rules/EarlyReturn/Rector/Return_/PreparedValueToEarlyReturnRector.php +++ b/rules/EarlyReturn/Rector/Return_/PreparedValueToEarlyReturnRector.php @@ -183,9 +183,11 @@ public function refactor(Node $node): ?StmtsAwareInterface /** * @param If_[] $ifs + * @param StmtsAwareInterface $stmtsAware + * * @return BareSingleAssignIf[] */ - private function getMatchingBareSingleAssignIfs(array $ifs, StmtsAwareInterface $stmtsAware): array + private function getMatchingBareSingleAssignIfs(array $ifs, \PhpParser\Node $stmtsAware): array { $bareSingleAssignIfs = []; foreach ($ifs as $key => $if) { @@ -233,7 +235,10 @@ private function isVariableSharedInAssignIfsAndReturn( return true; } - private function matchBareSingleAssignIf(Stmt $stmt, int $key, StmtsAwareInterface $stmtsAware): ?BareSingleAssignIf + /** + * @param StmtsAwareInterface $stmtsAware + */ + private function matchBareSingleAssignIf(Stmt $stmt, int $key, \PhpParser\Node $stmtsAware): ?BareSingleAssignIf { if (! $stmt instanceof If_) { return null; diff --git a/rules/Php84/Rector/Foreach_/ForeachToArrayAllRector.php b/rules/Php84/Rector/Foreach_/ForeachToArrayAllRector.php index 07cdc3ecd5f..ddd8d9ed3cd 100644 --- a/rules/Php84/Rector/Foreach_/ForeachToArrayAllRector.php +++ b/rules/Php84/Rector/Foreach_/ForeachToArrayAllRector.php @@ -25,6 +25,7 @@ use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; +use Webmozart\Assert\Assert; /** * @see \Rector\Tests\Php84\Rector\Foreach_\ForeachToArrayAllRector\ForeachToArrayAllRectorTest @@ -102,8 +103,13 @@ public function provideMinPhpVersion(): int return PhpVersionFeature::ARRAY_ALL; } - private function refactorBooleanAssignmentPattern(StmtsAwareInterface $stmtsAware): ?Node + /** + * @param StmtsAwareInterface $stmtsAware + */ + private function refactorBooleanAssignmentPattern(\PhpParser\Node $stmtsAware): ?Node { + Assert::propertyExists($stmtsAware, 'stmts'); + foreach ($stmtsAware->stmts as $key => $stmt) { if (! $stmt instanceof Foreach_) { continue; @@ -185,8 +191,13 @@ private function refactorBooleanAssignmentPattern(StmtsAwareInterface $stmtsAwar return null; } - private function refactorEarlyReturnPattern(StmtsAwareInterface $stmtsAware): ?Node + /** + * @param StmtsAwareInterface $stmtsAware + */ + private function refactorEarlyReturnPattern(\PhpParser\Node $stmtsAware): ?Node { + Assert::propertyExists($stmtsAware, 'stmts'); + foreach ($stmtsAware->stmts as $key => $stmt) { if (! $stmt instanceof Foreach_) { continue; diff --git a/rules/Php84/Rector/Foreach_/ForeachToArrayAnyRector.php b/rules/Php84/Rector/Foreach_/ForeachToArrayAnyRector.php index 9ddea5d90c2..317b0513e41 100644 --- a/rules/Php84/Rector/Foreach_/ForeachToArrayAnyRector.php +++ b/rules/Php84/Rector/Foreach_/ForeachToArrayAnyRector.php @@ -24,6 +24,7 @@ use Rector\VersionBonding\Contract\MinPhpVersionInterface; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; +use Webmozart\Assert\Assert; /** * @see \Rector\Tests\Php84\Rector\Foreach_\ForeachToArrayAnyRector\ForeachToArrayAnyRectorTest @@ -101,8 +102,13 @@ public function provideMinPhpVersion(): int return PhpVersionFeature::ARRAY_ANY; } - private function refactorBooleanAssignmentPattern(StmtsAwareInterface $stmtsAware): ?Node + /** + * @param StmtsAwareInterface $stmtsAware + */ + private function refactorBooleanAssignmentPattern(\PhpParser\Node $stmtsAware): ?Node { + Assert::propertyExists($stmtsAware, 'stmts'); + foreach ($stmtsAware->stmts as $key => $stmt) { if (! $stmt instanceof Foreach_) { continue; @@ -182,8 +188,13 @@ private function refactorBooleanAssignmentPattern(StmtsAwareInterface $stmtsAwar return null; } - private function refactorEarlyReturnPattern(StmtsAwareInterface $stmtsAware): ?Node + /** + * @param StmtsAwareInterface $stmtsAware + */ + private function refactorEarlyReturnPattern(\PhpParser\Node $stmtsAware): ?Node { + Assert::propertyExists($stmtsAware, 'stmts'); + foreach ($stmtsAware->stmts as $key => $stmt) { if (! $stmt instanceof Foreach_) { continue; From 40d4f0966806cc75270d495ed067f8f9ed701790 Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Thu, 20 Nov 2025 00:53:24 +0000 Subject: [PATCH 09/10] flip keys --- .../RemoveUnusedPrivateMethodRector.php | 18 ++++++++++++++++++ .../AbstractImmutableNodeTraverser.php | 1 + 2 files changed, 19 insertions(+) diff --git a/rules/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector.php b/rules/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector.php index 89ab3af618e..6910c71b7d8 100644 --- a/rules/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector.php +++ b/rules/DeadCode/Rector/ClassMethod/RemoveUnusedPrivateMethodRector.php @@ -78,7 +78,25 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { +<<<<<<< HEAD if ($node->getMethods() === []) { +======= + foreach ($node->stmts as $stmt) { + if (!$stmt instanceof ClassMethod) { + continue; + } + + if ($stmt->isPrivate()) { + return null; + } + + dump('todo'); + } + + $classMethods = $node->getMethods(); + + if ($classMethods === []) { +>>>>>>> 77e904a598 (flip keys) return null; } diff --git a/src/PhpParser/NodeTraverser/AbstractImmutableNodeTraverser.php b/src/PhpParser/NodeTraverser/AbstractImmutableNodeTraverser.php index 66953e2b65d..4b8bb40fd49 100644 --- a/src/PhpParser/NodeTraverser/AbstractImmutableNodeTraverser.php +++ b/src/PhpParser/NodeTraverser/AbstractImmutableNodeTraverser.php @@ -211,6 +211,7 @@ protected function traverseArray(array $nodes): array $visitorIndex = -1; $currentNodeVisitors = $this->getVisitorsForNode($node); + // @todo change everything we dont need here foreach ($currentNodeVisitors as $visitorIndex => $visitor) { $return = $visitor->enterNode($node); if ($return !== null) { From eecd5c85668bcb3e5dcc43b90a61838a012a48de Mon Sep 17 00:00:00 2001 From: Tomas Votruba Date: Thu, 20 Nov 2025 00:54:36 +0000 Subject: [PATCH 10/10] note --- .../ClassMethod/Php4ConstructorRector.php | 23 ------------------- src/NodeTypeResolver/Node/AttributeKey.php | 4 ++++ 2 files changed, 4 insertions(+), 23 deletions(-) diff --git a/rules/Php70/Rector/ClassMethod/Php4ConstructorRector.php b/rules/Php70/Rector/ClassMethod/Php4ConstructorRector.php index 0d2b84c63bb..976454d3423 100644 --- a/rules/Php70/Rector/ClassMethod/Php4ConstructorRector.php +++ b/rules/Php70/Rector/ClassMethod/Php4ConstructorRector.php @@ -97,7 +97,6 @@ public function refactor(Node $node): Class_|null return null; } -<<<<<<< HEAD $className = $this->getName($node); if (! is_string($className)) { return null; @@ -106,28 +105,6 @@ public function refactor(Node $node): Class_|null foreach ($node->stmts as $classStmtKey => $classStmt) { if (! $classStmt instanceof ClassMethod) { continue; -======= - // process parent call references first - $this->processClassMethodStatementsForParentConstructorCalls($psr4ConstructorMethod, $scope); - - // does it already have a "__construct" method? - if (! $node->getMethod(MethodName::CONSTRUCT) instanceof ClassMethod) { - $psr4ConstructorMethod->name = new Identifier(MethodName::CONSTRUCT); - } - - $classMethodStmts = $psr4ConstructorMethod->stmts; - if ($classMethodStmts === null) { - return null; - } - - $parentClassName = $classReflection->getParentClass() instanceof ClassReflection - ? $classReflection->getParentClass() - ->getName() : ''; - - foreach ($classMethodStmts as $classMethodStmt) { - if (! $classMethodStmt instanceof Expression) { - return null; ->>>>>>> ea2ce07c12 (fixup! fixup! fixup! fixup! [stmts-aware] kick of custom node parser to use StmtsAwareNode) } if (! $this->php4ConstructorClassMethodAnalyzer->detect($classStmt, $classReflection)) { diff --git a/src/NodeTypeResolver/Node/AttributeKey.php b/src/NodeTypeResolver/Node/AttributeKey.php index b5395a2d35b..ad2f4705657 100644 --- a/src/NodeTypeResolver/Node/AttributeKey.php +++ b/src/NodeTypeResolver/Node/AttributeKey.php @@ -162,8 +162,12 @@ final class AttributeKey public const IS_BYREF_RETURN = 'is_byref_return'; /** +<<<<<<< HEAD * @deprecated This value can change, as based on default input keys. Use existing array keys instead. * +======= + * @deprecated use keys directly from stmts iteration as convention +>>>>>>> c23994b233 (note) * @var string */ public const STMT_KEY = 'stmt_key';