Skip to content

Commit 0751d88

Browse files
committed
Drop re-processing guard for fixpoint loop, fix scope refresh
Remove CreatedByRuleDecorator and its per-pass guard in RectorRunner; FileProcessor now iterates rules to a fixed point, refreshing scope on re-processed class and else nodes so repeated passes keep a valid scope.
1 parent 1133b77 commit 0751d88

4 files changed

Lines changed: 29 additions & 76 deletions

File tree

‎src/Application/ChangedNodeScopeRefresher.php‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,19 @@
1515
use PhpParser\Node\Expr;
1616
use PhpParser\Node\Expr\Array_;
1717
use PhpParser\Node\Expr\Closure;
18+
use PhpParser\Node\Expr\ConstFetch;
1819
use PhpParser\Node\Expr\New_;
20+
use PhpParser\Node\Name;
1921
use PhpParser\Node\Param;
2022
use PhpParser\Node\PropertyItem;
2123
use PhpParser\Node\StaticVar;
2224
use PhpParser\Node\Stmt;
2325
use PhpParser\Node\Stmt\Class_;
2426
use PhpParser\Node\Stmt\Declare_;
27+
use PhpParser\Node\Stmt\Else_;
28+
use PhpParser\Node\Stmt\ElseIf_;
2529
use PhpParser\Node\Stmt\Expression;
30+
use PhpParser\Node\Stmt\If_;
2631
use PhpParser\Node\Stmt\Property;
2732
use PhpParser\Node\Stmt\Static_;
2833
use PhpParser\Node\Stmt\Use_;
@@ -80,6 +85,22 @@ public function refresh(Node $node, string $filePath, ?MutatingScope $mutatingSc
8085
*/
8186
private function resolveStmts(Node $node): array
8287
{
88+
// Else_/ElseIf_ are not processed standalone by PHPStan, only as part of an If_, so their body
89+
// stays unscoped; wrap into an If_ so processNodes() scopes the real statements they hold
90+
if ($node instanceof Else_) {
91+
return [
92+
new If_(new ConstFetch(new Name('true')), [
93+
'stmts' => $node->stmts,
94+
])];
95+
}
96+
97+
if ($node instanceof ElseIf_) {
98+
return [
99+
new If_($node->cond, [
100+
'stmts' => $node->stmts,
101+
])];
102+
}
103+
83104
if ($node instanceof Stmt) {
84105
return [$node];
85106
}

‎src/NodeDecorator/CreatedByRuleDecorator.php‎

Lines changed: 0 additions & 63 deletions
This file was deleted.

‎src/NodeTypeResolver/PHPStan/Scope/PHPStanNodeScopeResolver.php‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@
9191
use PHPStan\Node\VirtualNode;
9292
use PHPStan\Parser\ParserErrorsException;
9393
use PHPStan\PhpDocParser\Parser\ParserException;
94+
use PHPStan\Reflection\ClassReflection;
9495
use PHPStan\Reflection\ReflectionProvider;
9596
use PHPStan\ShouldNotHappenException;
9697
use PHPStan\Type\ObjectType;
@@ -636,6 +637,13 @@ private function resolveClassOrInterfaceScope(
636637
} catch (ShouldNotHappenException) {
637638
}
638639

640+
// enterClass() throws when the scope is already inside a class, which happens when a changed
641+
// class node is re-processed on a later pass; keep the in-class scope instead of dropping it
642+
$currentClassReflection = $mutatingScope->getClassReflection();
643+
if ($currentClassReflection instanceof ClassReflection && $currentClassReflection->getName() === $classReflection->getName()) {
644+
return $mutatingScope;
645+
}
646+
639647
$context = $this->privatesAccessor->getPrivateProperty($mutatingScope, 'context');
640648
$this->privatesAccessor->setPrivateProperty($context, 'classReflection', null);
641649

‎src/Rector/RectorRunner.php‎

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,7 @@
1212
use Rector\ChangesReporting\ValueObject\RectorWithLineChange;
1313
use Rector\Contract\Rector\RectorInterface;
1414
use Rector\Exception\ShouldNotHappenException;
15-
use Rector\NodeDecorator\CreatedByRuleDecorator;
1615
use Rector\NodeTypeResolver\Node\AttributeKey;
17-
use Rector\Skipper\Skipper\Skipper;
1816
use Rector\ValueObject\Application\File;
1917

2018
/**
@@ -38,8 +36,6 @@
3836
CODE_SAMPLE;
3937

4038
public function __construct(
41-
private Skipper $skipper,
42-
private CreatedByRuleDecorator $createdByRuleDecorator,
4339
private ChangedNodeScopeRefresher $changedNodeScopeRefresher,
4440
private CurrentFileProvider $currentFileProvider,
4541
) {
@@ -53,11 +49,6 @@ public function run(RectorInterface $rector, Node $node): int|Node|null|array
5349
$file = $this->getFile();
5450
$filePath = $file->getFilePath();
5551

56-
// node already changed by this rule in a previous pass → hard skip
57-
if ($this->skipper->shouldSkipCurrentNode($rector::class, $node)) {
58-
return null;
59-
}
60-
6152
// ensure origNode pulled before refactor to avoid changed during refactor, ref https://3v4l.org/YMEGN
6253
$originalNode = $node->getAttribute(AttributeKey::ORIGINAL_NODE) ?? $node;
6354

@@ -74,8 +65,6 @@ public function run(RectorInterface $rector, Node $node): int|Node|null|array
7465
}
7566

7667
if (is_int($refactoredNodeOrState)) {
77-
$this->createdByRuleDecorator->decorate($node, $originalNode, $rector::class);
78-
7968
// only remove node is supported
8069
if ($refactoredNodeOrState !== NodeVisitor::REMOVE_NODE) {
8170
throw new ShouldNotHappenException(sprintf('Unsupported state "%d" returned from "%s".', $refactoredNodeOrState, $rector::class));
@@ -116,8 +105,6 @@ private function postRefactorProcess(
116105
string $filePath
117106
): Node|array {
118107
/** @var non-empty-array<Node>|Node $refactoredNode */
119-
$this->createdByRuleDecorator->decorate($refactoredNode, $originalNode, $rector::class);
120-
121108
$rectorWithLineChange = new RectorWithLineChange($rector::class, $originalNode->getStartLine());
122109
$file->addRectorClassWithLine($rectorWithLineChange);
123110

0 commit comments

Comments
 (0)