mirror of
https://github.com/danog/psalm.git
synced 2025-01-21 21:31:13 +01:00
array_column return type inference (#787)
* Exception message typo fixed * array_column return type inference It takes a safe approach and only tries to be smart when array row type is definite single shape * cs fix
This commit is contained in:
parent
c31d963918
commit
6ecea35837
@ -195,6 +195,74 @@ class FunctionChecker extends FunctionLikeChecker
|
||||
|
||||
break;
|
||||
|
||||
case 'array_column':
|
||||
$row_shape = null;
|
||||
// calculate row shape
|
||||
if (isset($call_args[0]->value->inferredType)
|
||||
&& $call_args[0]->value->inferredType->isSingle()
|
||||
&& $call_args[0]->value->inferredType->hasArray()
|
||||
) {
|
||||
$input_array = $call_args[0]->value->inferredType->getTypes()['array'];
|
||||
if ($input_array instanceof Type\Atomic\ObjectLike) {
|
||||
$row_type = $input_array->getGenericArrayType()->type_params[1];
|
||||
if ($row_type->isSingle() && $row_type->hasArray()) {
|
||||
$row_shape = $row_type->getTypes()['array'];
|
||||
}
|
||||
} elseif ($input_array instanceof Type\Atomic\TArray) {
|
||||
$row_type = $input_array->type_params[1];
|
||||
if ($row_type->isSingle() && $row_type->hasArray()) {
|
||||
$row_shape = $row_type->getTypes()['array'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$value_column_name = null;
|
||||
// calculate value column name
|
||||
if (isset($call_args[1]->value->inferredType)) {
|
||||
$value_column_name_arg= $call_args[1]->value->inferredType;
|
||||
if ($value_column_name_arg->isSingleIntLiteral()) {
|
||||
$value_column_name = $value_column_name_arg->getSingleIntLiteral();
|
||||
} elseif ($value_column_name_arg->isSingleStringLiteral()) {
|
||||
$value_column_name = $value_column_name_arg->getSingleStringLiteral();
|
||||
}
|
||||
}
|
||||
|
||||
$key_column_name = null;
|
||||
// calculate key column name
|
||||
if (isset($call_args[2]->value->inferredType)) {
|
||||
$key_column_name_arg = $call_args[2]->value->inferredType;
|
||||
if ($key_column_name_arg->isSingleIntLiteral()) {
|
||||
$key_column_name = $key_column_name_arg->getSingleIntLiteral();
|
||||
} elseif ($key_column_name_arg->isSingleStringLiteral()) {
|
||||
$key_column_name = $key_column_name_arg->getSingleStringLiteral();
|
||||
}
|
||||
}
|
||||
|
||||
$result_key_type = Type::getMixed();
|
||||
$result_element_type = null;
|
||||
// calculate results
|
||||
if ($row_shape instanceof Type\Atomic\ObjectLike) {
|
||||
if ((null !== $value_column_name) && isset($row_shape->properties[$value_column_name])) {
|
||||
$result_element_type = $row_shape->properties[$value_column_name];
|
||||
} else {
|
||||
$result_element_type = Type::getMixed();
|
||||
}
|
||||
|
||||
if ((null !== $key_column_name) && isset($row_shape->properties[$key_column_name])) {
|
||||
$result_key_type = $row_shape->properties[$key_column_name];
|
||||
}
|
||||
}
|
||||
|
||||
if ($result_element_type) {
|
||||
return new Type\Union([
|
||||
new Type\Atomic\TArray([
|
||||
$result_key_type,
|
||||
$result_element_type
|
||||
])
|
||||
]);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'abs':
|
||||
if (isset($call_args[0]->value)) {
|
||||
$first_arg = $call_args[0]->value;
|
||||
|
@ -963,7 +963,7 @@ class Union
|
||||
public function getSingleIntLiteral()
|
||||
{
|
||||
if (count($this->types) !== 1 || count($this->literal_int_types) !== 1) {
|
||||
throw new \InvalidArgumentException("Not a string literal");
|
||||
throw new \InvalidArgumentException("Not an int literal");
|
||||
}
|
||||
|
||||
return reset($this->literal_int_types)->value;
|
||||
|
@ -643,6 +643,35 @@ class FunctionCallTest extends TestCase
|
||||
return array_filter(iterator_to_array(generator()), $filter);
|
||||
}'
|
||||
],
|
||||
'arrayColumnInference' => [
|
||||
'<?php
|
||||
function makeMixedArray(): array { return []; }
|
||||
/** @return array<array<int,bool>> */
|
||||
function makeGenericArray(): array { return []; }
|
||||
/** @return array<array{0:string}> */
|
||||
function makeShapeArray(): array { return []; }
|
||||
/** @return array<array{0:string}|int> */
|
||||
function makeUnionArray(): array { return []; }
|
||||
$a = array_column([[1], [2], [3]], 0);
|
||||
$b = array_column([["a" => 1], ["a" => 2], ["a" => 3]], "a");
|
||||
$c = array_column([["k" => "a", "v" => 1], ["k" => "b", "v" => 2]], "v", "k");
|
||||
$d = array_column([], 0);
|
||||
$e = array_column(makeMixedArray(), 0);
|
||||
$f = array_column(makeGenericArray(), 0);
|
||||
$g = array_column(makeShapeArray(), 0);
|
||||
$h = array_column(makeUnionArray(), 0);
|
||||
',
|
||||
'assertions' => [
|
||||
'$a' => 'array<mixed, int>',
|
||||
'$b' => 'array<mixed, int>',
|
||||
'$c' => 'array<string, int>',
|
||||
'$d' => 'array<mixed, mixed>',
|
||||
'$e' => 'array<mixed, mixed>',
|
||||
'$f' => 'array<mixed, mixed>',
|
||||
'$g' => 'array<mixed, string>',
|
||||
'$h' => 'array<mixed, mixed>',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user