Skip to content

Commit 3b9d6b2

Browse files
committed
Allow offset in fori loop
1 parent 8f9490e commit 3b9d6b2

File tree

2 files changed

+31
-4
lines changed

2 files changed

+31
-4
lines changed

src/Analyser/NodeScopeResolver.php

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@
182182
use PHPStan\Type\Generic\TemplateTypeMap;
183183
use PHPStan\Type\Generic\TemplateTypeVariance;
184184
use PHPStan\Type\Generic\TemplateTypeVarianceMap;
185+
use PHPStan\Type\IntegerRangeType;
185186
use PHPStan\Type\IntegerType;
186187
use PHPStan\Type\IntersectionType;
187188
use PHPStan\Type\MixedType;
@@ -1631,7 +1632,7 @@ private function processStmtNode(
16311632
if ($lastCondExpr !== null) {
16321633
$alwaysIterates = $alwaysIterates->and($bodyScope->getType($lastCondExpr)->toBoolean()->isTrue());
16331634
$bodyScope = $this->processExprNode($stmt, $lastCondExpr, $bodyScope, $nodeCallback, ExpressionContext::createDeep())->getTruthyScope();
1634-
$bodyScope = $this->inferForLoopExpressions($stmt, $lastCondExpr, $bodyScope);
1635+
$bodyScope = $this->inferForLoopExpressions($stmt, $lastCondExpr, $bodyScope, $initScope);
16351636
}
16361637

16371638
$finalScopeResult = $this->processStmtNodes($stmt, $stmt->stmts, $bodyScope, $nodeCallback, $context)->filterOutLoopExitPoints();
@@ -7308,17 +7309,22 @@ private function getFilteringExprForMatchArm(Expr\Match_ $expr, array $condition
73087309
);
73097310
}
73107311

7311-
private function inferForLoopExpressions(For_ $stmt, Expr $lastCondExpr, MutatingScope $bodyScope): MutatingScope
7312+
private function inferForLoopExpressions(
7313+
For_ $stmt,
7314+
Expr $lastCondExpr,
7315+
MutatingScope $bodyScope,
7316+
MutatingScope $initScope,
7317+
): MutatingScope
73127318
{
73137319
// infer $items[$i] type from for ($i = 0; $i < count($items); $i++) {...}
73147320

7321+
$positiveInt = IntegerRangeType::fromInterval(0, null);
73157322
if (
73167323
// $i = 0
73177324
count($stmt->init) === 1
73187325
&& $stmt->init[0] instanceof Assign
73197326
&& $stmt->init[0]->var instanceof Variable
7320-
&& $stmt->init[0]->expr instanceof Node\Scalar\Int_
7321-
&& $stmt->init[0]->expr->value === 0
7327+
&& $positiveInt->isSuperTypeOf($initScope->getType($stmt->init[0]->expr))->yes()
73227328
// $i++ or ++$i
73237329
&& count($stmt->loop) === 1
73247330
&& ($stmt->loop[0] instanceof Expr\PreInc || $stmt->loop[0] instanceof Expr\PostInc)

tests/PHPStan/Analyser/nsrt/for-loop-expr.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,24 @@ function getItemsArray(array $items): array
5050
assertType('array<string>', $items);
5151
return $items;
5252
}
53+
54+
/**
55+
* @param array<string> $items
56+
*/
57+
function skipFirstElement(array $items): void
58+
{
59+
for ($i = 1; count($items) > $i; ++$i) {
60+
$items[$i] = 'hello';
61+
}
62+
}
63+
64+
/**
65+
* @param positive-int $skip
66+
* @param array<string> $items
67+
*/
68+
function skipByX(int $skip, array $items): void
69+
{
70+
for ($i = $skip; count($items) > $i; ++$i) {
71+
$items[$i] = 'hello';
72+
}
73+
}

0 commit comments

Comments
 (0)