Skip to content

Commit 5b16da0

Browse files
phpstan-botclaude
authored andcommitted
Simplify reference propagation: prevent invalidation instead of re-registering
Replace the re-register-after-propagation approach with a cleaner design: - Skip invalidation of simple variable-to-variable IntertwinedVariableByReferenceWithExpr entries in invalidateExpression() so reference links persist across assignments - Add propagateReferences parameter to assignVariable() to prevent infinite recursion when propagating types through bidirectional reference links This is simpler and more efficient than the previous approach of letting entries be invalidated and then re-creating them after each propagation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent b179382 commit 5b16da0

1 file changed

Lines changed: 16 additions & 33 deletions

File tree

src/Analyser/MutatingScope.php

Lines changed: 16 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2565,7 +2565,7 @@ public function isUndefinedExpressionAllowed(Expr $expr): bool
25652565
return array_key_exists($exprString, $this->currentlyAllowedUndefinedExpressions);
25662566
}
25672567

2568-
public function assignVariable(string $variableName, Type $type, Type $nativeType, TrinaryLogic $certainty): self
2568+
public function assignVariable(string $variableName, Type $type, Type $nativeType, TrinaryLogic $certainty, bool $propagateReferences = true): self
25692569
{
25702570
$node = new Variable($variableName);
25712571
$scope = $this->assignExpression($node, $type, $nativeType);
@@ -2577,12 +2577,11 @@ public function assignVariable(string $variableName, Type $type, Type $nativeTyp
25772577
$scope->nativeExpressionTypes[$exprString] = new ExpressionTypeHolder($node, $nativeType, $certainty);
25782578
}
25792579

2580-
// Use $this->expressionTypes (pre-invalidation) to find intertwined entries.
2581-
// assignExpression() calls invalidateExpression() which removes entries whose
2582-
// sub-nodes contain the variable being assigned. For bidirectional reference
2583-
// links ($b = &$a), this incorrectly removes the reverse entry.
2584-
$processedIntertwinedEntries = [];
2585-
foreach ($this->expressionTypes as $expressionType) {
2580+
if (!$propagateReferences) {
2581+
return $scope;
2582+
}
2583+
2584+
foreach ($scope->expressionTypes as $expressionType) {
25862585
if (!$expressionType->getExpr() instanceof IntertwinedVariableByReferenceWithExpr) {
25872586
continue;
25882587
}
@@ -2599,12 +2598,12 @@ public function assignVariable(string $variableName, Type $type, Type $nativeTyp
25992598
&& is_string($expressionType->getExpr()->getExpr()->name)
26002599
&& !$has->no()
26012600
) {
2602-
$processedIntertwinedEntries[] = $expressionType->getExpr();
26032601
$scope = $scope->assignVariable(
26042602
$expressionType->getExpr()->getExpr()->name,
26052603
$scope->getType($expressionType->getExpr()->getAssignedExpr()),
26062604
$scope->getNativeType($expressionType->getExpr()->getAssignedExpr()),
26072605
$has,
2606+
false,
26082607
);
26092608
} else {
26102609
$scope = $scope->assignExpression(
@@ -2616,31 +2615,6 @@ public function assignVariable(string $variableName, Type $type, Type $nativeTyp
26162615

26172616
}
26182617

2619-
// Re-register intertwined entries (and their reverse) that were
2620-
// invalidated during propagation so that subsequent assignments
2621-
// to either variable continue to propagate correctly.
2622-
foreach ($processedIntertwinedEntries as $intertwinedExpr) {
2623-
$currentType = $scope->getType($intertwinedExpr->getAssignedExpr());
2624-
$currentNativeType = $scope->getNativeType($intertwinedExpr->getAssignedExpr());
2625-
2626-
// Re-register this direction
2627-
$scope = $scope->assignExpression($intertwinedExpr, $currentType, $currentNativeType);
2628-
2629-
// Re-register the reverse direction
2630-
if (
2631-
$intertwinedExpr->getExpr() instanceof Variable
2632-
&& is_string($intertwinedExpr->getExpr()->name)
2633-
) {
2634-
$linkedVarName = $intertwinedExpr->getExpr()->name;
2635-
$reverseExpr = new IntertwinedVariableByReferenceWithExpr(
2636-
$linkedVarName,
2637-
new Variable($variableName),
2638-
new Variable($linkedVarName),
2639-
);
2640-
$scope = $scope->assignExpression($reverseExpr, $currentType, $currentNativeType);
2641-
}
2642-
}
2643-
26442618
return $scope;
26452619
}
26462620

@@ -2831,6 +2805,15 @@ public function invalidateExpression(Expr $expressionToInvalidate, bool $require
28312805

28322806
foreach ($expressionTypes as $exprString => $exprTypeHolder) {
28332807
$exprExpr = $exprTypeHolder->getExpr();
2808+
if (
2809+
$exprExpr instanceof IntertwinedVariableByReferenceWithExpr
2810+
&& $exprExpr->getExpr() instanceof Variable
2811+
&& is_string($exprExpr->getExpr()->name)
2812+
&& $exprExpr->getAssignedExpr() instanceof Variable
2813+
&& is_string($exprExpr->getAssignedExpr()->name)
2814+
) {
2815+
continue;
2816+
}
28342817
if (!$this->shouldInvalidateExpression($exprStringToInvalidate, $expressionToInvalidate, $exprExpr, $exprString, $requireMoreCharacters, $invalidatingClass)) {
28352818
continue;
28362819
}

0 commit comments

Comments
 (0)