Skip to content

Commit

Permalink
Property assignment invalidates all remembered method values on an ob…
Browse files Browse the repository at this point in the history
…ject
  • Loading branch information
ondrejmirtes committed Sep 22, 2021
1 parent 7a2a437 commit 5f91da6
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 1 deletion.
63 changes: 62 additions & 1 deletion src/Analyser/MutatingScope.php
Expand Up @@ -3736,7 +3736,10 @@ public function specifyExpressionType(Expr $expr, Type $type, ?Type $nativeType
public function assignExpression(Expr $expr, Type $type): self
{
$scope = $this;
if ($expr instanceof PropertyFetch || $expr instanceof Expr\StaticPropertyFetch) {
if ($expr instanceof PropertyFetch) {
$scope = $this->invalidateExpression($expr)
->invalidateMethodsOnExpression($expr->var);
} elseif ($expr instanceof Expr\StaticPropertyFetch) {
$scope = $this->invalidateExpression($expr);
}

Expand Down Expand Up @@ -3806,6 +3809,64 @@ public function invalidateExpression(Expr $expressionToInvalidate, bool $require
);
}

public function invalidateMethodsOnExpression(Expr $expressionToInvalidate): self
{
$exprStringToInvalidate = $this->getNodeKey($expressionToInvalidate);
$moreSpecificTypeHolders = $this->moreSpecificTypes;
$nativeExpressionTypes = $this->nativeExpressionTypes;
$invalidated = false;
$nodeFinder = new NodeFinder();
foreach (array_keys($moreSpecificTypeHolders) as $exprString) {
$exprString = (string) $exprString;

try {
$expr = $this->parser->parseString('<?php ' . $exprString . ';')[0];
} catch (\PHPStan\Parser\ParserErrorsException $e) {
continue;
}
if (!$expr instanceof Node\Stmt\Expression) {
throw new \PHPStan\ShouldNotHappenException();
}
$found = $nodeFinder->findFirst([$expr->expr], function (Node $node) use ($exprStringToInvalidate): bool {
if (!$node instanceof MethodCall) {
return false;
}

return $this->getNodeKey($node->var) === $exprStringToInvalidate;
});
if ($found === null) {
continue;
}

unset($moreSpecificTypeHolders[$exprString]);
unset($nativeExpressionTypes[$exprString]);
$invalidated = true;
}

if (!$invalidated) {
return $this;
}

return $this->scopeFactory->create(
$this->context,
$this->isDeclareStrictTypes(),
$this->constantTypes,
$this->getFunction(),
$this->getNamespace(),
$this->getVariableTypes(),
$moreSpecificTypeHolders,
$this->conditionalExpressions,
$this->inClosureBindScopeClass,
$this->anonymousFunctionReflection,
$this->inFirstLevelStatement,
$this->currentlyAssignedExpressions,
$nativeExpressionTypes,
[],
$this->afterExtractCall,
$this->parentScope
);
}

public function removeTypeFromExpression(Expr $expr, Type $typeToRemove): self
{
$exprType = $this->getType($expr);
Expand Down
1 change: 1 addition & 0 deletions tests/PHPStan/Analyser/NodeScopeResolverTest.php
Expand Up @@ -510,6 +510,7 @@ public function dataFileAsserts(): iterable

yield from $this->gatherAssertTypes(__DIR__ . '/data/eval-implicit-throw.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-5628.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-5501.php');
}

/**
Expand Down
61 changes: 61 additions & 0 deletions tests/PHPStan/Analyser/data/bug-5501.php
@@ -0,0 +1,61 @@
<?php

namespace Bug5501;

use function PHPStan\Testing\assertType;

class Durable
{

/** @var int */
private $damage = 0;

/** @var int */
private $prop2 = 0;

/** @var mixed[] */
private $array;

public function applyDamage(int $amount): void
{
$this->prop2 = 5;

if ($this->isBroken()){
return;
}

assertType('false', $this->isBroken());
assertType('5', $this->prop2);

$this->damage = min($this->damage + $amount, 5);

assertType('bool', $this->isBroken());
assertType('5', $this->prop2);
}

public function applyDamage2(int $amount): void
{
$this->prop2 = 5;

if ($this->isBroken()){
return;
}

assertType('false', $this->isBroken());
assertType('5', $this->prop2);

$this->array['foo'] = min($this->damage + $amount, 5);

assertType('bool', $this->isBroken());
assertType('5', $this->prop2);
}

protected function onBroken(): void
{

}

public function isBroken(): bool{
return $this->damage >= 5;
}
}

0 comments on commit 5f91da6

Please sign in to comment.