1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-26 20:34:47 +01:00

added array-size max constraint to greater check (#4175)

added a few unit tests
This commit is contained in:
ygottschalk 2020-09-12 22:13:13 +02:00 committed by Daniil Gentili
parent 5d04368d98
commit c361f86c68
Signed by: danog
GPG Key ID: 8C1BE3B34B230CA7
2 changed files with 131 additions and 15 deletions

View File

@ -247,6 +247,8 @@ class AssertionFinder
$count_equality_position = self::hasNonEmptyCountEqualityCheck($conditional, $min_count);
$min_comparison = null;
$positive_number_position = self::hasPositiveNumberCheck($conditional, $min_comparison);
$max_count = null;
$count_inequality_position = self::hasLessThanCountEqualityCheck($conditional, $max_count);
if ($count_equality_position) {
if ($count_equality_position === self::ASSIGNMENT_TO_RIGHT) {
@ -277,6 +279,31 @@ class AssertionFinder
return $if_types;
}
if ($count_inequality_position) {
if ($count_inequality_position === self::ASSIGNMENT_TO_LEFT) {
$count_expr = $conditional->right;
} else {
throw new \UnexpectedValueException('$count_inequality_position value');
}
/** @var PhpParser\Node\Expr\FuncCall $count_expr */
$var_name = ExpressionIdentifier::getArrayVarId(
$count_expr->args[0]->value,
$this_class_name,
$source
);
if ($var_name) {
if ($max_count) {
$if_types[$var_name] = [['!has-at-least-' . ($max_count + 1)]];
} else {
$if_types[$var_name] = [['!non-empty-countable']];
}
}
return $if_types;
}
if ($positive_number_position) {
if ($positive_number_position === self::ASSIGNMENT_TO_RIGHT) {
$var_name = ExpressionIdentifier::getArrayVarId(
@ -2755,16 +2782,13 @@ class AssertionFinder
&& strtolower($conditional->left->name->parts[0]) === 'count'
&& $conditional->left->args;
$operator_greater_than_or_equal =
$operator_less_than_or_equal =
$conditional instanceof PhpParser\Node\Expr\BinaryOp\SmallerOrEqual
|| $conditional instanceof PhpParser\Node\Expr\BinaryOp\Smaller;
if ($left_count
&& $conditional->right instanceof PhpParser\Node\Scalar\LNumber
&& $operator_greater_than_or_equal
&& $conditional->right->value >= (
$conditional instanceof PhpParser\Node\Expr\BinaryOp\Smaller ? 0 : 1
)
&& $operator_less_than_or_equal
) {
$max_count = $conditional->right->value -
($conditional instanceof PhpParser\Node\Expr\BinaryOp\Smaller ? 1 : 0);
@ -2777,16 +2801,13 @@ class AssertionFinder
&& strtolower($conditional->right->name->parts[0]) === 'count'
&& $conditional->right->args;
$operator_less_than_or_equal =
$operator_greater_than_or_equal =
$conditional instanceof PhpParser\Node\Expr\BinaryOp\GreaterOrEqual
|| $conditional instanceof PhpParser\Node\Expr\BinaryOp\Greater;
if ($right_count
&& $conditional->left instanceof PhpParser\Node\Scalar\LNumber
&& $operator_less_than_or_equal
&& $conditional->left->value >= (
$conditional instanceof PhpParser\Node\Expr\BinaryOp\Greater ? 0 : 1
)
&& $operator_greater_than_or_equal
) {
$max_count = $conditional->left->value -
($conditional instanceof PhpParser\Node\Expr\BinaryOp\Greater ? 1 : 0);

View File

@ -419,15 +419,110 @@ class ArrayFunctionCallTest extends TestCase
'$b' => 'int',
],
],
'arrayPopNonEmptyAfterCountLessThanEqualToOne' => [
'arrayNotEmptyArrayAfterCountLessThanEqualToOne' => [
'<?php
/** @var list<int> */
$a = [1, 2, 3];
$b = 5;
if (count($a) <= 1) {
echo $a[0];
$leftCount = [1, 2, 3];
if (count($leftCount) <= 1) {
echo $leftCount[0];
}
/** @var list<int> */
$rightCount = [1, 2, 3];
if (1 >= count($rightCount)) {
echo $rightCount[0];
}',
],
'arrayNotEmptyArrayAfterCountLessThanTwo' => [
'<?php
/** @var list<int> */
$leftCount = [1, 2, 3];
if (count($leftCount) < 2) {
echo $leftCount[0];
}
/** @var list<int> */
$rightCount = [1, 2, 3];
if (2 > count($rightCount)) {
echo $rightCount[0];
}',
],
'arrayEmptyArrayAfterCountLessThanOne' => [
'<?php
/** @var list<int> */
$leftCount = [1, 2, 3];
assert (count($leftCount) < 1);
/** @var list<int> */
$rightCount = [1, 2, 3];
assert (1 > count($rightCount));',
'assertions' => [
'$leftCount' => 'array<empty, empty>',
'$rightCount' => 'array<empty, empty>',
],
],
'arrayEmptyArrayAfterCountLessThanEqualToZero' => [
'<?php
/** @var list<int> */
$leftCount = [1, 2, 3];
assert (count($leftCount) <= 0);
/** @var list<int> */
$rightCount = [1, 2, 3];
assert (0 >= count($rightCount));',
'assertions' => [
'$leftCount' => 'array<empty, empty>',
'$rightCount' => 'array<empty, empty>',
],
],
'arrayNotNonEmptyArrayAfterCountGreaterThanEqualToZero' => [
'<?php
/** @var list<int> */
$leftCount = [1, 2, 3];
assert(count($leftCount) >= 0);
/** @var list<int> */
$rightCount = [1, 2, 3];
assert(0 <= count($rightCount));',
'assertions' => [
'$leftCount' => 'list<int>',
'$rightCount' => 'list<int>',
],
],
'arrayNotNonEmptyArrayAfterCountGreaterThanMinusOne' => [
'<?php
/** @var list<int> */
$leftCount = [1, 2, 3];
assert (count($leftCount) > -1);
/** @var list<int> */
$rightCount = [1, 2, 3];
assert (-1 < count($rightCount));',
'assertions' => [
'$leftCount' => 'list<int>',
'$rightCount' => 'list<int>',
],
],
'arrayNonEmptyArrayAfterCountGreaterThanEqualToOne' => [
'<?php
/** @var list<int> */
$leftCount = [1, 2, 3];
assert(count($leftCount) >= 1);
/** @var list<int> */
$rightCount = [1, 2, 3];
assert(1 <= count($rightCount));',
'assertions' => [
'$leftCount' => 'non-empty-list<int>',
'$rightCount' => 'non-empty-list<int>',
],
],
'arrayNonEmptyArrayAfterCountGreaterThanZero' => [
'<?php
/** @var list<int> */
$leftCount = [1, 2, 3];
assert (count($leftCount) > 0);
/** @var list<int> */
$rightCount = [1, 2, 3];
assert (0 < count($rightCount));',
'assertions' => [
'$leftCount' => 'non-empty-list<int>',
'$rightCount' => 'non-empty-list<int>',
],
],
'arrayPopNonEmptyAfterArrayAddition' => [
'<?php
/** @var array<string, int> */