diff --git a/src/Rector/AbstractRector.php b/src/Rector/AbstractRector.php index 88c629e2698..633eae9de71 100644 --- a/src/Rector/AbstractRector.php +++ b/src/Rector/AbstractRector.php @@ -18,6 +18,7 @@ use PHPStan\Type\ObjectType; use PHPStan\Type\Type; use Rector\Application\ChangedNodeScopeRefresher; +use Rector\Application\NodeAttributeReIndexer; use Rector\Application\Provider\CurrentFileProvider; use Rector\BetterPhpDocParser\Comment\CommentsMerger; use Rector\ChangesReporting\ValueObject\RectorWithLineChange; @@ -136,6 +137,16 @@ final public function enterNode(Node $node): int|Node|null|array // ensure origNode pulled before refactor to avoid changed during refactor, ref https://3v4l.org/YMEGN $originalNode = $node->getAttribute(AttributeKey::ORIGINAL_NODE) ?? $node; + /** + * This reindex is needed when multiple rules apply. + * The existing node position can already be removed/moved by a different rule from a "parent" node. + * + * That can modify/remove a deep node, for example: + * - first rule: - Class_ → ClassMethod → remove index 0 + * - second rule: - ClassMethod → here fetch the index 0 no longer exists + */ + NodeAttributeReIndexer::reIndexNodeAttributes($node); + $refactoredNodeOrState = $this->refactor($node); // nothing to change → continue diff --git a/tests/Issues/IssuePropertyPromoRemoveDelegatingParent/Fixture/fixture.php.inc b/tests/Issues/IssuePropertyPromoRemoveDelegatingParent/Fixture/fixture.php.inc new file mode 100644 index 00000000000..41d5f1d887c --- /dev/null +++ b/tests/Issues/IssuePropertyPromoRemoveDelegatingParent/Fixture/fixture.php.inc @@ -0,0 +1,34 @@ +prop = $prop; + parent::__construct(); + } +} + +?> +----- + diff --git a/tests/Issues/IssuePropertyPromoRemoveDelegatingParent/IssuePropertyPromoRemoveDelegatingParentTest.php b/tests/Issues/IssuePropertyPromoRemoveDelegatingParent/IssuePropertyPromoRemoveDelegatingParentTest.php new file mode 100644 index 00000000000..0231e5064e3 --- /dev/null +++ b/tests/Issues/IssuePropertyPromoRemoveDelegatingParent/IssuePropertyPromoRemoveDelegatingParentTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/IssuePropertyPromoRemoveDelegatingParent/Source/SomeParentWithEmptyConstruct.php b/tests/Issues/IssuePropertyPromoRemoveDelegatingParent/Source/SomeParentWithEmptyConstruct.php new file mode 100644 index 00000000000..05e4ddb58ae --- /dev/null +++ b/tests/Issues/IssuePropertyPromoRemoveDelegatingParent/Source/SomeParentWithEmptyConstruct.php @@ -0,0 +1,18 @@ +init(); + } + + private function init(): void + { + echo 'A init'; + } +} \ No newline at end of file diff --git a/tests/Issues/IssuePropertyPromoRemoveDelegatingParent/config/configured_rule.php b/tests/Issues/IssuePropertyPromoRemoveDelegatingParent/config/configured_rule.php new file mode 100644 index 00000000000..75e42bde090 --- /dev/null +++ b/tests/Issues/IssuePropertyPromoRemoveDelegatingParent/config/configured_rule.php @@ -0,0 +1,11 @@ +withRules([ + ClassPropertyAssignToConstructorPromotionRector::class, + RemoveParentDelegatingConstructorRector::class, + ]); \ No newline at end of file