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

Merge pull request #7106 from orklah/7098

fix reconciliation between positive-int and inferior/superior assertions
This commit is contained in:
orklah 2021-12-08 22:29:15 +01:00 committed by GitHub
commit 604f47d52a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 53 additions and 30 deletions

View File

@ -1638,8 +1638,7 @@ class SimpleAssertionReconciler extends Reconciler
}
$existing_var_type->addType($atomic_type);
} elseif ($atomic_type instanceof Atomic\TLiteralInt) {
$new_range = new Atomic\TIntRange($assertion_value, null);
if (!$new_range->contains($atomic_type->value)) {
if ($atomic_type->value < $assertion_value) {
$existing_var_type->removeType($atomic_type->getKey());
} /*elseif ($inside_loop) {
//when inside a loop, allow the range to extends the type
@ -1651,11 +1650,10 @@ class SimpleAssertionReconciler extends Reconciler
}
}*/
} elseif ($atomic_type instanceof Atomic\TPositiveInt) {
if ($assertion_value <= 0) {
//emit an issue here in the future about incompatible type
if ($assertion_value > 1) {
$existing_var_type->removeType($atomic_type->getKey());
$existing_var_type->addType(new Atomic\TIntRange($assertion_value, null));
}
$existing_var_type->removeType($atomic_type->getKey());
$existing_var_type->addType(new Atomic\TIntRange($assertion_value, null));
} elseif ($atomic_type instanceof TInt) {
$existing_var_type->removeType($atomic_type->getKey());
$existing_var_type->addType(new Atomic\TIntRange($assertion_value, null));
@ -1685,10 +1683,9 @@ class SimpleAssertionReconciler extends Reconciler
}
$existing_var_type->addType($atomic_type);
} elseif ($atomic_type instanceof Atomic\TLiteralInt) {
$new_range = new Atomic\TIntRange(null, $assertion_value);
if (!$new_range->contains($atomic_type->value)) {
if ($atomic_type->value > $assertion_value) {
$existing_var_type->removeType($atomic_type->getKey());
}/* elseif ($inside_loop) {
} /* elseif ($inside_loop) {
//when inside a loop, allow the range to extends the type
$existing_var_type->removeType($atomic_type->getKey());
if ($atomic_type->value < $assertion_value) {
@ -1698,11 +1695,10 @@ class SimpleAssertionReconciler extends Reconciler
}
}*/
} elseif ($atomic_type instanceof Atomic\TPositiveInt) {
if ($assertion_value <= 0) {
//emit an issue here in the future about incompatible type
}
$existing_var_type->removeType($atomic_type->getKey());
$existing_var_type->addType(new Atomic\TIntRange(1, $assertion_value));
if ($assertion_value >= 1) {
$existing_var_type->addType(new Atomic\TIntRange(1, $assertion_value));
}
} elseif ($atomic_type instanceof TInt) {
$existing_var_type->removeType($atomic_type->getKey());
$existing_var_type->addType(new Atomic\TIntRange(null, $assertion_value));

View File

@ -1519,9 +1519,7 @@ class SimpleNegatedAssertionReconciler extends Reconciler
}
$existing_var_type->addType($atomic_type);
} elseif ($atomic_type instanceof Atomic\TLiteralInt) {
$new_range = new Atomic\TIntRange(null, $assertion_value);
if (!$new_range->contains($atomic_type->value)) {
//emit an issue here in the future about incompatible type
if ($atomic_type->value > $assertion_value) {
$existing_var_type->removeType($atomic_type->getKey());
} /*elseif ($inside_loop) {
//when inside a loop, allow the range to extends the type
@ -1533,11 +1531,10 @@ class SimpleNegatedAssertionReconciler extends Reconciler
}
}*/
} elseif ($atomic_type instanceof Atomic\TPositiveInt) {
if ($assertion_value > 0) {
//emit an issue here in the future about incompatible type
}
$existing_var_type->removeType($atomic_type->getKey());
$existing_var_type->addType(new Atomic\TIntRange(null, $assertion_value));
if ($assertion_value >= 1) {
$existing_var_type->addType(new Atomic\TIntRange(1, $assertion_value));
}
} elseif ($atomic_type instanceof TInt) {
$existing_var_type->removeType($atomic_type->getKey());
$existing_var_type->addType(new Atomic\TIntRange(null, $assertion_value));
@ -1564,11 +1561,9 @@ class SimpleNegatedAssertionReconciler extends Reconciler
}
$existing_var_type->addType($atomic_type);
} elseif ($atomic_type instanceof Atomic\TLiteralInt) {
$new_range = new Atomic\TIntRange($assertion_value, null);
if (!$new_range->contains($atomic_type->value)) {
//emit an issue here in the future about incompatible type
if ($atomic_type->value < $assertion_value) {
$existing_var_type->removeType($atomic_type->getKey());
}/* elseif ($inside_loop) {
} /* elseif ($inside_loop) {
//when inside a loop, allow the range to extends the type
$existing_var_type->removeType($atomic_type->getKey());
if ($atomic_type->value < $assertion_value) {
@ -1578,11 +1573,10 @@ class SimpleNegatedAssertionReconciler extends Reconciler
}
}*/
} elseif ($atomic_type instanceof Atomic\TPositiveInt) {
if ($assertion_value > 0) {
//emit an issue here in the future about incompatible type
if ($assertion_value > 1) {
$existing_var_type->removeType($atomic_type->getKey());
$existing_var_type->addType(new Atomic\TIntRange($assertion_value, null));
}
$existing_var_type->removeType($atomic_type->getKey());
$existing_var_type->addType(new Atomic\TIntRange($assertion_value, 1));
} elseif ($atomic_type instanceof TInt) {
$existing_var_type->removeType($atomic_type->getKey());
$existing_var_type->addType(new Atomic\TIntRange($assertion_value, null));

View File

@ -106,6 +106,10 @@ class TIntRange extends TInt
*/
public static function convertToIntRange(TInt $int_atomic): TIntRange
{
if ($int_atomic instanceof TIntRange) {
return $int_atomic;
}
if ($int_atomic instanceof TPositiveInt) {
return new TIntRange(1, null);
}

View File

@ -17,6 +17,7 @@ use Psalm\Storage\FileStorage;
use Psalm\Type;
use Psalm\Type\Atomic\TFloat;
use Psalm\Type\Atomic\TInt;
use Psalm\Type\Atomic\TIntRange;
use Psalm\Type\Atomic\TLiteralFloat;
use Psalm\Type\Atomic\TLiteralInt;
use Psalm\Type\Atomic\TLiteralString;
@ -280,8 +281,12 @@ class Union implements TypeNode
}
}
} elseif ($type instanceof TInt && $this->literal_int_types) {
foreach ($this->literal_int_types as $key => $_) {
unset($this->literal_int_types[$key], $this->types[$key]);
//we remove any literal that is already included in a wider type
$int_type_in_range = TIntRange::convertToIntRange($type);
foreach ($this->literal_int_types as $key => $literal_int_type) {
if ($int_type_in_range->contains($literal_int_type->value)) {
unset($this->literal_int_types[$key], $this->types[$key]);
}
}
} elseif ($type instanceof TFloat && $this->literal_float_types) {
foreach ($this->literal_float_types as $key => $_) {

View File

@ -644,6 +644,30 @@ class IntRangeTest extends TestCase
}
}',
],
'positiveIntToRangeWithInferior' => [
'<?php
/** @var positive-int $length */
$length = 0;
if ($length < 8) {
throw new \RuntimeException();
}',
'assertions' => [
'$length===' => 'int<8, max>',
],
],
'positiveIntToRangeWithSuperiorOrEqual' => [
'<?php
/** @var positive-int $length */
$length = 0;
if ($length >= 8) {
throw new \RuntimeException();
}',
'assertions' => [
'$length===' => 'int<1, 7>',
],
],
];
}