From 992f26a8e7e634118d8f63aedcb97f9c164ec681 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 16 Dec 2022 18:10:46 +0100 Subject: [PATCH 1/3] Fix array_merge edge case --- .../ArrayMergeReturnTypeProvider.php | 14 +++++++++++--- tests/ArrayFunctionCallTest.php | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php index 05efc3c4ef2..12a9854626c 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php @@ -130,10 +130,18 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev $class_strings[$key] = true; } - if (!isset($generic_properties[$key]) || !$type->possibly_undefined) { + if (!isset($generic_properties[$key]) || ( + !$type->possibly_undefined + && !$unpacking_possibly_empty + && $is_replace + )) { + if ($unpacking_possibly_empty) { + $type = $type->setPossiblyUndefined(true); + } $generic_properties[$key] = $type; } else { - $was_possibly_undefined = $generic_properties[$key]->possibly_undefined; + $was_possibly_undefined = $generic_properties[$key]->possibly_undefined + || $unpacking_possibly_empty; $generic_properties[$key] = Type::combineUnionTypes( $generic_properties[$key], @@ -147,7 +155,7 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev } } - if (!$unpacked_type_part->is_list && !$unpacking_possibly_empty) { + if (!$unpacked_type_part->is_list) { $all_nonempty_lists = false; } diff --git a/tests/ArrayFunctionCallTest.php b/tests/ArrayFunctionCallTest.php index 369b6aae59c..3189266a0cb 100644 --- a/tests/ArrayFunctionCallTest.php +++ b/tests/ArrayFunctionCallTest.php @@ -211,6 +211,24 @@ function getInts(): array { return [123]; } 'ignored_issues' => [], 'php_version' => '8.0', ], + 'arrayMergeListOfShapes' => [ + 'code' => ' */ + $a = []; + + $b = array_merge(...$a); + + /** @var non-empty-list */ + $c = []; + + $d = array_merge(...$c); + ', + 'assertions' => [ + '$b' => 'array{a?: int}', + '$d' => 'array{a: int}', + ] + ], 'arrayMergeIntArrays' => [ 'code' => ' Date: Fri, 16 Dec 2022 18:39:35 +0100 Subject: [PATCH 2/3] Fix --- .../ReturnTypeProvider/ArrayMergeReturnTypeProvider.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php index 12a9854626c..27b26595823 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php @@ -47,7 +47,7 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev return Type::getMixed(); } - $is_replace = substr($event->getFunctionId(), 6, 7) === 'replace'; + $is_replace = $event->getFunctionId() === 'array_replace'; $inner_value_types = []; $inner_key_types = []; @@ -113,7 +113,7 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev } if (is_string($key)) { $all_int_offsets = false; - } elseif (!$is_replace) { + } else if (!$is_replace) { if ($unpacking_indefinite_number_of_args || $type->possibly_undefined) { $added_inner_values = true; $inner_value_types = array_merge( From 2c00c64304b8230995552b6af6d4c309b5fb0525 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Fri, 16 Dec 2022 19:18:33 +0100 Subject: [PATCH 3/3] Bypass bug --- psalm-baseline.xml | 69 ++----------------- .../ArrayMergeReturnTypeProvider.php | 3 +- 2 files changed, 5 insertions(+), 67 deletions(-) diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 81a14de0338..b8dacf52dee 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,5 +1,5 @@ - + $comment_block->tags['variablesfrom'][0] @@ -390,6 +390,9 @@ $class_strings ?: null + + $is_replace + @@ -593,10 +596,6 @@ $key_type->possibly_undefined - - $fallback_params - $fallback_params - $this->properties[0] $this->properties[0] @@ -705,64 +704,4 @@ $subNodes['expr'] - - - $parts - - - - - $conds - - - - - self::prepareName($name) - - - - - $stmts - - - - - $stmts - - - - - $stmts - - - - - $stmts - - - - - $stmts - - - - - $stmts - - - - - $stmts - - - - - $stmts - - - - - $stmts - - diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php index 27b26595823..e66ed4a5c4f 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php @@ -22,7 +22,6 @@ use function count; use function is_string; use function max; -use function substr; /** * @internal @@ -113,7 +112,7 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev } if (is_string($key)) { $all_int_offsets = false; - } else if (!$is_replace) { + } elseif (!$is_replace) { if ($unpacking_indefinite_number_of_args || $type->possibly_undefined) { $added_inner_values = true; $inner_value_types = array_merge(