1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-22 05:41:20 +01:00

Improve accuracy of array_filter

This commit is contained in:
Brown 2020-09-14 13:31:53 -04:00
parent 8d7fe83e2a
commit 0b209864fe
2 changed files with 64 additions and 5 deletions

View File

@ -64,6 +64,52 @@ class ArrayFilterReturnTypeProvider implements \Psalm\Plugin\Hook\FunctionReturn
} else { } else {
$inner_type = $first_arg_array->getGenericValueType(); $inner_type = $first_arg_array->getGenericValueType();
$key_type = $first_arg_array->getGenericKeyType(); $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])) { if (!isset($call_args[1])) {
@ -97,7 +143,20 @@ class ArrayFilterReturnTypeProvider implements \Psalm\Plugin\Hook\FunctionReturn
if ($key_type->getLiteralInts()) { if ($key_type->getLiteralInts()) {
$key_type->addType(new Type\Atomic\TInt); $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]; $function_call_arg = $call_args[1];
if ($function_call_arg->value instanceof PhpParser\Node\Scalar\String_ if ($function_call_arg->value instanceof PhpParser\Node\Scalar\String_

View File

@ -16,15 +16,15 @@ class ArrayFunctionCallTest extends TestCase
return [ return [
'arrayFilter' => [ 'arrayFilter' => [
'<?php '<?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( $e = array_filter(
["a" => 5, "b" => 12, "c" => null], ["a" => rand(0, 10), "b" => rand(0, 10), "c" => null],
function(?int $i): bool { function(?int $i): bool {
return true; return true;
} }
);', );',
'assertions' => [ 'assertions' => [
'$d' => 'array<string, int>', '$d' => 'array{a?: int, b?: int}',
'$e' => 'array<string, int|null>', '$e' => 'array<string, int|null>',
], ],
], ],
@ -2010,7 +2010,7 @@ class ArrayFunctionCallTest extends TestCase
function ints(array $ints) : void {} function ints(array $ints) : void {}
$brr = array_filter([2,3,0,4,5]); $brr = array_filter([2,3,0,4,5]);
ints($brr);', 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' => [ 'usortOneParamInvalid' => [
'<?php '<?php