Skip to content

Commit

Permalink
Fix scope after for loop
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Jan 12, 2022
1 parent d05a86b commit 4fb1a77
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 28 deletions.
15 changes: 10 additions & 5 deletions src/Analyser/NodeScopeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -1029,19 +1029,24 @@ private function processStmtNode(
foreach ($finalScopeResult->getExitPointsByType(Continue_::class) as $continueExitPoint) {
$finalScope = $continueExitPoint->getScope()->mergeWith($finalScope);
}

$loopScope = $finalScope;
foreach ($stmt->loop as $loopExpr) {
$finalScope = $this->processExprNode($loopExpr, $finalScope, $nodeCallback, ExpressionContext::createTopLevel())->getScope();
$loopScope = $this->processExprNode($loopExpr, $loopScope, $nodeCallback, ExpressionContext::createTopLevel())->getScope();
}
$finalScope = $loopScope->generalizeWith($finalScope);
foreach ($stmt->cond as $condExpr) {
$finalScope = $finalScope->filterByFalseyValue($condExpr);
}

foreach ($finalScopeResult->getExitPointsByType(Break_::class) as $breakExitPoint) {
$finalScope = $breakExitPoint->getScope()->mergeWith($finalScope);
}

if ($this->polluteScopeWithLoopInitialAssignments) {
$scope = $initScope;
if (!$this->polluteScopeWithLoopInitialAssignments) {
$finalScope = $finalScope->mergeWith($scope);
}

$finalScope = $finalScope->mergeWith($scope);

return new StatementResult(
$finalScope,
$finalScopeResult->hasYield() || $hasYield,
Expand Down
76 changes: 53 additions & 23 deletions tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ public function dataAssignInIf(): array
$testScope,
'previousI',
TrinaryLogic::createYes(),
'0|1',
'int<1, max>',
],
[
$testScope,
Expand Down Expand Up @@ -582,14 +582,14 @@ public function dataAssignInIf(): array
[
$testScope,
'nonexistentVariableOutsideFor',
TrinaryLogic::createMaybe(),
TrinaryLogic::createYes(),
'1',
],
[
$testScope,
'integerOrNullFromFor',
TrinaryLogic::createYes(),
'1|null',
'1',
],
[
$testScope,
Expand Down Expand Up @@ -816,17 +816,17 @@ public function dataConstantTypes(): array
[
$testScope,
'incrementInForLoop',
'int<1, max>',
'int<2, max>',
],
[
$testScope,
'valueOverwrittenInForLoop',
'1|2',
'2',
],
[
$testScope,
'arrayOverwrittenInForLoop',
'array{a: int<1, max>, b: \'bar\'|\'foo\'}',
'array{a: int<2, max>, b: \'bar\'}',
],
[
$testScope,
Expand All @@ -836,12 +836,12 @@ public function dataConstantTypes(): array
[
$testScope,
'intProperty',
'int<1, max>',
'int<2, max>',
],
[
$testScope,
'staticIntProperty',
'int<1, max>',
'int<2, max>',
],
[
$testScope,
Expand Down Expand Up @@ -6922,11 +6922,6 @@ public function dataLoopVariables(): array
'$foo',
"'end'",
],
[
'LoopVariables\Bar|LoopVariables\Foo|LoopVariables\Lorem|null',
'$foo',
"'afterLoop'",
],
[
'int<1, max>|null',
'$nullableVal',
Expand All @@ -6942,11 +6937,6 @@ public function dataLoopVariables(): array
'$nullableVal',
"'nullableValElse'",
],
[
'1|int<10, max>|null',
'$nullableVal',
"'afterLoop'",
],
[
'LoopVariables\Foo|false',
'$falseOrObject',
Expand All @@ -6957,11 +6947,6 @@ public function dataLoopVariables(): array
'$falseOrObject',
"'end'",
],
[
'LoopVariables\Foo|false',
'$falseOrObject',
"'afterLoop'",
],
];
}

Expand Down Expand Up @@ -7043,6 +7028,21 @@ public function dataForeachLoopVariables(): array
'$i',
"'afterLoop'",
],
[
'LoopVariables\Bar|LoopVariables\Foo|LoopVariables\Lorem|null',
'$foo',
"'afterLoop'",
],
[
'1|int<10, max>|null',
'$nullableVal',
"'afterLoop'",
],
[
'LoopVariables\Foo|false',
'$falseOrObject',
"'afterLoop'",
],
];
}

Expand All @@ -7064,6 +7064,21 @@ public function dataWhileLoopVariables(): array
'$i',
"'afterLoop'",
],
[
'LoopVariables\Bar|LoopVariables\Foo|LoopVariables\Lorem|null',
'$foo',
"'afterLoop'",
],
[
'1|int<10, max>|null',
'$nullableVal',
"'afterLoop'",
],
[
'LoopVariables\Foo|false',
'$falseOrObject',
"'afterLoop'",
],
];
}

Expand All @@ -7086,6 +7101,21 @@ public function dataForLoopVariables(): array
'$i',
"'afterLoop'",
],
[
'LoopVariables\Bar|LoopVariables\Foo|LoopVariables\Lorem',
'$foo',
"'afterLoop'",
],
[
'1|int<10, max>',
'$nullableVal',
"'afterLoop'",
],
[
'LoopVariables\Foo',
'$falseOrObject',
"'afterLoop'",
],
];
}

Expand Down
21 changes: 21 additions & 0 deletions tests/PHPStan/Analyser/data/for-loop-i-type.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,30 @@ public function doBar() {
for($i = 1; $i < 50; $i++) {
assertType('int<1, 49>', $i);
}

assertType('50', $i);

for($i = 50; $i > 0; $i--) {
assertType('int<1, max>', $i); // could be int<1, 50>
}

assertType('0', $i);
}

public function doBaz() {
for($i = 1; $i < 50; $i += 2) {
assertType('int<1, 49>', $i);
}

assertType('int<50, 51>', $i);
}

public function doLOrem() {
for($i = 1; $i < 50; $i++) {
break;
}

assertType('int<1, 50>', $i);
}

}

0 comments on commit 4fb1a77

Please sign in to comment.