From 0b54a31c863816b42757351648607c0b2fb6304d Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Fri, 2 Jan 2026 22:51:44 +0700 Subject: [PATCH 1/4] [DeadCode][Php80] Handle crash on mix ClassPropertyAssignToConstructorPromotionRector+RemoveParentDelegatingConstructorRector --- .../Fixture/fixture.php.inc | 34 +++++++++++++++++++ ...ropertyPromoRemoveDelegatingParentTest.php | 28 +++++++++++++++ .../Source/SomeParentWithEmptyConstruct.php | 18 ++++++++++ .../config/configured_rule.php | 11 ++++++ 4 files changed, 91 insertions(+) create mode 100644 tests/Issues/IssuePropertyPromoRemoveDelegatingParent/Fixture/fixture.php.inc create mode 100644 tests/Issues/IssuePropertyPromoRemoveDelegatingParent/IssuePropertyPromoRemoveDelegatingParentTest.php create mode 100644 tests/Issues/IssuePropertyPromoRemoveDelegatingParent/Source/SomeParentWithEmptyConstruct.php create mode 100644 tests/Issues/IssuePropertyPromoRemoveDelegatingParent/config/configured_rule.php 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..05acdf8608d --- /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 From 6fa2549e66e4b82fb539bce48941b2354c6a6cca Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Fri, 2 Jan 2026 23:04:10 +0700 Subject: [PATCH 2/4] fix --- src/Rector/AbstractRector.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Rector/AbstractRector.php b/src/Rector/AbstractRector.php index 88c629e2698..2455d923b92 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,14 @@ 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 as when multiple rules apply + // the existing node position can already be removed/moved by different rule from "parent" node + // + // that modify/remove 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 From 4e80381529237022eec7756fa3a30fe93af24045 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Fri, 2 Jan 2026 23:07:52 +0700 Subject: [PATCH 3/4] fix finalize class --- .../Source/SomeParentWithEmptyConstruct.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Issues/IssuePropertyPromoRemoveDelegatingParent/Source/SomeParentWithEmptyConstruct.php b/tests/Issues/IssuePropertyPromoRemoveDelegatingParent/Source/SomeParentWithEmptyConstruct.php index 05acdf8608d..05e4ddb58ae 100644 --- a/tests/Issues/IssuePropertyPromoRemoveDelegatingParent/Source/SomeParentWithEmptyConstruct.php +++ b/tests/Issues/IssuePropertyPromoRemoveDelegatingParent/Source/SomeParentWithEmptyConstruct.php @@ -4,7 +4,7 @@ namespace Rector\Tests\Issues\IssuePropertyPromoRemoveDelegatingParent\Source; -class SomeParentWithEmptyConstruct +abstract class SomeParentWithEmptyConstruct { public function __construct() { From 13e1a730dac6188efd8c045395c89bf6d6554d11 Mon Sep 17 00:00:00 2001 From: Abdul Malik Ikhsan Date: Fri, 2 Jan 2026 23:09:29 +0700 Subject: [PATCH 4/4] fix comment ci --- src/Rector/AbstractRector.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Rector/AbstractRector.php b/src/Rector/AbstractRector.php index 2455d923b92..633eae9de71 100644 --- a/src/Rector/AbstractRector.php +++ b/src/Rector/AbstractRector.php @@ -137,12 +137,14 @@ 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 as when multiple rules apply - // the existing node position can already be removed/moved by different rule from "parent" node - // - // that modify/remove deep node, for example: - // - first rule: - Class_ → ClassMethod → remove index 0 - // - second rule: - ClassMethod → here fetch the index 0 no longer exists + /** + * 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);