From b9741cb1772ce116dcf4d3918da01c59d400a345 Mon Sep 17 00:00:00 2001 From: Daniil Gentili Date: Tue, 27 Feb 2024 18:03:35 +0100 Subject: [PATCH] Fix --- .../Reflector/FunctionLikeDocblockScanner.php | 1 - .../ArrayFilterParamsProvider.php | 31 +--- .../ArrayFilterReturnTypeProvider.php | 166 +++++++++--------- src/Psalm/Type/UnionTrait.php | 1 - 4 files changed, 93 insertions(+), 106 deletions(-) diff --git a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php index afcae5e88b0..d604b4ee8a6 100644 --- a/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php +++ b/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php @@ -44,7 +44,6 @@ use Psalm\Type; use Psalm\Type\Atomic\TArray; use Psalm\Type\Atomic\TConditional; -use Psalm\Type\Atomic\TKeyedArray; use Psalm\Type\Atomic\TNull; use Psalm\Type\Atomic\TTemplateParam; use Psalm\Type\TaintKindGroup; diff --git a/src/Psalm/Internal/Provider/ParamsProvider/ArrayFilterParamsProvider.php b/src/Psalm/Internal/Provider/ParamsProvider/ArrayFilterParamsProvider.php index 33805a000a2..3407bc1e025 100644 --- a/src/Psalm/Internal/Provider/ParamsProvider/ArrayFilterParamsProvider.php +++ b/src/Psalm/Internal/Provider/ParamsProvider/ArrayFilterParamsProvider.php @@ -16,9 +16,7 @@ use Psalm\Plugin\EventHandler\FunctionParamsProviderInterface; use Psalm\Storage\FunctionLikeParameter; use Psalm\Type; -use Psalm\Type\Atomic\TArray; use Psalm\Type\Atomic\TCallable; -use Psalm\Type\Atomic\TKeyedArray; use Psalm\Type\Union; use function strtolower; @@ -79,11 +77,12 @@ public static function getFunctionParams(FunctionParamsProviderEvent $event): ?a return null; } + $codebase = $statements_source->getCodebase(); // currently only supports literal types and variables (but not function calls) // due to https://github.com/vimeo/psalm/issues/8905 $first_arg_type = SimpleTypeInferer::infer( - $statements_source->getCodebase(), + $codebase, $statements_source->node_data, $call_args[0]->value, $statements_source->getAliases(), @@ -100,30 +99,18 @@ public static function getFunctionParams(FunctionParamsProviderEvent $event): ?a $first_arg_type = $event->getContext()->vars_in_scope[$extended_var_id] ?? null; } - $fallback = new TArray([Type::getArrayKey(), Type::getMixed()]); - if (!$first_arg_type || $first_arg_type->isMixed()) { - $first_arg_array = $fallback; + if (!$first_arg_type || $first_arg_type->isMixed() || !$first_arg_type->hasArray()) { + $key_type = Type::getArrayKey(); + $inner_type = Type::getMixed(); } else { - $first_arg_array = $first_arg_type->hasArray() - && ($array_atomic_type = $first_arg_type->getArray()) - && ($array_atomic_type instanceof TArray - || $array_atomic_type instanceof TKeyedArray) - ? $array_atomic_type - : $fallback; - } - - if ($first_arg_array instanceof TArray) { - $inner_type = $first_arg_array->type_params[1]; - $key_type = $first_arg_array->type_params[0]; - } else { - $inner_type = $first_arg_array->getGenericValueType(); - $key_type = $first_arg_array->getGenericKeyType(); + $inner_type = Type::combineUnionTypeArray($first_arg_type->getArrayValueTypes(), $codebase); + $key_type = Type::combineUnionTypeArray($first_arg_type->getArrayKeyTypes(), $codebase); } $has_both = false; if (isset($call_args[2])) { $mode_type = SimpleTypeInferer::infer( - $statements_source->getCodebase(), + $codebase, $statements_source->node_data, $call_args[2]->value, $statements_source->getAliases(), @@ -168,7 +155,7 @@ public static function getFunctionParams(FunctionParamsProviderEvent $event): ?a $inner_type = Type::combineUnionTypes( $inner_type, $key_type, - $statements_source->getCodebase(), + $codebase, ); continue; diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFilterReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFilterReturnTypeProvider.php index 1ee8e73cc5d..0f791fe8604 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFilterReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFilterReturnTypeProvider.php @@ -63,107 +63,109 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev $fallback = new TArray([Type::getArrayKey(), Type::getMixed()]); $array_arg = $call_args[0]->value ?? null; if (!$array_arg) { - $first_arg_array = $fallback; + $first_arg_arrays = [$fallback]; } else { $first_arg_type = $statements_source->node_data->getType($array_arg); - if (!$first_arg_type || $first_arg_type->isMixed()) { - $first_arg_array = $fallback; + if (!$first_arg_type || $first_arg_type->isMixed() || !($arrays = $first_arg_type->getArrays())) { + $first_arg_arrays = [$fallback]; } else { - $first_arg_array = $first_arg_type->hasArray() - && ($array_atomic_type = $first_arg_type->getArray()) - && ($array_atomic_type instanceof TArray - || $array_atomic_type instanceof TKeyedArray) - ? $array_atomic_type - : $fallback; + $first_arg_arrays = $arrays; } } - if ($first_arg_array instanceof TArray) { - $inner_type = $first_arg_array->type_params[1]; - $key_type = $first_arg_array->type_params[0]; - } else { - $inner_type = $first_arg_array->getGenericValueType(); - $key_type = $first_arg_array->getGenericKeyType(); - - if (!isset($call_args[1]) && $first_arg_array->fallback_params === null) { - $had_one = count($first_arg_array->properties) === 1; - - $new_properties = array_filter( - array_map( - static function ($keyed_type) use ($statements_source, $context) { - $prev_keyed_type = $keyed_type; - - $keyed_type = AssertionReconciler::reconcile( - new Truthy(), - $keyed_type, - '', - $statements_source, - $context->inside_loop, - [], - null, - $statements_source->getSuppressedIssues(), - ); + $out_types = []; + foreach ($first_arg_arrays as $first_arg_array) { + if ($first_arg_array instanceof TArray) { + $inner_type = $first_arg_array->type_params[1]; + $key_type = $first_arg_array->type_params[0]; + } else { + $inner_type = $first_arg_array->getGenericValueType(); + $key_type = $first_arg_array->getGenericKeyType(); + + if (!isset($call_args[1]) && $first_arg_array->fallback_params === null) { + $had_one = count($first_arg_array->properties) === 1; + + $new_properties = array_filter( + array_map( + static function ($keyed_type) use ($statements_source, $context) { + $prev_keyed_type = $keyed_type; + + $keyed_type = AssertionReconciler::reconcile( + new Truthy(), + $keyed_type, + '', + $statements_source, + $context->inside_loop, + [], + null, + $statements_source->getSuppressedIssues(), + ); + + return $keyed_type->setPossiblyUndefined(!$prev_keyed_type->isAlwaysTruthy()); + }, + $first_arg_array->properties, + ), + static fn($keyed_type) => !$keyed_type->isNever() + ); - return $keyed_type->setPossiblyUndefined(!$prev_keyed_type->isAlwaysTruthy()); - }, - $first_arg_array->properties, - ), - static fn($keyed_type) => !$keyed_type->isNever() - ); + if (!$new_properties) { + $out_types []= Type::getEmptyArrayAtomic(); + continue; + } - if (!$new_properties) { - return Type::getEmptyArray(); + $out_types []= new TKeyedArray( + $new_properties, + null, + $first_arg_array->fallback_params, + $first_arg_array->is_list && $had_one, + ); } - - return new Union([new TKeyedArray( - $new_properties, - null, - $first_arg_array->fallback_params, - $first_arg_array->is_list && $had_one, - )]); } - } - if (!isset($call_args[1])) { - $inner_type = AssertionReconciler::reconcile( - new Truthy(), - $inner_type, - '', - $statements_source, - $context->inside_loop, - [], - null, - $statements_source->getSuppressedIssues(), - ); - - if ($first_arg_array instanceof TKeyedArray - && $first_arg_array->is_list - && $key_type->isSingleIntLiteral() - && $key_type->getSingleIntLiteral()->value === 0 - ) { - return Type::getList( + if (!isset($call_args[1])) { + $inner_type = AssertionReconciler::reconcile( + new Truthy(), $inner_type, + '', + $statements_source, + $context->inside_loop, + [], + null, + $statements_source->getSuppressedIssues(), ); - } - if ($key_type->getLiteralStrings()) { - $key_type = $key_type->getBuilder()->addType(new TString)->freeze(); - } + if ($first_arg_array instanceof TKeyedArray + && $first_arg_array->is_list + && $key_type->isSingleIntLiteral() + && $key_type->getSingleIntLiteral()->value === 0 + ) { + $out_types []= Type::getListAtomic( + $inner_type, + ); + continue; + } - if ($key_type->getLiteralInts()) { - $key_type = $key_type->getBuilder()->addType(new TInt)->freeze(); - } + if ($inner_type->isUnionEmpty()) { + $out_types []= Type::getEmptyArrayAtomic(); + } - if ($inner_type->isUnionEmpty()) { - return Type::getEmptyArray(); - } + if ($key_type->getLiteralStrings()) { + $key_type = $key_type->getBuilder()->addType(new TString)->freeze(); + } - return new Union([ - new TArray([ + if ($key_type->getLiteralInts()) { + $key_type = $key_type->getBuilder()->addType(new TInt)->freeze(); + } + + $out_types []= new TArray([ $key_type, $inner_type, - ]), - ]); + ]); + } + } + + if ($out_types) { + return new Union([$out_types]); } if (!isset($call_args[2])) { diff --git a/src/Psalm/Type/UnionTrait.php b/src/Psalm/Type/UnionTrait.php index 1e31d1c8d70..66a43c006de 100644 --- a/src/Psalm/Type/UnionTrait.php +++ b/src/Psalm/Type/UnionTrait.php @@ -7,7 +7,6 @@ use InvalidArgumentException; use Psalm\CodeLocation; use Psalm\Codebase; -use Psalm\Internal\Type\TypeCombiner; use Psalm\Internal\TypeVisitor\CanContainObjectTypeVisitor; use Psalm\Internal\TypeVisitor\ClasslikeReplacer; use Psalm\Internal\TypeVisitor\ContainsClassLikeVisitor;