Skip to content

Commit

Permalink
Fixed expression invalidation when ++ is involved
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Jan 12, 2020
1 parent ca37ebd commit 9665e16
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 11 deletions.
25 changes: 15 additions & 10 deletions src/Analyser/MutatingScope.php
Original file line number Diff line number Diff line change
Expand Up @@ -2574,26 +2574,31 @@ public function assignExpression(Expr $expr, Type $type): self
return $scope->specifyExpressionType($expr, $type);
}

public function invalidateExpression(Expr $expressionToInvalidate, bool $requireMoreCharacters = false): self
public function invalidateExpression(Expr $expressionToInvalidate): self
{
$exprStringToInvalidate = $this->printer->prettyPrintExpr($expressionToInvalidate);
$moreSpecificTypeHolders = $this->moreSpecificTypes;
foreach (array_keys($moreSpecificTypeHolders) as $exprString) {
$exprString = (string) $exprString;
if ($requireMoreCharacters && $exprString === $exprStringToInvalidate) {
continue;
if (Strings::startsWith($exprString, $exprStringToInvalidate)) {
if ($exprString === $exprStringToInvalidate) {
unset($moreSpecificTypeHolders[$exprString]);
continue;
}
$nextLetter = substr($exprString, strlen($exprStringToInvalidate), 1);
if (Strings::match($nextLetter, '#[a-zA-Z_0-9\x7f-\xff]#') === null) {
unset($moreSpecificTypeHolders[$exprString]);
continue;
}
}
if (!Strings::startsWith($exprString, $exprStringToInvalidate)) {
$matches = \Nette\Utils\Strings::matchAll($exprString, '#\$[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*#');
if ($matches === []) {
continue;
}

if ($exprString === $exprStringToInvalidate) {
unset($moreSpecificTypeHolders[$exprString]);
continue;
}
$matches = array_column($matches, 0);

$nextLetter = substr($exprString, strlen($exprStringToInvalidate), 1);
if (Strings::match($nextLetter, '#[a-zA-Z_0-9\x7f-\xff]#') !== null) {
if (!in_array($exprStringToInvalidate, $matches, true)) {
continue;
}

Expand Down
4 changes: 3 additions & 1 deletion src/Analyser/NodeScopeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -1492,7 +1492,7 @@ function (MutatingScope $scope) use ($expr, $nodeCallback, $context): Expression
$result = $this->processArgs($methodReflection, $parametersAcceptor, $expr->args, $scope, $nodeCallback, $context);
$scope = $result->getScope();
if ($methodReflection !== null && $methodReflection->hasSideEffects()->yes()) {
$scope = $scope->invalidateExpression($expr->var, true);
$scope = $scope->invalidateExpression($expr->var);
}
$hasYield = $hasYield || $result->hasYield();
} elseif ($expr instanceof StaticCall) {
Expand Down Expand Up @@ -1829,6 +1829,8 @@ static function (MutatingScope $scope): ExpressionResult {
},
false
)->getScope();
} else {
$scope = $scope->invalidateExpression($expr->var);
}
}
} elseif ($expr instanceof Ternary) {
Expand Down
6 changes: 6 additions & 0 deletions tests/PHPStan/Analyser/NodeScopeResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9752,6 +9752,11 @@ public function dataListType(): array
return $this->gatherAssertTypes(__DIR__ . '/data/list-type.php');
}

public function dataBug2835(): array
{
return $this->gatherAssertTypes(__DIR__ . '/data/bug-2835.php');
}

/**
* @dataProvider dataBug2574
* @dataProvider dataBug2577
Expand All @@ -9775,6 +9780,7 @@ public function dataListType(): array
* @dataProvider dataPhpDocInheritanceConstructors
* @dataProvider dataListType
* @dataProvider dataBug2822
* @dataProvider dataBug2835
* @param ConstantStringType $expectedType
* @param Type $actualType
*/
Expand Down
41 changes: 41 additions & 0 deletions tests/PHPStan/Analyser/data/bug-2835.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

namespace Bug2835AssertTypes;

use function PHPStan\Analyser\assertType;

class Foo
{

/**
* @param array<int, array> $tokens
* @return bool
*/
public function doFoo(array $tokens): bool {
$i = 0;
while (isset($tokens[$i])) {
assertType('int', $i);
if ($tokens[$i]['code'] !== 1) {
assertType('mixed~1', $tokens[$i]['code']);
$i++;
assertType('int', $i);
assertType('mixed', $tokens[$i]['code']);
continue;
}
assertType('1', $tokens[$i]['code']);
$i++;
assertType('int', $i);
assertType('mixed', $tokens[$i]['code']);
if ($tokens[$i]['code'] !== 2) {
assertType('mixed~2', $tokens[$i]['code']);
$i++;
assertType('int', $i);
continue;
}
assertType('2', $tokens[$i]['code']);
return true;
}
return false;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -413,4 +413,10 @@ public function testStrictComparisonPropertyNativeTypesPhp74(): void
]);
}

public function testBug2835(): void
{
$this->checkAlwaysTrueStrictComparison = true;
$this->analyse([__DIR__ . '/data/bug-2835.php'], []);
}

}
29 changes: 29 additions & 0 deletions tests/PHPStan/Rules/Comparison/data/bug-2835.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace Bug2835;

class Foo
{

/**
* @param array<int, array> $tokens
* @return bool
*/
public function doFoo(array $tokens): bool {
$i = 0;
while (isset($tokens[$i])) {
if ($tokens[$i]['code'] !== 1) {
$i++;
continue;
}
$i++;
if ($tokens[$i]['code'] !== 2) {
$i++;
continue;
}
return true;
}
return false;
}

}

0 comments on commit 9665e16

Please sign in to comment.