Skip to content

Commit

Permalink
Fix
Browse files Browse the repository at this point in the history
  • Loading branch information
danog committed Feb 27, 2024
1 parent 438096e commit b9741cb
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 106 deletions.
Expand Up @@ -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;
Expand Down
Expand Up @@ -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;
Expand Down Expand Up @@ -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(),
Expand All @@ -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(),
Expand Down Expand Up @@ -168,7 +155,7 @@ public static function getFunctionParams(FunctionParamsProviderEvent $event): ?a
$inner_type = Type::combineUnionTypes(
$inner_type,
$key_type,
$statements_source->getCodebase(),
$codebase,
);

continue;
Expand Down
Expand Up @@ -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])) {
Expand Down
1 change: 0 additions & 1 deletion src/Psalm/Type/UnionTrait.php
Expand Up @@ -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;
Expand Down

0 comments on commit b9741cb

Please sign in to comment.