Skip to content

Commit a7fed03

Browse files
committed
Fix readonly properties bugs and infinite recursion
1 parent a8975b1 commit a7fed03

File tree

5 files changed

+54
-13
lines changed

5 files changed

+54
-13
lines changed

src/Analyser/MutatingScope.php

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3465,7 +3465,7 @@ public function assignExpression(Expr $expr, Type $type, ?Type $nativeType = nul
34653465
} elseif ($expr instanceof Expr\StaticPropertyFetch) {
34663466
$scope = $this->invalidateExpression($expr);
34673467
} elseif ($expr instanceof Variable) {
3468-
$scope = $this->invalidateExpression($expr, true);
3468+
$scope = $this->invalidateExpression($expr);
34693469
}
34703470

34713471
return $scope->specifyExpressionType($expr, $type, $nativeType);
@@ -3538,15 +3538,6 @@ private function shouldInvalidateExpression(string $exprStringToInvalidate, Expr
35383538
if ($requireMoreCharacters && $exprStringToInvalidate === $this->getNodeKey($expr)) {
35393539
return false;
35403540
}
3541-
if ($expr instanceof PropertyFetch) {
3542-
$propertyReflection = $this->propertyReflectionFinder->findPropertyReflectionFromNode($expr, $this);
3543-
if ($propertyReflection !== null) {
3544-
$nativePropertyReflection = $propertyReflection->getNativeReflection();
3545-
if ($nativePropertyReflection !== null && $nativePropertyReflection->isReadOnly()) {
3546-
return false;
3547-
}
3548-
}
3549-
}
35503541

35513542
$nodeFinder = new NodeFinder();
35523543
$expressionToInvalidateClass = get_class($exprToInvalidate);
@@ -3560,7 +3551,21 @@ private function shouldInvalidateExpression(string $exprStringToInvalidate, Expr
35603551
return $nodeString === $exprStringToInvalidate;
35613552
});
35623553

3563-
return $found !== null;
3554+
if ($found === null) {
3555+
return false;
3556+
}
3557+
3558+
if ($this->phpVersion->supportsReadOnlyProperties() && $expr instanceof PropertyFetch && $expr->name instanceof Node\Identifier && $requireMoreCharacters) {
3559+
$propertyReflection = $this->propertyReflectionFinder->findPropertyReflectionFromNode($expr, $this);
3560+
if ($propertyReflection !== null) {
3561+
$nativePropertyReflection = $propertyReflection->getNativeReflection();
3562+
if ($nativePropertyReflection !== null && $nativePropertyReflection->isReadOnly()) {
3563+
return false;
3564+
}
3565+
}
3566+
}
3567+
3568+
return true;
35643569
}
35653570

35663571
private function invalidateMethodsOnExpression(Expr $expressionToInvalidate): self

tests/PHPStan/Analyser/NodeScopeResolverTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1142,6 +1142,11 @@ public function dataFileAsserts(): iterable
11421142
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-82.php');
11431143
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-4565.php');
11441144
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-3789.php');
1145+
1146+
if (PHP_VERSION_ID >= 80100) {
1147+
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-8543.php');
1148+
}
1149+
11451150
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-8520.php');
11461151
}
11471152

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php // lint >= 8.1
2+
3+
namespace Bug8543;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class HelloWorld
8+
{
9+
public readonly int $i;
10+
11+
public int $j;
12+
13+
public function invalidate(): void
14+
{
15+
}
16+
}
17+
18+
function (HelloWorld $hw): void {
19+
$hw->i = 1;
20+
$hw->j = 2;
21+
assertType('1', $hw->i);
22+
assertType('2', $hw->j);
23+
24+
$hw->invalidate();
25+
assertType('1', $hw->i);
26+
assertType('int', $hw->j);
27+
28+
$hw = new HelloWorld();
29+
assertType('int', $hw->i);
30+
assertType('int', $hw->j);
31+
};

tests/PHPStan/Analyser/data/dependent-expression-certainty.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ function (bool $a, bool $b) {
153153

154154
assertVariableCertainty(TrinaryLogic::createMaybe(), $foo);
155155
if (returnsBool($b)) {
156-
assertVariableCertainty(TrinaryLogic::createYes(), $foo);
156+
assertVariableCertainty(TrinaryLogic::createMaybe(), $foo); // could be Yes
157157
}
158158

159159
if (returnsBool($a)) {

tests/PHPStan/Analyser/data/dependent-variable-certainty.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ function (bool $a, bool $b) {
149149

150150
assertVariableCertainty(TrinaryLogic::createMaybe(), $foo);
151151
if ($b) {
152-
assertVariableCertainty(TrinaryLogic::createYes(), $foo);
152+
assertVariableCertainty(TrinaryLogic::createMaybe(), $foo); // could be Yes
153153
}
154154

155155
if ($a) {

0 commit comments

Comments
 (0)