mirror of
https://github.com/danog/psalm.git
synced 2025-01-21 21:31:13 +01:00
Improve accuracy of array_filter
This commit is contained in:
parent
8d7fe83e2a
commit
0b209864fe
@ -64,6 +64,52 @@ class ArrayFilterReturnTypeProvider implements \Psalm\Plugin\Hook\FunctionReturn
|
||||
} else {
|
||||
$inner_type = $first_arg_array->getGenericValueType();
|
||||
$key_type = $first_arg_array->getGenericKeyType();
|
||||
|
||||
if (!isset($call_args[1]) && !$first_arg_array->previous_value_type) {
|
||||
$had_one = count($first_arg_array->properties) === 1;
|
||||
|
||||
$first_arg_array = clone $first_arg_array;
|
||||
|
||||
$new_properties = \array_filter(
|
||||
array_map(
|
||||
function ($keyed_type) use ($statements_source, $context) {
|
||||
$prev_keyed_type = $keyed_type;
|
||||
|
||||
$keyed_type = \Psalm\Internal\Type\AssertionReconciler::reconcile(
|
||||
'!falsy',
|
||||
clone $keyed_type,
|
||||
'',
|
||||
$statements_source,
|
||||
$context->inside_loop,
|
||||
[],
|
||||
null,
|
||||
$statements_source->getSuppressedIssues()
|
||||
);
|
||||
|
||||
$keyed_type->possibly_undefined = ($prev_keyed_type->hasInt()
|
||||
&& !$prev_keyed_type->hasLiteralInt())
|
||||
|| $prev_keyed_type->hasFloat()
|
||||
|| $prev_keyed_type->getId() !== $keyed_type->getId();
|
||||
|
||||
return $keyed_type;
|
||||
},
|
||||
$first_arg_array->properties
|
||||
),
|
||||
function ($keyed_type) {
|
||||
return !$keyed_type->isEmpty();
|
||||
}
|
||||
);
|
||||
|
||||
if (!$new_properties) {
|
||||
return Type::getEmptyArray();
|
||||
}
|
||||
|
||||
$first_arg_array->properties = $new_properties;
|
||||
|
||||
$first_arg_array->is_list = $first_arg_array->is_list && $had_one;
|
||||
|
||||
return new Type\Union([$first_arg_array]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($call_args[1])) {
|
||||
@ -97,7 +143,20 @@ class ArrayFilterReturnTypeProvider implements \Psalm\Plugin\Hook\FunctionReturn
|
||||
if ($key_type->getLiteralInts()) {
|
||||
$key_type->addType(new Type\Atomic\TInt);
|
||||
}
|
||||
} elseif (!isset($call_args[2])) {
|
||||
|
||||
if (!$inner_type->getAtomicTypes()) {
|
||||
return Type::getEmptyArray();
|
||||
}
|
||||
|
||||
return new Type\Union([
|
||||
new Type\Atomic\TArray([
|
||||
$key_type,
|
||||
$inner_type,
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
if (!isset($call_args[2])) {
|
||||
$function_call_arg = $call_args[1];
|
||||
|
||||
if ($function_call_arg->value instanceof PhpParser\Node\Scalar\String_
|
||||
|
@ -16,15 +16,15 @@ class ArrayFunctionCallTest extends TestCase
|
||||
return [
|
||||
'arrayFilter' => [
|
||||
'<?php
|
||||
$d = array_filter(["a" => 5, "b" => 12, "c" => null]);
|
||||
$d = array_filter(["a" => rand(0, 10), "b" => rand(0, 10), "c" => null]);
|
||||
$e = array_filter(
|
||||
["a" => 5, "b" => 12, "c" => null],
|
||||
["a" => rand(0, 10), "b" => rand(0, 10), "c" => null],
|
||||
function(?int $i): bool {
|
||||
return true;
|
||||
}
|
||||
);',
|
||||
'assertions' => [
|
||||
'$d' => 'array<string, int>',
|
||||
'$d' => 'array{a?: int, b?: int}',
|
||||
'$e' => 'array<string, int|null>',
|
||||
],
|
||||
],
|
||||
@ -2010,7 +2010,7 @@ class ArrayFunctionCallTest extends TestCase
|
||||
function ints(array $ints) : void {}
|
||||
$brr = array_filter([2,3,0,4,5]);
|
||||
ints($brr);',
|
||||
'error_message' => 'ArgumentTypeCoercion - src' . DIRECTORY_SEPARATOR . 'somefile.php:5:26 - Argument 1 of ints expects list<int>, parent type array<int, int(2)|int(3)|int(4)|int(5)> provided',
|
||||
'error_message' => 'InvalidArgument',
|
||||
],
|
||||
'usortOneParamInvalid' => [
|
||||
'<?php
|
||||
|
Loading…
x
Reference in New Issue
Block a user