From 3eea15e840ce92d5a21ec2be96216b2ecc8ed283 Mon Sep 17 00:00:00 2001 From: Jack Robertson Date: Sat, 24 Jul 2021 16:02:13 +0100 Subject: [PATCH 1/2] Add failing test --- tests/ReturnTypeProvider/ArraySliceTest.php | 28 +++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 tests/ReturnTypeProvider/ArraySliceTest.php diff --git a/tests/ReturnTypeProvider/ArraySliceTest.php b/tests/ReturnTypeProvider/ArraySliceTest.php new file mode 100644 index 00000000000..5cb2c34d014 --- /dev/null +++ b/tests/ReturnTypeProvider/ArraySliceTest.php @@ -0,0 +1,28 @@ + [ + ' Date: Sat, 24 Jul 2021 16:09:29 +0100 Subject: [PATCH 2/2] Support array_slice template parameter return type inference --- .../ArraySliceReturnTypeProvider.php | 75 ++++++++++++------- 1 file changed, 47 insertions(+), 28 deletions(-) diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArraySliceReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArraySliceReturnTypeProvider.php index 510f390f7f2..cca2571b849 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArraySliceReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArraySliceReturnTypeProvider.php @@ -18,50 +18,69 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev { $statements_source = $event->getStatementsSource(); $call_args = $event->getCallArgs(); + if (!$statements_source instanceof \Psalm\Internal\Analyzer\StatementsAnalyzer) { return Type::getMixed(); } $first_arg = isset($call_args[0]->value) ? $call_args[0]->value : null; - $first_arg_array = $first_arg - && ($first_arg_type = $statements_source->node_data->getType($first_arg)) - && $first_arg_type->hasType('array') - && ($array_atomic_type = $first_arg_type->getAtomicTypes()['array']) - && ($array_atomic_type instanceof Type\Atomic\TArray - || $array_atomic_type instanceof Type\Atomic\TKeyedArray - || $array_atomic_type instanceof Type\Atomic\TList) - ? $array_atomic_type - : null; - - if (!$first_arg_array) { + if (!$first_arg) { return Type::getArray(); } - $dont_preserve_int_keys = !isset($call_args[3]->value) - || (($third_arg_type = $statements_source->node_data->getType($call_args[3]->value)) - && ((string) $third_arg_type === 'false')); - - $already_cloned = false; + $first_arg_type = $statements_source->node_data->getType($first_arg); - if ($first_arg_array instanceof Type\Atomic\TKeyedArray) { - $already_cloned = true; - $first_arg_array = $first_arg_array->getGenericArrayType(); + if (!$first_arg_type) { + return Type::getArray(); } - if ($first_arg_array instanceof Type\Atomic\TArray) { - if (!$already_cloned) { - $first_arg_array = clone $first_arg_array; + $atomic_types = $first_arg_type->getAtomicTypes(); + + $return_atomic_type = null; + + while ($atomic_type = \array_shift($atomic_types)) { + if ($atomic_type instanceof Type\Atomic\TTemplateParam) { + $atomic_types = \array_merge($atomic_types, $atomic_type->as->getAtomicTypes()); + continue; + } + + $already_cloned = false; + + if ($atomic_type instanceof Type\Atomic\TKeyedArray) { + $already_cloned = true; + $atomic_type = $atomic_type->getGenericArrayType(); + } + + if ($atomic_type instanceof Type\Atomic\TArray) { + if (!$already_cloned) { + $atomic_type = clone $atomic_type; + } + + $return_atomic_type = new Type\Atomic\TArray($atomic_type->type_params); + continue; + } + + if ($atomic_type instanceof Type\Atomic\TList) { + $return_atomic_type = new Type\Atomic\TArray([Type::getInt(), clone $atomic_type->type_param]); + continue; } - $array_type = new Type\Atomic\TArray($first_arg_array->type_params); - } else { - $array_type = new Type\Atomic\TArray([Type::getInt(), clone $first_arg_array->type_param]); + + return Type::getArray(); } - if ($dont_preserve_int_keys && $array_type->type_params[0]->isInt()) { - $array_type = new Type\Atomic\TList($array_type->type_params[1]); + if (!$return_atomic_type) { + throw new \UnexpectedValueException('This should never happen'); + } + + $dont_preserve_int_keys = !isset($call_args[3]->value) + || (($third_arg_type = $statements_source->node_data->getType($call_args[3]->value)) + && ((string) $third_arg_type === 'false')); + + if ($dont_preserve_int_keys && $return_atomic_type->type_params[0]->isInt()) { + $return_atomic_type = new Type\Atomic\TList($return_atomic_type->type_params[1]); } - return new Type\Union([$array_type]); + return new Type\Union([$return_atomic_type]); } }