From 54ac13b190b96097d6663c9b6cc50a5b859d8e11 Mon Sep 17 00:00:00 2001 From: Matt Brown Date: Sun, 28 Mar 2021 09:52:23 -0400 Subject: [PATCH] Allow nested template unwrapping inside `reset`, `end` Fixes #5208 --- ...rayPointerAdjustmentReturnTypeProvider.php | 54 ++++++++++++------- ...emplateTest.php => NestedTemplateTest.php} | 14 ++++- 2 files changed, 47 insertions(+), 21 deletions(-) rename tests/Template/{NestedClassTemplateTest.php => NestedTemplateTest.php} (91%) diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPointerAdjustmentReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPointerAdjustmentReturnTypeProvider.php index 8935be6e0..553a78172 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPointerAdjustmentReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPointerAdjustmentReturnTypeProvider.php @@ -26,29 +26,43 @@ class ArrayPointerAdjustmentReturnTypeProvider implements \Psalm\Plugin\EventHan $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::getMixed(); } - if ($first_arg_array instanceof Type\Atomic\TArray) { - $value_type = clone $first_arg_array->type_params[1]; - $definitely_has_items = $first_arg_array instanceof Type\Atomic\TNonEmptyArray; - } elseif ($first_arg_array instanceof Type\Atomic\TList) { - $value_type = clone $first_arg_array->type_param; - $definitely_has_items = $first_arg_array instanceof Type\Atomic\TNonEmptyList; - } else { - $value_type = $first_arg_array->getGenericValueType(); - $definitely_has_items = $first_arg_array->getGenericArrayType() instanceof Type\Atomic\TNonEmptyArray; + $first_arg_type = $statements_source->node_data->getType($first_arg); + + if (!$first_arg_type) { + return Type::getMixed(); + } + + $atomic_types = $first_arg_type->getAtomicTypes(); + + $value_type = null; + $definitely_has_items = false; + + 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; + } + + if ($atomic_type instanceof Type\Atomic\TArray) { + $value_type = clone $atomic_type->type_params[1]; + $definitely_has_items = $atomic_type instanceof Type\Atomic\TNonEmptyArray; + } elseif ($atomic_type instanceof Type\Atomic\TList) { + $value_type = clone $atomic_type->type_param; + $definitely_has_items = $atomic_type instanceof Type\Atomic\TNonEmptyList; + } elseif ($atomic_type instanceof Type\Atomic\TKeyedArray) { + $value_type = $atomic_type->getGenericValueType(); + $definitely_has_items = $atomic_type->getGenericArrayType() instanceof Type\Atomic\TNonEmptyArray; + } else { + return Type::getMixed(); + } + } + + if (!$value_type) { + throw new \UnexpectedValueException('This should never happen'); } if ($value_type->isEmpty()) { diff --git a/tests/Template/NestedClassTemplateTest.php b/tests/Template/NestedTemplateTest.php similarity index 91% rename from tests/Template/NestedClassTemplateTest.php rename to tests/Template/NestedTemplateTest.php index ea48599d1..00a3e2571 100644 --- a/tests/Template/NestedClassTemplateTest.php +++ b/tests/Template/NestedTemplateTest.php @@ -5,7 +5,7 @@ use const DIRECTORY_SEPARATOR; use Psalm\Tests\TestCase; use Psalm\Tests\Traits; -class NestedClassTemplateTest extends TestCase +class NestedTemplateTest extends TestCase { use Traits\InvalidCodeAnalysisTestTrait; use Traits\ValidCodeAnalysisTestTrait; @@ -107,6 +107,18 @@ class NestedClassTemplateTest extends TestCase $result = load(StringWrapper::class);' ], + 'unwrapNestedTemplateWithReset' => [ + ' + * @param TArray $arr + * @return TValue + */ + function toList(array $arr): array { + return reset($arr); + }' + ], ]; }