1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-22 05:41:20 +01:00

Add range checks to identify for loops that always enter

This commit is contained in:
Matthew Brown 2020-01-04 13:05:23 -05:00
parent 9fa2db1b6c
commit 8cbc26c2f1
2 changed files with 59 additions and 0 deletions

View File

@ -32,10 +32,20 @@ class ForAnalyzer
$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 @@ class ForAnalyzer
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) {

View File

@ -109,6 +109,14 @@ class ForTest extends \Psalm\Tests\TestCase
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 @@ class ForTest extends \Psalm\Tests\TestCase
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',
],
];
}
}