Skip to content
Permalink
Browse files

Add range checks to identify for loops that always enter

  • Loading branch information
muglug committed Jan 4, 2020
1 parent 9fa2db1 commit 8cbc26c2f17ccd764a5ff98c98c6ec2c206ef99b
Showing with 59 additions and 0 deletions.
  1. +41 −0 src/Psalm/Internal/Analyzer/Statements/Block/ForAnalyzer.php
  2. +18 −0 tests/Loop/ForTest.php
@@ -32,10 +32,20 @@ public static function analyze(
$pre_assigned_var_ids = $context->assigned_var_ids;
$context->assigned_var_ids = [];

$init_var_types = [];

foreach ($stmt->init as $init) {
if (ExpressionAnalyzer::analyze($statements_analyzer, $init, $context) === false) {
return false;
}

if ($init instanceof PhpParser\Node\Expr\Assign
&& $init->var instanceof PhpParser\Node\Expr\Variable
&& is_string($init->var->name)
&& ($init_var_type = $statements_analyzer->node_data->getType($init->expr))
) {
$init_var_types[$init->var->name] = $init_var_type;
}
}

$assigned_var_ids = $context->assigned_var_ids;
@@ -94,6 +104,37 @@ public static function analyze(
break;
}
}

if ($cond instanceof PhpParser\Node\Expr\BinaryOp
&& $cond->right instanceof PhpParser\Node\Scalar\LNumber
&& $cond->left instanceof PhpParser\Node\Expr\Variable
&& is_string($cond->left->name)
&& isset($init_var_types[$cond->left->name])
&& $init_var_types[$cond->left->name]->isSingleIntLiteral()
) {
$init_value = $init_var_types[$cond->left->name]->getSingleIntLiteral()->value;
$cond_value = $cond->right->value;

if ($cond instanceof PhpParser\Node\Expr\BinaryOp\Smaller && $init_value < $cond_value) {
$always_enters_loop = true;
break;
}

if ($cond instanceof PhpParser\Node\Expr\BinaryOp\SmallerOrEqual && $init_value <= $cond_value) {
$always_enters_loop = true;
break;
}

if ($cond instanceof PhpParser\Node\Expr\BinaryOp\Greater && $init_value > $cond_value) {
$always_enters_loop = true;
break;
}

if ($cond instanceof PhpParser\Node\Expr\BinaryOp\GreaterOrEqual && $init_value >= $cond_value) {
$always_enters_loop = true;
break;
}
}
}

if ($while_true) {
@@ -109,6 +109,14 @@ function test(Node $head) {
for ($i = 0; $i < 5; $i++);
echo $i;',
],
'nestedEchoAfterFor' => [
'<?php
for ($i = 1; $i < 2; $i++) {
for ($j = 1; $j < 2; $j++) {}
}
echo $i * $j;'
],
];
}

@@ -146,6 +154,16 @@ public function providerInvalidCodeParse()
echo $a;',
'error_message' => 'UndefinedGlobalVariable',
],
'nestedEchoAfterFor' => [
'<?php
for ($i = 1; $i < 2; $i++) {
if (rand(0, 1)) break;
for ($j = 1; $j < 2; $j++) {}
}
echo $i * $j;',
'error_message' => 'PossiblyUndefinedGlobalVariable',
],
];
}
}

0 comments on commit 8cbc26c

Please sign in to comment.
You can’t perform that action at this time.