Skip to content

Commit 4fb1a77

Browse files
committed
Fix scope after for loop
1 parent d05a86b commit 4fb1a77

File tree

3 files changed

+84
-28
lines changed

3 files changed

+84
-28
lines changed

Diff for: src/Analyser/NodeScopeResolver.php

+10-5
Original file line numberDiff line numberDiff line change
@@ -1029,19 +1029,24 @@ private function processStmtNode(
10291029
foreach ($finalScopeResult->getExitPointsByType(Continue_::class) as $continueExitPoint) {
10301030
$finalScope = $continueExitPoint->getScope()->mergeWith($finalScope);
10311031
}
1032+
1033+
$loopScope = $finalScope;
10321034
foreach ($stmt->loop as $loopExpr) {
1033-
$finalScope = $this->processExprNode($loopExpr, $finalScope, $nodeCallback, ExpressionContext::createTopLevel())->getScope();
1035+
$loopScope = $this->processExprNode($loopExpr, $loopScope, $nodeCallback, ExpressionContext::createTopLevel())->getScope();
1036+
}
1037+
$finalScope = $loopScope->generalizeWith($finalScope);
1038+
foreach ($stmt->cond as $condExpr) {
1039+
$finalScope = $finalScope->filterByFalseyValue($condExpr);
10341040
}
1041+
10351042
foreach ($finalScopeResult->getExitPointsByType(Break_::class) as $breakExitPoint) {
10361043
$finalScope = $breakExitPoint->getScope()->mergeWith($finalScope);
10371044
}
10381045

1039-
if ($this->polluteScopeWithLoopInitialAssignments) {
1040-
$scope = $initScope;
1046+
if (!$this->polluteScopeWithLoopInitialAssignments) {
1047+
$finalScope = $finalScope->mergeWith($scope);
10411048
}
10421049

1043-
$finalScope = $finalScope->mergeWith($scope);
1044-
10451050
return new StatementResult(
10461051
$finalScope,
10471052
$finalScopeResult->hasYield() || $hasYield,

Diff for: tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php

+53-23
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,7 @@ public function dataAssignInIf(): array
421421
$testScope,
422422
'previousI',
423423
TrinaryLogic::createYes(),
424-
'0|1',
424+
'int<1, max>',
425425
],
426426
[
427427
$testScope,
@@ -582,14 +582,14 @@ public function dataAssignInIf(): array
582582
[
583583
$testScope,
584584
'nonexistentVariableOutsideFor',
585-
TrinaryLogic::createMaybe(),
585+
TrinaryLogic::createYes(),
586586
'1',
587587
],
588588
[
589589
$testScope,
590590
'integerOrNullFromFor',
591591
TrinaryLogic::createYes(),
592-
'1|null',
592+
'1',
593593
],
594594
[
595595
$testScope,
@@ -816,17 +816,17 @@ public function dataConstantTypes(): array
816816
[
817817
$testScope,
818818
'incrementInForLoop',
819-
'int<1, max>',
819+
'int<2, max>',
820820
],
821821
[
822822
$testScope,
823823
'valueOverwrittenInForLoop',
824-
'1|2',
824+
'2',
825825
],
826826
[
827827
$testScope,
828828
'arrayOverwrittenInForLoop',
829-
'array{a: int<1, max>, b: \'bar\'|\'foo\'}',
829+
'array{a: int<2, max>, b: \'bar\'}',
830830
],
831831
[
832832
$testScope,
@@ -836,12 +836,12 @@ public function dataConstantTypes(): array
836836
[
837837
$testScope,
838838
'intProperty',
839-
'int<1, max>',
839+
'int<2, max>',
840840
],
841841
[
842842
$testScope,
843843
'staticIntProperty',
844-
'int<1, max>',
844+
'int<2, max>',
845845
],
846846
[
847847
$testScope,
@@ -6922,11 +6922,6 @@ public function dataLoopVariables(): array
69226922
'$foo',
69236923
"'end'",
69246924
],
6925-
[
6926-
'LoopVariables\Bar|LoopVariables\Foo|LoopVariables\Lorem|null',
6927-
'$foo',
6928-
"'afterLoop'",
6929-
],
69306925
[
69316926
'int<1, max>|null',
69326927
'$nullableVal',
@@ -6942,11 +6937,6 @@ public function dataLoopVariables(): array
69426937
'$nullableVal',
69436938
"'nullableValElse'",
69446939
],
6945-
[
6946-
'1|int<10, max>|null',
6947-
'$nullableVal',
6948-
"'afterLoop'",
6949-
],
69506940
[
69516941
'LoopVariables\Foo|false',
69526942
'$falseOrObject',
@@ -6957,11 +6947,6 @@ public function dataLoopVariables(): array
69576947
'$falseOrObject',
69586948
"'end'",
69596949
],
6960-
[
6961-
'LoopVariables\Foo|false',
6962-
'$falseOrObject',
6963-
"'afterLoop'",
6964-
],
69656950
];
69666951
}
69676952

@@ -7043,6 +7028,21 @@ public function dataForeachLoopVariables(): array
70437028
'$i',
70447029
"'afterLoop'",
70457030
],
7031+
[
7032+
'LoopVariables\Bar|LoopVariables\Foo|LoopVariables\Lorem|null',
7033+
'$foo',
7034+
"'afterLoop'",
7035+
],
7036+
[
7037+
'1|int<10, max>|null',
7038+
'$nullableVal',
7039+
"'afterLoop'",
7040+
],
7041+
[
7042+
'LoopVariables\Foo|false',
7043+
'$falseOrObject',
7044+
"'afterLoop'",
7045+
],
70467046
];
70477047
}
70487048

@@ -7064,6 +7064,21 @@ public function dataWhileLoopVariables(): array
70647064
'$i',
70657065
"'afterLoop'",
70667066
],
7067+
[
7068+
'LoopVariables\Bar|LoopVariables\Foo|LoopVariables\Lorem|null',
7069+
'$foo',
7070+
"'afterLoop'",
7071+
],
7072+
[
7073+
'1|int<10, max>|null',
7074+
'$nullableVal',
7075+
"'afterLoop'",
7076+
],
7077+
[
7078+
'LoopVariables\Foo|false',
7079+
'$falseOrObject',
7080+
"'afterLoop'",
7081+
],
70677082
];
70687083
}
70697084

@@ -7086,6 +7101,21 @@ public function dataForLoopVariables(): array
70867101
'$i',
70877102
"'afterLoop'",
70887103
],
7104+
[
7105+
'LoopVariables\Bar|LoopVariables\Foo|LoopVariables\Lorem',
7106+
'$foo',
7107+
"'afterLoop'",
7108+
],
7109+
[
7110+
'1|int<10, max>',
7111+
'$nullableVal',
7112+
"'afterLoop'",
7113+
],
7114+
[
7115+
'LoopVariables\Foo',
7116+
'$falseOrObject',
7117+
"'afterLoop'",
7118+
],
70897119
];
70907120
}
70917121

Diff for: tests/PHPStan/Analyser/data/for-loop-i-type.php

+21
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,30 @@ public function doBar() {
1111
for($i = 1; $i < 50; $i++) {
1212
assertType('int<1, 49>', $i);
1313
}
14+
15+
assertType('50', $i);
16+
1417
for($i = 50; $i > 0; $i--) {
1518
assertType('int<1, max>', $i); // could be int<1, 50>
1619
}
20+
21+
assertType('0', $i);
22+
}
23+
24+
public function doBaz() {
25+
for($i = 1; $i < 50; $i += 2) {
26+
assertType('int<1, 49>', $i);
27+
}
28+
29+
assertType('int<50, 51>', $i);
30+
}
31+
32+
public function doLOrem() {
33+
for($i = 1; $i < 50; $i++) {
34+
break;
35+
}
36+
37+
assertType('int<1, 50>', $i);
1738
}
1839

1940
}

0 commit comments

Comments
 (0)