mirror of
https://github.com/danog/psalm.git
synced 2025-01-21 21:31:13 +01:00
Improve array function list handling (#2377)
* array_column() returns a list unless the 3rd arg is passed * array_pad() return type provider * array_chunk() return type provider * array_map() preserve list types
This commit is contained in:
parent
2f02da62c1
commit
4e594e0a65
@ -31,10 +31,12 @@ class FunctionReturnTypeProvider
|
||||
{
|
||||
self::$handlers = [];
|
||||
|
||||
$this->registerClass(ReturnTypeProvider\ArrayChunkReturnTypeProvider::class);
|
||||
$this->registerClass(ReturnTypeProvider\ArrayColumnReturnTypeProvider::class);
|
||||
$this->registerClass(ReturnTypeProvider\ArrayFilterReturnTypeProvider::class);
|
||||
$this->registerClass(ReturnTypeProvider\ArrayMapReturnTypeProvider::class);
|
||||
$this->registerClass(ReturnTypeProvider\ArrayMergeReturnTypeProvider::class);
|
||||
$this->registerClass(ReturnTypeProvider\ArrayPadReturnTypeProvider::class);
|
||||
$this->registerClass(ReturnTypeProvider\ArrayPointerAdjustmentReturnTypeProvider::class);
|
||||
$this->registerClass(ReturnTypeProvider\ArrayPopReturnTypeProvider::class);
|
||||
$this->registerClass(ReturnTypeProvider\ArrayRandReturnTypeProvider::class);
|
||||
|
@ -0,0 +1,49 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Psalm\Internal\Provider\ReturnTypeProvider;
|
||||
|
||||
use function count;
|
||||
use Psalm\CodeLocation;
|
||||
use Psalm\Context;
|
||||
use Psalm\Internal\Type\ArrayType;
|
||||
use Psalm\StatementsSource;
|
||||
use Psalm\Type;
|
||||
|
||||
class ArrayChunkReturnTypeProvider implements \Psalm\Plugin\Hook\FunctionReturnTypeProviderInterface
|
||||
{
|
||||
public static function getFunctionIds(): array
|
||||
{
|
||||
return ['array_chunk'];
|
||||
}
|
||||
|
||||
public static function getFunctionReturnType(
|
||||
StatementsSource $statements_source,
|
||||
string $function_id,
|
||||
array $call_args,
|
||||
Context $context,
|
||||
CodeLocation $code_location
|
||||
) {
|
||||
if (count($call_args) >= 2
|
||||
&& ($array_arg_type = $statements_source->getNodeTypeProvider()->getType($call_args[0]->value))
|
||||
&& $array_arg_type->isSingle()
|
||||
&& $array_arg_type->hasArray()
|
||||
&& ($array_type = ArrayType::infer($array_arg_type->getTypes()['array']))
|
||||
) {
|
||||
$preserve_keys = isset($call_args[2])
|
||||
&& ($preserve_keys_arg_type = $statements_source->getNodeTypeProvider()->getType($call_args[2]->value))
|
||||
&& (string) $preserve_keys_arg_type !== 'false';
|
||||
|
||||
return new Type\Union([
|
||||
new Type\Atomic\TList(
|
||||
new Type\Union([
|
||||
$preserve_keys
|
||||
? new Type\Atomic\TNonEmptyArray([$array_type->key, $array_type->value])
|
||||
: new Type\Atomic\TNonEmptyList($array_type->value)
|
||||
])
|
||||
)
|
||||
]);
|
||||
}
|
||||
|
||||
return new Type\Union([new Type\Atomic\TList(Type::getArray())]);
|
||||
}
|
||||
}
|
@ -67,6 +67,7 @@ class ArrayColumnReturnTypeProvider implements \Psalm\Plugin\Hook\FunctionReturn
|
||||
}
|
||||
|
||||
$key_column_name = null;
|
||||
$third_arg_type = null;
|
||||
// calculate key column name
|
||||
if (isset($call_args[2])
|
||||
&& ($third_arg_type = $statements_source->node_data->getType($call_args[2]->value))
|
||||
@ -93,19 +94,10 @@ class ArrayColumnReturnTypeProvider implements \Psalm\Plugin\Hook\FunctionReturn
|
||||
}
|
||||
}
|
||||
|
||||
if ($result_element_type) {
|
||||
return new Type\Union([
|
||||
new Type\Atomic\TArray([
|
||||
$result_key_type,
|
||||
$result_element_type,
|
||||
]),
|
||||
]);
|
||||
}
|
||||
|
||||
$callmap_callables = CallMap::getCallablesFromCallMap($function_id);
|
||||
|
||||
assert($callmap_callables && $callmap_callables[0]->return_type);
|
||||
|
||||
return $callmap_callables[0]->return_type;
|
||||
return new Type\Union([
|
||||
isset($call_args[2]) && (string) $third_arg_type !== 'null'
|
||||
? new Type\Atomic\TArray([$result_key_type, $result_element_type ?? Type::getMixed()])
|
||||
: new Type\Atomic\TList($result_element_type ?? Type::getMixed())
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ use Psalm\Context;
|
||||
use Psalm\Internal\Analyzer\Statements\Expression\CallAnalyzer;
|
||||
use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
|
||||
use Psalm\Internal\Codebase\CallMap;
|
||||
use Psalm\Internal\Type\ArrayType;
|
||||
use Psalm\StatementsSource;
|
||||
use Psalm\Type;
|
||||
use function strpos;
|
||||
@ -41,16 +42,14 @@ class ArrayMapReturnTypeProvider implements \Psalm\Plugin\Hook\FunctionReturnTyp
|
||||
$array_arg = isset($call_args[1]->value) ? $call_args[1]->value : null;
|
||||
|
||||
$array_arg_atomic_type = null;
|
||||
$array_arg_type = null;
|
||||
|
||||
if ($array_arg && ($array_arg_type = $statements_source->node_data->getType($array_arg))) {
|
||||
$arg_types = $array_arg_type->getTypes();
|
||||
if ($array_arg && ($array_arg_union_type = $statements_source->node_data->getType($array_arg))) {
|
||||
$arg_types = $array_arg_union_type->getTypes();
|
||||
|
||||
if (isset($arg_types['array'])
|
||||
&& ($arg_types['array'] instanceof Type\Atomic\TArray
|
||||
|| $arg_types['array'] instanceof Type\Atomic\ObjectLike
|
||||
|| $arg_types['array'] instanceof Type\Atomic\TList)
|
||||
) {
|
||||
if (isset($arg_types['array'])) {
|
||||
$array_arg_atomic_type = $arg_types['array'];
|
||||
$array_arg_type = ArrayType::infer($array_arg_atomic_type);
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,15 +57,7 @@ class ArrayMapReturnTypeProvider implements \Psalm\Plugin\Hook\FunctionReturnTyp
|
||||
$function_call_arg = $call_args[0];
|
||||
|
||||
if (count($call_args) === 2) {
|
||||
if ($array_arg_atomic_type instanceof Type\Atomic\ObjectLike) {
|
||||
$generic_key_type = $array_arg_atomic_type->getGenericKeyType();
|
||||
} elseif ($array_arg_atomic_type instanceof Type\Atomic\TList) {
|
||||
$generic_key_type = Type::getInt();
|
||||
} else {
|
||||
$generic_key_type = $array_arg_atomic_type
|
||||
? clone $array_arg_atomic_type->type_params[0]
|
||||
: Type::getArrayKey();
|
||||
}
|
||||
$generic_key_type = $array_arg_type->key ?? Type::getArrayKey();
|
||||
} else {
|
||||
$generic_key_type = Type::getInt();
|
||||
}
|
||||
@ -84,19 +75,20 @@ class ArrayMapReturnTypeProvider implements \Psalm\Plugin\Hook\FunctionReturnTyp
|
||||
$inner_type = clone $closure_return_type;
|
||||
|
||||
if ($array_arg_atomic_type instanceof Type\Atomic\ObjectLike && count($call_args) === 2) {
|
||||
return new Type\Union([
|
||||
new Type\Atomic\ObjectLike(
|
||||
array_map(
|
||||
/**
|
||||
* @return Type\Union
|
||||
*/
|
||||
function (Type\Union $_) use ($inner_type) {
|
||||
return clone $inner_type;
|
||||
},
|
||||
$array_arg_atomic_type->properties
|
||||
)
|
||||
),
|
||||
]);
|
||||
$atomic_type = new Type\Atomic\ObjectLike(
|
||||
array_map(
|
||||
/**
|
||||
* @return Type\Union
|
||||
*/
|
||||
function (Type\Union $_) use ($inner_type) {
|
||||
return clone $inner_type;
|
||||
},
|
||||
$array_arg_atomic_type->properties
|
||||
)
|
||||
);
|
||||
$atomic_type->is_list = $array_arg_atomic_type->is_list;
|
||||
|
||||
return new Type\Union([$atomic_type]);
|
||||
}
|
||||
|
||||
if ($array_arg_atomic_type instanceof Type\Atomic\TList) {
|
||||
@ -258,31 +250,41 @@ class ArrayMapReturnTypeProvider implements \Psalm\Plugin\Hook\FunctionReturnTyp
|
||||
|
||||
if ($mapping_return_type) {
|
||||
if ($array_arg_atomic_type instanceof Type\Atomic\ObjectLike && count($call_args) === 2) {
|
||||
return new Type\Union([
|
||||
new Type\Atomic\ObjectLike(
|
||||
array_map(
|
||||
/**
|
||||
* @return Type\Union
|
||||
*/
|
||||
function (Type\Union $_) use ($mapping_return_type) {
|
||||
return clone $mapping_return_type;
|
||||
},
|
||||
$array_arg_atomic_type->properties
|
||||
)
|
||||
),
|
||||
]);
|
||||
$atomic_type = new Type\Atomic\ObjectLike(
|
||||
array_map(
|
||||
/**
|
||||
* @return Type\Union
|
||||
*/
|
||||
function (Type\Union $_) use ($mapping_return_type) {
|
||||
return clone $mapping_return_type;
|
||||
},
|
||||
$array_arg_atomic_type->properties
|
||||
)
|
||||
);
|
||||
$atomic_type->is_list = $array_arg_atomic_type->is_list;
|
||||
|
||||
return new Type\Union([$atomic_type]);
|
||||
}
|
||||
|
||||
return new Type\Union([
|
||||
new Type\Atomic\TArray([
|
||||
$generic_key_type,
|
||||
$mapping_return_type,
|
||||
]),
|
||||
count($call_args) === 2 && !($array_arg_type->is_list ?? false)
|
||||
? new Type\Atomic\TArray([
|
||||
$generic_key_type,
|
||||
$mapping_return_type,
|
||||
])
|
||||
: new Type\Atomic\TList($mapping_return_type)
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Type::getArray();
|
||||
return count($call_args) === 2 && !($array_arg_type->is_list ?? false)
|
||||
? new Type\Union([
|
||||
new Type\Atomic\TArray([
|
||||
$array_arg_type->key ?? Type::getArrayKey(),
|
||||
Type::getMixed(),
|
||||
])
|
||||
])
|
||||
: Type::getList();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,61 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Psalm\Internal\Provider\ReturnTypeProvider;
|
||||
|
||||
use function count;
|
||||
use Psalm\CodeLocation;
|
||||
use Psalm\Context;
|
||||
use Psalm\Internal\Type\ArrayType;
|
||||
use Psalm\StatementsSource;
|
||||
use Psalm\Type;
|
||||
|
||||
class ArrayPadReturnTypeProvider implements \Psalm\Plugin\Hook\FunctionReturnTypeProviderInterface
|
||||
{
|
||||
public static function getFunctionIds(): array
|
||||
{
|
||||
return ['array_pad'];
|
||||
}
|
||||
|
||||
public static function getFunctionReturnType(
|
||||
StatementsSource $statements_source,
|
||||
string $function_id,
|
||||
array $call_args,
|
||||
Context $context,
|
||||
CodeLocation $code_location
|
||||
) {
|
||||
$type_provider = $statements_source->getNodeTypeProvider();
|
||||
|
||||
if (count($call_args) >= 3
|
||||
&& ($array_arg_type = $type_provider->getType($call_args[0]->value))
|
||||
&& ($size_arg_type = $type_provider->getType($call_args[1]->value))
|
||||
&& ($value_arg_type = $type_provider->getType($call_args[2]->value))
|
||||
&& $array_arg_type->isSingle()
|
||||
&& $array_arg_type->hasArray()
|
||||
&& ($array_type = ArrayType::infer($array_arg_type->getTypes()['array']))
|
||||
) {
|
||||
$codebase = $statements_source->getCodebase();
|
||||
$key_type = Type::combineUnionTypes($array_type->key, Type::getInt(), $codebase);
|
||||
$value_type = Type::combineUnionTypes($array_type->value, $value_arg_type, $codebase);
|
||||
$can_return_empty = (
|
||||
!$size_arg_type->isSingleIntLiteral()
|
||||
|| $size_arg_type->getSingleIntLiteral()->value === 0
|
||||
);
|
||||
|
||||
return new Type\Union([
|
||||
$array_type->is_list
|
||||
? (
|
||||
$can_return_empty
|
||||
? new Type\Atomic\TList($value_type)
|
||||
: new Type\Atomic\TNonEmptyList($value_type)
|
||||
)
|
||||
: (
|
||||
$can_return_empty
|
||||
? new Type\Atomic\TArray([$key_type, $value_type])
|
||||
: new Type\Atomic\TNonEmptyArray([$key_type, $value_type])
|
||||
)
|
||||
]);
|
||||
}
|
||||
|
||||
return Type::getArray();
|
||||
}
|
||||
}
|
@ -152,18 +152,6 @@ function array_change_key_case(array $arr, int $case = CASE_LOWER)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-template T
|
||||
*
|
||||
* @param array<array-key, T> $arr
|
||||
*
|
||||
* @return array<int, array<array-key, T>>
|
||||
* @psalm-pure
|
||||
*/
|
||||
function array_chunk(array $arr, int $size, bool $preserve_keys = false)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @psalm-template TKey as array-key
|
||||
*
|
||||
|
59
src/Psalm/Internal/Type/ArrayType.php
Normal file
59
src/Psalm/Internal/Type/ArrayType.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Psalm\Internal\Type;
|
||||
|
||||
use Psalm\Type;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class ArrayType
|
||||
{
|
||||
/** @var Type\Union */
|
||||
public $key;
|
||||
|
||||
/** @var Type\Union */
|
||||
public $value;
|
||||
|
||||
/** @var bool */
|
||||
public $is_list;
|
||||
|
||||
public function __construct(Type\Union $key, Type\Union $value, bool $is_list)
|
||||
{
|
||||
$this->key = $key;
|
||||
$this->value = $value;
|
||||
$this->is_list = $is_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return static|null
|
||||
*/
|
||||
public static function infer(Type\Atomic $type): ?self
|
||||
{
|
||||
if ($type instanceof Type\Atomic\ObjectLike) {
|
||||
return new static(
|
||||
$type->getGenericKeyType(),
|
||||
$type->getGenericValueType(),
|
||||
$type->is_list
|
||||
);
|
||||
}
|
||||
|
||||
if ($type instanceof Type\Atomic\TList) {
|
||||
return new static(
|
||||
Type::getInt(),
|
||||
$type->type_param,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
if ($type instanceof Type\Atomic\TArray) {
|
||||
return new static(
|
||||
$type->type_params[0],
|
||||
$type->type_params[1],
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -819,6 +819,16 @@ class FunctionCallTest extends TestCase
|
||||
'$foo' => 'float|int',
|
||||
],
|
||||
],
|
||||
'arrayMapWithArrayAndCallable' => [
|
||||
'<?php
|
||||
/**
|
||||
* @psalm-return array<array-key, int>
|
||||
*/
|
||||
function foo(array $v): array {
|
||||
$r = array_map("intval", $v);
|
||||
return $r;
|
||||
}',
|
||||
],
|
||||
'arrayMapObjectLikeAndCallable' => [
|
||||
'<?php
|
||||
/**
|
||||
@ -830,6 +840,18 @@ class FunctionCallTest extends TestCase
|
||||
return $r;
|
||||
}',
|
||||
],
|
||||
'arrayMapObjectLikeListAndCallable' => [
|
||||
'<?php
|
||||
/** @param list<int> $list */
|
||||
function takesList(array $list): void {}
|
||||
|
||||
takesList(
|
||||
array_map(
|
||||
"intval",
|
||||
["1", "2", "3"]
|
||||
)
|
||||
);',
|
||||
],
|
||||
'arrayMapObjectLikeAndClosure' => [
|
||||
'<?php
|
||||
/**
|
||||
@ -846,6 +868,50 @@ class FunctionCallTest extends TestCase
|
||||
'MixedTypeCoercion',
|
||||
],
|
||||
],
|
||||
'arrayMapObjectLikeListAndClosure' => [
|
||||
'<?php
|
||||
/** @param list<string> $list */
|
||||
function takesList(array $list): void {}
|
||||
|
||||
takesList(
|
||||
array_map(
|
||||
function (string $str): string { return $str . "x"; },
|
||||
["foo", "bar", "baz"]
|
||||
)
|
||||
);',
|
||||
],
|
||||
'arrayMapUntypedCallable' => [
|
||||
'<?php
|
||||
/**
|
||||
* @var callable $callable
|
||||
* @var array<string, int> $array
|
||||
*/
|
||||
$a = array_map($callable, $array);
|
||||
|
||||
/**
|
||||
* @var callable $callable
|
||||
* @var array<string, int> $array
|
||||
*/
|
||||
$b = array_map($callable, $array, $array);
|
||||
|
||||
/**
|
||||
* @var callable $callable
|
||||
* @var list<string> $list
|
||||
*/
|
||||
$c = array_map($callable, $list);
|
||||
|
||||
/**
|
||||
* @var callable $callable
|
||||
* @var list<string> $list
|
||||
*/
|
||||
$d = array_map($callable, $list, $list);',
|
||||
'assertions' => [
|
||||
'$a' => 'array<string, mixed>',
|
||||
'$b' => 'list<mixed>',
|
||||
'$c' => 'list<mixed>',
|
||||
'$d' => 'list<mixed>',
|
||||
],
|
||||
],
|
||||
'arrayFilterGoodArgs' => [
|
||||
'<?php
|
||||
function fooFoo(int $i) : bool {
|
||||
@ -1123,19 +1189,23 @@ class FunctionCallTest extends TestCase
|
||||
$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);
|
||||
$f = array_column(makeMixedArray(), 0, "k");
|
||||
$g = array_column(makeMixedArray(), 0, null);
|
||||
$h = array_column(makeGenericArray(), 0);
|
||||
$i = array_column(makeShapeArray(), 0);
|
||||
$j = array_column(makeUnionArray(), 0);
|
||||
',
|
||||
'assertions' => [
|
||||
'$a' => 'array<array-key, int>',
|
||||
'$b' => 'array<array-key, int>',
|
||||
'$a' => 'list<int>',
|
||||
'$b' => 'list<int>',
|
||||
'$c' => 'array<string, int>',
|
||||
'$d' => 'array<array-key, mixed>',
|
||||
'$e' => 'array<array-key, mixed>',
|
||||
'$d' => 'list<mixed>',
|
||||
'$e' => 'list<mixed>',
|
||||
'$f' => 'array<array-key, mixed>',
|
||||
'$g' => 'array<array-key, string>',
|
||||
'$h' => 'array<array-key, mixed>',
|
||||
'$g' => 'list<mixed>',
|
||||
'$h' => 'list<mixed>',
|
||||
'$i' => 'list<string>',
|
||||
'$j' => 'list<mixed>',
|
||||
],
|
||||
],
|
||||
'strtrWithPossiblyFalseFirstArg' => [
|
||||
@ -2068,6 +2138,151 @@ class FunctionCallTest extends TestCase
|
||||
$mysqli = mysqli_init();
|
||||
mysqli_real_connect($mysqli, null, \'test\', null);',
|
||||
],
|
||||
'arrayPad' => [
|
||||
'<?php
|
||||
$a = array_pad(["foo" => 1, "bar" => 2], 10, 123);
|
||||
$b = array_pad(["a", "b", "c"], 10, "x");
|
||||
/** @var list<int> $list */
|
||||
$c = array_pad($list, 10, 0);
|
||||
/** @var array<string, string> $array */
|
||||
$d = array_pad($array, 10, "");',
|
||||
'assertions' => [
|
||||
'$a' => 'non-empty-array<int|string, int>',
|
||||
'$b' => 'non-empty-list<string>',
|
||||
'$c' => 'non-empty-list<int>',
|
||||
'$d' => 'non-empty-array<int|string, string>',
|
||||
],
|
||||
],
|
||||
'arrayPadDynamicSize' => [
|
||||
'<?php
|
||||
function getSize(): int { return random_int(1, 10); }
|
||||
|
||||
$a = array_pad(["foo" => 1, "bar" => 2], getSize(), 123);
|
||||
$b = array_pad(["a", "b", "c"], getSize(), "x");
|
||||
/** @var list<int> $list */
|
||||
$c = array_pad($list, getSize(), 0);
|
||||
/** @var array<string, string> $array */
|
||||
$d = array_pad($array, getSize(), "");',
|
||||
'assertions' => [
|
||||
'$a' => 'array<int|string, int>',
|
||||
'$b' => 'list<string>',
|
||||
'$c' => 'list<int>',
|
||||
'$d' => 'array<int|string, string>',
|
||||
],
|
||||
],
|
||||
'arrayPadZeroSize' => [
|
||||
'<?php
|
||||
/** @var array $arr */
|
||||
$result = array_pad($arr, 0, null);',
|
||||
'assertions' => [
|
||||
'$result' => 'array<array-key, mixed|null>',
|
||||
],
|
||||
],
|
||||
'arrayPadTypeCombination' => [
|
||||
'<?php
|
||||
$a = array_pad(["foo" => 1, "bar" => "two"], 5, false);
|
||||
$b = array_pad(["a", 2, 3.14], 5, null);
|
||||
/** @var list<string|bool> $list */
|
||||
$c = array_pad($list, 5, 0);
|
||||
/** @var array<string, string> $array */
|
||||
$d = array_pad($array, 5, null);',
|
||||
'assertions' => [
|
||||
'$a' => 'non-empty-array<int|string, false|int|string>',
|
||||
'$b' => 'non-empty-list<float|int|null|string>',
|
||||
'$c' => 'non-empty-list<bool|int|string>',
|
||||
'$d' => 'non-empty-array<int|string, null|string>',
|
||||
],
|
||||
],
|
||||
'arrayPadMixed' => [
|
||||
'<?php
|
||||
/** @var array{foo: mixed, bar: mixed} $arr */
|
||||
$a = array_pad($arr, 5, null);
|
||||
/** @var mixed $mixed */
|
||||
$b = array_pad([$mixed, $mixed], 5, null);
|
||||
/** @var list $list */
|
||||
$c = array_pad($list, 5, null);
|
||||
/** @var mixed[] $array */
|
||||
$d = array_pad($array, 5, null);',
|
||||
'assertions' => [
|
||||
'$a' => 'non-empty-array<int|string, mixed|null>',
|
||||
'$b' => 'non-empty-list<mixed|null>',
|
||||
'$c' => 'non-empty-list<mixed|null>',
|
||||
'$d' => 'non-empty-array<array-key, mixed|null>',
|
||||
],
|
||||
],
|
||||
'arrayPadFallback' => [
|
||||
'<?php
|
||||
/**
|
||||
* @var mixed $mixed
|
||||
* @psalm-suppress MixedArgument
|
||||
*/
|
||||
$result = array_pad($mixed, $mixed, $mixed);',
|
||||
'assertions' => [
|
||||
'$result' => 'array<array-key, mixed>',
|
||||
],
|
||||
],
|
||||
'arrayChunk' => [
|
||||
'<?php
|
||||
/** @var array{a: int, b: int, c: int, d: int} $arr */
|
||||
$a = array_chunk($arr, 2);
|
||||
/** @var list<string> $list */
|
||||
$b = array_chunk($list, 2);
|
||||
/** @var array<string, float> $arr */
|
||||
$c = array_chunk($arr, 2);
|
||||
',
|
||||
'assertions' => [
|
||||
'$a' => 'list<non-empty-list<int>>',
|
||||
'$b' => 'list<non-empty-list<string>>',
|
||||
'$c' => 'list<non-empty-list<float>>',
|
||||
],
|
||||
],
|
||||
'arrayChunkPreservedKeys' => [
|
||||
'<?php
|
||||
/** @var array{a: int, b: int, c: int, d: int} $arr */
|
||||
$a = array_chunk($arr, 2, true);
|
||||
/** @var list<string> $list */
|
||||
$b = array_chunk($list, 2, true);
|
||||
/** @var array<string, float> $arr */
|
||||
$c = array_chunk($arr, 2, true);',
|
||||
'assertions' => [
|
||||
'$a' => 'list<non-empty-array<string, int>>',
|
||||
'$b' => 'list<non-empty-array<int, string>>',
|
||||
'$c' => 'list<non-empty-array<string, float>>',
|
||||
],
|
||||
],
|
||||
'arrayChunkPreservedKeysExplicitFalse' => [
|
||||
'<?php
|
||||
/** @var array<string, string> $arr */
|
||||
$result = array_chunk($arr, 2, false);',
|
||||
'assertions' => [
|
||||
'$result' => 'list<non-empty-list<string>>',
|
||||
],
|
||||
],
|
||||
'arrayChunkMixed' => [
|
||||
'<?php
|
||||
/** @var array{a: mixed, b: mixed, c: mixed} $arr */
|
||||
$a = array_chunk($arr, 2);
|
||||
/** @var list<mixed> $list */
|
||||
$b = array_chunk($list, 2);
|
||||
/** @var mixed[] $arr */
|
||||
$c = array_chunk($arr, 2);',
|
||||
'assertions' => [
|
||||
'$a' => 'list<non-empty-list<mixed>>',
|
||||
'$b' => 'list<non-empty-list<mixed>>',
|
||||
'$c' => 'list<non-empty-list<mixed>>',
|
||||
],
|
||||
],
|
||||
'arrayChunkFallback' => [
|
||||
'<?php
|
||||
/**
|
||||
* @var mixed $mixed
|
||||
* @psalm-suppress MixedArgument
|
||||
*/
|
||||
$result = array_chunk($mixed, $mixed, $mixed);',
|
||||
'assertions' => [
|
||||
'$result' => 'list<array<array-key, mixed>>',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user