Skip to content

Commit

Permalink
Merge pull request #7106 from orklah/7098
Browse files Browse the repository at this point in the history
fix reconciliation between positive-int and inferior/superior assertions
  • Loading branch information
orklah committed Dec 8, 2021
2 parents eb2f4dc + 3f46377 commit 604f47d
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 30 deletions.
22 changes: 9 additions & 13 deletions src/Psalm/Internal/Type/SimpleAssertionReconciler.php
Expand Up @@ -1638,8 +1638,7 @@ private static function reconcileSuperiorTo(
}
$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
Expand All @@ -1651,11 +1650,10 @@ private static function reconcileSuperiorTo(
}
}*/
} 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));
Expand Down Expand Up @@ -1685,10 +1683,9 @@ private static function reconcileInferiorTo(
}
$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) {
Expand All @@ -1698,11 +1695,10 @@ private static function reconcileInferiorTo(
}
}*/
} 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));
Expand Down
24 changes: 9 additions & 15 deletions src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php
Expand Up @@ -1519,9 +1519,7 @@ private static function reconcileSuperiorTo(Union $existing_var_type, string $as
}
$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
Expand All @@ -1533,11 +1531,10 @@ private static function reconcileSuperiorTo(Union $existing_var_type, string $as
}
}*/
} 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));
Expand All @@ -1564,11 +1561,9 @@ private static function reconcileInferiorTo(Union $existing_var_type, string $as
}
$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) {
Expand All @@ -1578,11 +1573,10 @@ private static function reconcileInferiorTo(Union $existing_var_type, string $as
}
}*/
} 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));
Expand Down
4 changes: 4 additions & 0 deletions src/Psalm/Type/Atomic/TIntRange.php
Expand Up @@ -106,6 +106,10 @@ public static function getNewHighestBound(?int $bound1, ?int $bound2): ?int
*/
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);
}
Expand Down
9 changes: 7 additions & 2 deletions src/Psalm/Type/Union.php
Expand Up @@ -17,6 +17,7 @@
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;
Expand Down Expand Up @@ -280,8 +281,12 @@ public function addType(Atomic $type): void
}
}
} 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 => $_) {
Expand Down
24 changes: 24 additions & 0 deletions tests/IntRangeTest.php
Expand Up @@ -644,6 +644,30 @@ function doAnalysis(): void
}
}',
],
'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>',
],
],
];
}

Expand Down

0 comments on commit 604f47d

Please sign in to comment.