Skip to content

Commit e89130e

Browse files
phpstan-botclaude
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 504414a commit e89130e

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
@@ -2635,7 +2635,7 @@ public function isUndefinedExpressionAllowed(Expr $expr): bool
26352635
return array_key_exists($exprString, $this->currentlyAllowedUndefinedExpressions);
26362636
}
26372637

2638-
public function assignVariable(string $variableName, Type $type, Type $nativeType, TrinaryLogic $certainty): self
2638+
public function assignVariable(string $variableName, Type $type, Type $nativeType, TrinaryLogic $certainty, bool $propagateReferences = true): self
26392639
{
26402640
$node = new Variable($variableName);
26412641
$scope = $this->assignExpression($node, $type, $nativeType);
@@ -2647,12 +2647,11 @@ public function assignVariable(string $variableName, Type $type, Type $nativeTyp
26472647
$scope->nativeExpressionTypes[$exprString] = new ExpressionTypeHolder($node, $nativeType, $certainty);
26482648
}
26492649

2650-
// Use $this->expressionTypes (pre-invalidation) to find intertwined entries.
2651-
// assignExpression() calls invalidateExpression() which removes entries whose
2652-
// sub-nodes contain the variable being assigned. For bidirectional reference
2653-
// links ($b = &$a), this incorrectly removes the reverse entry.
2654-
$processedIntertwinedEntries = [];
2655-
foreach ($this->expressionTypes as $expressionType) {
2650+
if (!$propagateReferences) {
2651+
return $scope;
2652+
}
2653+
2654+
foreach ($scope->expressionTypes as $expressionType) {
26562655
if (!$expressionType->getExpr() instanceof IntertwinedVariableByReferenceWithExpr) {
26572656
continue;
26582657
}
@@ -2669,12 +2668,12 @@ public function assignVariable(string $variableName, Type $type, Type $nativeTyp
26692668
&& is_string($expressionType->getExpr()->getExpr()->name)
26702669
&& !$has->no()
26712670
) {
2672-
$processedIntertwinedEntries[] = $expressionType->getExpr();
26732671
$scope = $scope->assignVariable(
26742672
$expressionType->getExpr()->getExpr()->name,
26752673
$scope->getType($expressionType->getExpr()->getAssignedExpr()),
26762674
$scope->getNativeType($expressionType->getExpr()->getAssignedExpr()),
26772675
$has,
2676+
false,
26782677
);
26792678
} else {
26802679
$scope = $scope->assignExpression(
@@ -2686,31 +2685,6 @@ public function assignVariable(string $variableName, Type $type, Type $nativeTyp
26862685

26872686
}
26882687

2689-
// Re-register intertwined entries (and their reverse) that were
2690-
// invalidated during propagation so that subsequent assignments
2691-
// to either variable continue to propagate correctly.
2692-
foreach ($processedIntertwinedEntries as $intertwinedExpr) {
2693-
$currentType = $scope->getType($intertwinedExpr->getAssignedExpr());
2694-
$currentNativeType = $scope->getNativeType($intertwinedExpr->getAssignedExpr());
2695-
2696-
// Re-register this direction
2697-
$scope = $scope->assignExpression($intertwinedExpr, $currentType, $currentNativeType);
2698-
2699-
// Re-register the reverse direction
2700-
if (
2701-
$intertwinedExpr->getExpr() instanceof Variable
2702-
&& is_string($intertwinedExpr->getExpr()->name)
2703-
) {
2704-
$linkedVarName = $intertwinedExpr->getExpr()->name;
2705-
$reverseExpr = new IntertwinedVariableByReferenceWithExpr(
2706-
$linkedVarName,
2707-
new Variable($variableName),
2708-
new Variable($linkedVarName),
2709-
);
2710-
$scope = $scope->assignExpression($reverseExpr, $currentType, $currentNativeType);
2711-
}
2712-
}
2713-
27142688
return $scope;
27152689
}
27162690

@@ -2901,6 +2875,15 @@ public function invalidateExpression(Expr $expressionToInvalidate, bool $require
29012875

29022876
foreach ($expressionTypes as $exprString => $exprTypeHolder) {
29032877
$exprExpr = $exprTypeHolder->getExpr();
2878+
if (
2879+
$exprExpr instanceof IntertwinedVariableByReferenceWithExpr
2880+
&& $exprExpr->getExpr() instanceof Variable
2881+
&& is_string($exprExpr->getExpr()->name)
2882+
&& $exprExpr->getAssignedExpr() instanceof Variable
2883+
&& is_string($exprExpr->getAssignedExpr()->name)
2884+
) {
2885+
continue;
2886+
}
29042887
if (!$this->shouldInvalidateExpression($exprStringToInvalidate, $expressionToInvalidate, $exprExpr, $exprString, $requireMoreCharacters, $invalidatingClass)) {
29052888
continue;
29062889
}

0 commit comments

Comments
 (0)