From fca60dcae817962779ef2f91318eebcb68618c7f Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Sat, 26 Nov 2022 15:45:01 +0100 Subject: [PATCH] Cleanup --- .../Internal/Type/AssertionReconciler.php | 8 +- .../Type/SimpleAssertionReconciler.php | 132 +++++++++++++----- 2 files changed, 104 insertions(+), 36 deletions(-) diff --git a/src/Psalm/Internal/Type/AssertionReconciler.php b/src/Psalm/Internal/Type/AssertionReconciler.php index 27430adf2..97daa774a 100644 --- a/src/Psalm/Internal/Type/AssertionReconciler.php +++ b/src/Psalm/Internal/Type/AssertionReconciler.php @@ -625,7 +625,7 @@ class AssertionReconciler extends Reconciler return $type_2_atomic->addIntersectionType($type_1_atomic); } - if ($type_2_atomic instanceof TKeyedArray + /*if ($type_2_atomic instanceof TKeyedArray && $type_1_atomic instanceof TList ) { $type_2_key = $type_2_atomic->getGenericKeyType(); @@ -678,7 +678,7 @@ class AssertionReconciler extends Reconciler true ); } - } + }*/ if ($type_2_atomic instanceof TTemplateParam && $type_1_atomic instanceof TTemplateParam @@ -730,7 +730,7 @@ class AssertionReconciler extends Reconciler } //we filter the second part of a list with the second part of standard iterables - if (($type_2_atomic instanceof TArray + /*if (($type_2_atomic instanceof TArray || $type_2_atomic instanceof TIterable) && $type_1_atomic instanceof TList ) { @@ -756,7 +756,7 @@ class AssertionReconciler extends Reconciler $matching_atomic_type = $type_1_atomic; $atomic_comparison_results->type_coerced = true; - } + }*/ //we filter each property of a Keyed Array with the second part of standard iterables if (($type_2_atomic instanceof TArray diff --git a/src/Psalm/Internal/Type/SimpleAssertionReconciler.php b/src/Psalm/Internal/Type/SimpleAssertionReconciler.php index 36ddd0a91..38e8d6662 100644 --- a/src/Psalm/Internal/Type/SimpleAssertionReconciler.php +++ b/src/Psalm/Internal/Type/SimpleAssertionReconciler.php @@ -261,7 +261,12 @@ class SimpleAssertionReconciler extends Reconciler if ($assertion instanceof HasExactCount) { return self::reconcileExactlyCountable( $existing_var_type, - $assertion->count + $assertion, + $key, + $negated, + $code_location, + $suppressed_issues, + $is_equality, ); } @@ -653,7 +658,7 @@ class SimpleAssertionReconciler extends Reconciler $redundant = false; $properties = $array_atomic_type->properties; for ($i = $prop_max_count; $i < $assertion->count; $i++) { - $properties[$i] = $properties[$i] + $properties[$i] = isset($properties[$i]) ? $properties[$i]->setPossiblyUndefined(false) : $array_atomic_type->fallback_params[1]; } @@ -691,46 +696,61 @@ class SimpleAssertionReconciler extends Reconciler return $existing_var_type->freeze(); } - /** - * @param positive-int $count - */ private static function reconcileExactlyCountable( Union $existing_var_type, - int $count + HasExactCount $assertion, + ?string $key, + bool $negated, + ?CodeLocation $code_location, + array $suppressed_issues, + bool $is_equality ): Union { $existing_var_type = $existing_var_type->getBuilder(); if ($existing_var_type->hasType('array')) { + $old_var_type_string = $existing_var_type->getId(); $array_atomic_type = $existing_var_type->getAtomicTypes()['array']; + $redundant = true; if ($array_atomic_type instanceof TArray) { - $non_empty_array = new TNonEmptyArray( - $array_atomic_type->type_params, - $count - ); + if (!$array_atomic_type instanceof TNonEmptyArray + || $array_atomic_type->count !== $assertion->count + ) { + $non_empty_array = new TNonEmptyArray( + $array_atomic_type->type_params, + $assertion->count + ); - $existing_var_type->removeType('array'); - $existing_var_type->addType( - $non_empty_array - ); - } elseif ($array_atomic_type instanceof TList) { - $properties = []; - for ($x = 0; $x < $count; $x++) { - $properties []= $array_atomic_type->type_param; + $existing_var_type->removeType('array'); + $existing_var_type->addType( + $non_empty_array + ); + + $redundant = false; + } else { + $redundant = true; } - $non_empty_list = new TKeyedArray( - $properties, - null, - null, - true - ); - - $existing_var_type->removeType('array'); - $existing_var_type->addType( - $non_empty_list - ); } elseif ($array_atomic_type instanceof TKeyedArray) { - if ($array_atomic_type->fallback_params === null) { - if (count($array_atomic_type->properties) === $count) { + $prop_max_count = count($array_atomic_type->properties); + $prop_min_count = 0; + foreach ($array_atomic_type->properties as $prop) { + if (!$prop->possibly_undefined) { + $prop_min_count++; + } + } + + if ($assertion->count < $prop_min_count) { + // Impossible + $existing_var_type->removeType('array'); + $redundant = false; + } elseif ($assertion->count === $prop_min_count) { + // Redundant + $redundant = true; + } elseif ($array_atomic_type->fallback_params === null) { + if ($assertion->count > $prop_max_count) { + // Impossible + $existing_var_type->removeType('array'); + $redundant = false; + } elseif ($assertion->count === $prop_max_count) { $existing_var_type->removeType('array'); $existing_var_type->addType($array_atomic_type->setProperties( array_map( @@ -738,14 +758,62 @@ class SimpleAssertionReconciler extends Reconciler $array_atomic_type->properties ) )); + } elseif ($existing_var_type->is_list) { + $properties = $array_atomic_type->properties; + for ($x = $prop_min_count; $x < $assertion->count; $x++) { + $properties[$x] = isset($properties[$x]) + ? $properties[$x]->setPossiblyUndefined(false) + : $array_atomic_type->fallback_params[1]; + } + $array_atomic_type = new TKeyedArray( + $properties, + null, + null, + true + ); + $existing_var_type->removeType('array'); + $existing_var_type->addType($array_atomic_type); } } else { - if ($array_atomic_type->allShapeKeysAlwaysDefined() && count($array_atomic_type->properties) === $count) { + if ($array_atomic_type->is_list) { + $properties = $array_atomic_type->properties; + for ($x = $prop_min_count; $x < $assertion->count; $x++) { + $properties[$x] = isset($properties[$x]) + ? $properties[$x]->setPossiblyUndefined(false) + : $array_atomic_type->fallback_params[1]; + } + $array_atomic_type = new TKeyedArray( + $properties, + null, + null, + true + ); + $existing_var_type->removeType('array'); + $existing_var_type->addType($array_atomic_type); + } elseif ($prop_max_count === $prop_min_count && $prop_max_count === $assertion->count) { $existing_var_type->removeType('array'); $existing_var_type->addType($array_atomic_type->makeSealed()); } } } + + if (!$is_equality + && !$existing_var_type->hasMixed() + && ($redundant || $existing_var_type->isUnionEmpty()) + ) { + if ($key && $code_location) { + self::triggerIssueForImpossible( + $existing_var_type, + $old_var_type_string, + $key, + $assertion, + $redundant, + $negated, + $code_location, + $suppressed_issues + ); + } + } } return $existing_var_type->freeze();