mirror of
https://github.com/danog/psalm.git
synced 2024-11-27 04:45:20 +01:00
Fix #4081 - better inference of positive ints
This commit is contained in:
parent
d29620a42b
commit
df0d426f61
@ -568,11 +568,29 @@ class NonDivArithmeticOpAnalyzer
|
||||
}
|
||||
|
||||
if ($left_type_part instanceof TInt && $right_type_part instanceof TInt) {
|
||||
$always_positive = !$parent instanceof PhpParser\Node\Expr\BinaryOp\Minus
|
||||
&& ($left_type_part instanceof TPositiveInt
|
||||
|| ($left_type_part instanceof TLiteralInt && $left_type_part->value > 0))
|
||||
&& ($right_type_part instanceof TPositiveInt
|
||||
|| ($right_type_part instanceof TLiteralInt && $right_type_part->value > 0));
|
||||
$left_is_positive = $left_type_part instanceof TPositiveInt
|
||||
|| ($left_type_part instanceof TLiteralInt && $left_type_part->value > 0);
|
||||
|
||||
$right_is_positive = $right_type_part instanceof TPositiveInt
|
||||
|| ($right_type_part instanceof TLiteralInt && $right_type_part->value > 0);
|
||||
|
||||
if ($parent instanceof PhpParser\Node\Expr\BinaryOp\Minus) {
|
||||
$always_positive = false;
|
||||
} elseif ($left_is_positive && $right_is_positive) {
|
||||
$always_positive = true;
|
||||
} elseif ($parent instanceof PhpParser\Node\Expr\BinaryOp\Plus
|
||||
&& ($left_type_part instanceof TLiteralInt && $left_type_part->value === 0)
|
||||
&& $right_is_positive
|
||||
) {
|
||||
$always_positive = true;
|
||||
} elseif ($parent instanceof PhpParser\Node\Expr\BinaryOp\Plus
|
||||
&& ($right_type_part instanceof TLiteralInt && $right_type_part->value === 0)
|
||||
&& $left_is_positive
|
||||
) {
|
||||
$always_positive = true;
|
||||
} else {
|
||||
$always_positive = false;
|
||||
}
|
||||
|
||||
if ($parent instanceof PhpParser\Node\Expr\BinaryOp\Mod) {
|
||||
$result_type = $always_positive ? Type::getPositiveInt() : Type::getInt();
|
||||
|
@ -992,8 +992,10 @@ class AssertionReconciler extends \Psalm\Type\Reconciler
|
||||
$can_be_equal = false;
|
||||
$did_remove_type = false;
|
||||
|
||||
foreach ($existing_var_atomic_types as $atomic_key => $_) {
|
||||
if ($atomic_key !== $assertion) {
|
||||
foreach ($existing_var_atomic_types as $atomic_key => $atomic_type) {
|
||||
if ($atomic_key !== $assertion
|
||||
&& !($atomic_type instanceof Type\Atomic\TPositiveInt && $value > 0)
|
||||
) {
|
||||
$existing_var_type->removeType($atomic_key);
|
||||
$did_remove_type = true;
|
||||
} else {
|
||||
|
@ -184,7 +184,7 @@ abstract class Type
|
||||
*/
|
||||
public static function getPositiveInt(bool $from_calculation = false)
|
||||
{
|
||||
$union = new Union([new TInt()]);
|
||||
$union = new Union([new Type\Atomic\TPositiveInt()]);
|
||||
$union->from_calculation = $from_calculation;
|
||||
|
||||
return $union;
|
||||
|
@ -284,6 +284,21 @@ class BinaryOperationTest extends TestCase
|
||||
return $opts + ["host" => 5];
|
||||
}'
|
||||
],
|
||||
'addIntToZero' => [
|
||||
'<?php
|
||||
$tick = 0;
|
||||
|
||||
test($tick + 1);
|
||||
|
||||
$tick++;
|
||||
|
||||
test($tick);
|
||||
|
||||
/**
|
||||
* @psalm-param positive-int $tickedTimes
|
||||
*/
|
||||
function test(int $tickedTimes): void {}'
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user