mirror of
https://github.com/danog/psalm.git
synced 2025-01-21 21:31:13 +01:00
Add range checks to identify for loops that always enter
This commit is contained in:
parent
9fa2db1b6c
commit
8cbc26c2f1
@ -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) {
|
||||
|
@ -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',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user