Skip to content

Commit

Permalink
Fix readonly properties bugs and infinite recursion
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Dec 17, 2022
1 parent a8975b1 commit a7fed03
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 13 deletions.
27 changes: 16 additions & 11 deletions src/Analyser/MutatingScope.php
Original file line number Diff line number Diff line change
Expand Up @@ -3465,7 +3465,7 @@ public function assignExpression(Expr $expr, Type $type, ?Type $nativeType = nul
} elseif ($expr instanceof Expr\StaticPropertyFetch) {
$scope = $this->invalidateExpression($expr);
} elseif ($expr instanceof Variable) {
$scope = $this->invalidateExpression($expr, true);
$scope = $this->invalidateExpression($expr);
}

return $scope->specifyExpressionType($expr, $type, $nativeType);
Expand Down Expand Up @@ -3538,15 +3538,6 @@ private function shouldInvalidateExpression(string $exprStringToInvalidate, Expr
if ($requireMoreCharacters && $exprStringToInvalidate === $this->getNodeKey($expr)) {
return false;
}
if ($expr instanceof PropertyFetch) {
$propertyReflection = $this->propertyReflectionFinder->findPropertyReflectionFromNode($expr, $this);
if ($propertyReflection !== null) {
$nativePropertyReflection = $propertyReflection->getNativeReflection();
if ($nativePropertyReflection !== null && $nativePropertyReflection->isReadOnly()) {
return false;
}
}
}

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

return $found !== null;
if ($found === null) {
return false;
}

if ($this->phpVersion->supportsReadOnlyProperties() && $expr instanceof PropertyFetch && $expr->name instanceof Node\Identifier && $requireMoreCharacters) {
$propertyReflection = $this->propertyReflectionFinder->findPropertyReflectionFromNode($expr, $this);
if ($propertyReflection !== null) {
$nativePropertyReflection = $propertyReflection->getNativeReflection();
if ($nativePropertyReflection !== null && $nativePropertyReflection->isReadOnly()) {
return false;
}
}
}

return true;
}

private function invalidateMethodsOnExpression(Expr $expressionToInvalidate): self
Expand Down
5 changes: 5 additions & 0 deletions tests/PHPStan/Analyser/NodeScopeResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1142,6 +1142,11 @@ public function dataFileAsserts(): iterable
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-82.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-4565.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-3789.php');

if (PHP_VERSION_ID >= 80100) {
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-8543.php');
}

yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-8520.php');
}

Expand Down
31 changes: 31 additions & 0 deletions tests/PHPStan/Analyser/data/bug-8543.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php // lint >= 8.1

namespace Bug8543;

use function PHPStan\Testing\assertType;

class HelloWorld
{
public readonly int $i;

public int $j;

public function invalidate(): void
{
}
}

function (HelloWorld $hw): void {
$hw->i = 1;
$hw->j = 2;
assertType('1', $hw->i);
assertType('2', $hw->j);

$hw->invalidate();
assertType('1', $hw->i);
assertType('int', $hw->j);

$hw = new HelloWorld();
assertType('int', $hw->i);
assertType('int', $hw->j);
};
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ function (bool $a, bool $b) {

assertVariableCertainty(TrinaryLogic::createMaybe(), $foo);
if (returnsBool($b)) {
assertVariableCertainty(TrinaryLogic::createYes(), $foo);
assertVariableCertainty(TrinaryLogic::createMaybe(), $foo); // could be Yes
}

if (returnsBool($a)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ function (bool $a, bool $b) {

assertVariableCertainty(TrinaryLogic::createMaybe(), $foo);
if ($b) {
assertVariableCertainty(TrinaryLogic::createYes(), $foo);
assertVariableCertainty(TrinaryLogic::createMaybe(), $foo); // could be Yes
}

if ($a) {
Expand Down

0 comments on commit a7fed03

Please sign in to comment.