From 74eea18563e26652bbbfe0deb7b12a5fc1abc9e3 Mon Sep 17 00:00:00 2001 From: Brown Date: Sun, 26 Jul 2020 19:09:26 -0400 Subject: [PATCH] Add better checks --- .../Type/SimpleAssertionReconciler.php | 8 +-- src/Psalm/Internal/Type/TypeCombination.php | 54 +++++++++++++---- tests/TypeCombinationTest.php | 59 +++++++++++++++++++ tests/TypeReconciliation/ValueTest.php | 10 +++- 4 files changed, 115 insertions(+), 16 deletions(-) diff --git a/src/Psalm/Internal/Type/SimpleAssertionReconciler.php b/src/Psalm/Internal/Type/SimpleAssertionReconciler.php index 476d168e8..351c1e153 100644 --- a/src/Psalm/Internal/Type/SimpleAssertionReconciler.php +++ b/src/Psalm/Internal/Type/SimpleAssertionReconciler.php @@ -309,8 +309,7 @@ class SimpleAssertionReconciler extends \Psalm\Type\Reconciler $code_location, $suppressed_issues, $failed_reconciliation, - $is_equality, - $is_strict_equality + $is_equality ); } @@ -464,8 +463,7 @@ class SimpleAssertionReconciler extends \Psalm\Type\Reconciler ?CodeLocation $code_location, array $suppressed_issues, int &$failed_reconciliation, - bool $is_equality, - ?int $min_count + bool $is_equality ) : Union { $old_var_type_string = $existing_var_type->getId(); @@ -474,7 +472,7 @@ class SimpleAssertionReconciler extends \Psalm\Type\Reconciler $positive_types = []; foreach ($existing_var_type->getAtomicTypes() as $atomic_type) { - if ($atomic_type instanceof TLiteralInt + if ($atomic_type instanceof Type\Atomic\TLiteralInt && !$atomic_type->value < 1 ) { $did_remove_type = true; diff --git a/src/Psalm/Internal/Type/TypeCombination.php b/src/Psalm/Internal/Type/TypeCombination.php index 45b425355..5ffd60b3b 100644 --- a/src/Psalm/Internal/Type/TypeCombination.php +++ b/src/Psalm/Internal/Type/TypeCombination.php @@ -1243,24 +1243,51 @@ class TypeCombination return null; } + $had_zero = isset($combination->ints['int(0)']); + if ($type instanceof TLiteralInt) { + if ($type->value === 0) { + $had_zero = true; + } + if ($combination->ints !== null && count($combination->ints) < $literal_limit) { $combination->ints[$type_key] = $type; } else { + $combination->ints[$type_key] = $type; + + $all_nonnegative = !array_filter( + $combination->ints, + function ($int) { + return $int->value < 0; + } + ); + $combination->ints = null; - $combination->value_types['int'] = new TInt(); + + if (!isset($combination->value_types['int'])) { + $combination->value_types['int'] = $all_nonnegative ? new TPositiveInt() : new TInt(); + } elseif ($combination->value_types['int'] instanceof TPositiveInt + && !$all_nonnegative + ) { + $combination->value_types['int'] = new TInt(); + } } } else { if ($type instanceof TPositiveInt) { - if (($combination->ints - && !array_filter( - $combination->ints, - function ($int) { - return $int->value < 1; - } - )) - || !isset($combination->value_types['int']) - ) { + if ($combination->ints) { + $all_nonnegative = !array_filter( + $combination->ints, + function ($int) { + return $int->value < 0; + } + ); + + if ($all_nonnegative) { + $combination->value_types['int'] = $type; + } else { + $combination->value_types['int'] = new TInt(); + } + } elseif (!isset($combination->value_types['int'])) { $combination->value_types['int'] = $type; } } else { @@ -1270,6 +1297,13 @@ class TypeCombination $combination->ints = null; } + if ($had_zero + && isset($combination->value_types['int']) + && $combination->value_types['int'] instanceof TPositiveInt + ) { + $combination->ints = ['int(0)' => new TLiteralInt(0)]; + } + return null; } diff --git a/tests/TypeCombinationTest.php b/tests/TypeCombinationTest.php index 0d2a6cd56..377caeab3 100644 --- a/tests/TypeCombinationTest.php +++ b/tests/TypeCombinationTest.php @@ -571,6 +571,65 @@ class TypeCombinationTest extends TestCase 'array{null}' ], ], + 'combineZeroAndPositiveInt' => [ + 'int(0)|positive-int', + [ + '0', + 'positive-int', + ], + ], + 'combinePositiveIntAndZero' => [ + 'int(0)|positive-int', + [ + 'positive-int', + '0', + ], + ], + 'combinePositiveIntAndMinusOne' => [ + 'int', + [ + 'positive-int', + '-1', + ], + ], + 'combineMinusOneAndPositiveInt' => [ + 'int', + [ + '-1', + 'positive-int', + ], + ], + 'combineZeroMinusOneAndPositiveInt' => [ + 'int', + [ + '0', + '-1', + 'positive-int', + ], + ], + 'combineZeroOneAndPositiveInt' => [ + 'int(0)|positive-int', + [ + '0', + '1', + 'positive-int', + ], + ], + 'combinePositiveIntOneAndZero' => [ + 'int(0)|positive-int', + [ + 'positive-int', + '1', + '0', + ], + ], + 'combinePositiveInts' => [ + 'positive-int', + [ + 'positive-int', + 'positive-int', + ], + ], ]; } diff --git a/tests/TypeReconciliation/ValueTest.php b/tests/TypeReconciliation/ValueTest.php index 2bd8a85c1..6b089e96d 100644 --- a/tests/TypeReconciliation/ValueTest.php +++ b/tests/TypeReconciliation/ValueTest.php @@ -710,7 +710,15 @@ class ValueTest extends TestCase function makeNumStringFromFloat(float $v) { return (string) $v; }' - ], + ], + 'compareNegatedValue' => [ + ' 0)) { + echo $i; + }', + ], ]; }