diff --git a/src/Application/ChangedNodeScopeRefresher.php b/src/Application/ChangedNodeScopeRefresher.php index 954edadc8e4..c90a8b25e4f 100644 --- a/src/Application/ChangedNodeScopeRefresher.php +++ b/src/Application/ChangedNodeScopeRefresher.php @@ -23,6 +23,7 @@ use PhpParser\Node\StaticVar; use PhpParser\Node\Stmt; use PhpParser\Node\Stmt\Class_; +use PhpParser\Node\Stmt\ClassLike; use PhpParser\Node\Stmt\Declare_; use PhpParser\Node\Stmt\Else_; use PhpParser\Node\Stmt\ElseIf_; @@ -76,6 +77,13 @@ public function refresh(Node $node, string $filePath, ?MutatingScope $mutatingSc fn (Node $subNode): ?Node => NodeAttributeReIndexer::reIndexNodeAttributes($subNode) ); + // a class-like scope is already inside the class, so PHPStan cannot enter it again and would fail + // on the class node, leaving its methods with the class scope; process the class stmts directly instead + if ($node instanceof ClassLike && $mutatingScope->isInClass()) { + $this->phpStanNodeScopeResolver->processNodes($node->stmts, $filePath, $mutatingScope); + return; + } + $stmts = $this->resolveStmts($node); $this->phpStanNodeScopeResolver->processNodes($stmts, $filePath, $mutatingScope); } diff --git a/tests/Issues/ClassScopeRefresh/ClassScopeRefreshTest.php b/tests/Issues/ClassScopeRefresh/ClassScopeRefreshTest.php new file mode 100644 index 00000000000..737cf651c96 --- /dev/null +++ b/tests/Issues/ClassScopeRefresh/ClassScopeRefreshTest.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/ClassScopeRefresh/Fixture/keep_method_scope_after_class_change.php.inc b/tests/Issues/ClassScopeRefresh/Fixture/keep_method_scope_after_class_change.php.inc new file mode 100644 index 00000000000..8260d941d3d --- /dev/null +++ b/tests/Issues/ClassScopeRefresh/Fixture/keep_method_scope_after_class_change.php.inc @@ -0,0 +1,51 @@ + +----- +value = $value; + } + + public function run(array $items): array + { + $result = []; + foreach ($items as $item) { + $result = array_merge($result, $items); + } + + return $result; + } +} + +?> diff --git a/tests/Issues/ClassScopeRefresh/config/configured_rule.php b/tests/Issues/ClassScopeRefresh/config/configured_rule.php new file mode 100644 index 00000000000..99534c7b88f --- /dev/null +++ b/tests/Issues/ClassScopeRefresh/config/configured_rule.php @@ -0,0 +1,15 @@ +withRules([ + DowngradePropertyPromotionRector::class, + DowngradeReadonlyPropertyRector::class, + DowngradeArraySpreadRector::class, + ]);