mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
Merge pull request #9794 from robchett/remove_TCallableArray_and_TCallableList
Improved type of callable-array
This commit is contained in:
commit
24168f6c58
@ -12,6 +12,7 @@
|
||||
- [BC] The only optional boolean parameter of `TKeyedArray::getGenericArrayType` was removed, and was replaced with a string parameter with a different meaning.
|
||||
|
||||
- [BC] The `TDependentListKey` type was removed and replaced with an optional property of the `TIntRange` type.
|
||||
- [BC] `TCallableArray` and `TCallableList` removed and replaced with `TCallableKeyedArray`.
|
||||
|
||||
- [BC] Value of constant `Psalm\Type\TaintKindGroup::ALL_INPUT` changed to reflect new `TaintKind::INPUT_SLEEP` and `TaintKind::INPUT_XPATH` have been added. Accordingly, default values for `$taint` parameters of `Psalm\Codebase::addTaintSource()` and `Psalm\Codebase::addTaintSink()` have been changed as well.
|
||||
|
||||
|
@ -183,8 +183,6 @@ $a = [];
|
||||
foreach (range(1,1) as $_) $a[(string)rand(0,1)] = rand(0,1); // array<string,int>
|
||||
```
|
||||
|
||||
`TCallableArray` - denotes an array that is _also_ `callable`.
|
||||
|
||||
`TCallableKeyedArray` - denotes an object-like array that is _also_ `callable`.
|
||||
|
||||
`TClassStringMap` - Represents an array where the type of each value is a function of its string key value
|
||||
|
@ -39,7 +39,6 @@ use Psalm\Storage\MethodStorage;
|
||||
use Psalm\Type;
|
||||
use Psalm\Type\Atomic\TArray;
|
||||
use Psalm\Type\Atomic\TCallable;
|
||||
use Psalm\Type\Atomic\TCallableArray;
|
||||
use Psalm\Type\Atomic\TCallableKeyedArray;
|
||||
use Psalm\Type\Atomic\TClosure;
|
||||
use Psalm\Type\Atomic\TKeyedArray;
|
||||
@ -1528,9 +1527,7 @@ class ArgumentsAnalyzer
|
||||
|
||||
foreach ($arg_value_type->getAtomicTypes() as $atomic_arg_type) {
|
||||
$packed_var_definite_args_tmp = [];
|
||||
if ($atomic_arg_type instanceof TCallableArray ||
|
||||
$atomic_arg_type instanceof TCallableKeyedArray
|
||||
) {
|
||||
if ($atomic_arg_type instanceof TCallableKeyedArray) {
|
||||
$packed_var_definite_args_tmp[] = 2;
|
||||
} elseif ($atomic_arg_type instanceof TKeyedArray) {
|
||||
if ($atomic_arg_type->fallback_params !== null) {
|
||||
|
@ -25,7 +25,6 @@ use Psalm\Storage\FunctionLikeStorage;
|
||||
use Psalm\Type;
|
||||
use Psalm\Type\Atomic\TArray;
|
||||
use Psalm\Type\Atomic\TCallable;
|
||||
use Psalm\Type\Atomic\TCallableArray;
|
||||
use Psalm\Type\Atomic\TCallableKeyedArray;
|
||||
use Psalm\Type\Atomic\TClassString;
|
||||
use Psalm\Type\Atomic\TClosure;
|
||||
@ -358,9 +357,7 @@ class FunctionCallReturnTypeFetcher
|
||||
|
||||
if (count($atomic_types) === 1) {
|
||||
if (isset($atomic_types['array'])) {
|
||||
if ($atomic_types['array'] instanceof TCallableArray
|
||||
|| $atomic_types['array'] instanceof TCallableKeyedArray
|
||||
) {
|
||||
if ($atomic_types['array'] instanceof TCallableKeyedArray) {
|
||||
return Type::getInt(false, 2);
|
||||
}
|
||||
|
||||
|
@ -292,8 +292,7 @@ final class HighOrderFunctionArgHandler
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($a instanceof Type\Atomic\TCallableArray ||
|
||||
$a instanceof Type\Atomic\TCallableString ||
|
||||
if ($a instanceof Type\Atomic\TCallableString ||
|
||||
$a instanceof Type\Atomic\TCallableKeyedArray
|
||||
) {
|
||||
return false;
|
||||
|
@ -18,7 +18,6 @@ use Psalm\Type;
|
||||
use Psalm\Type\Atomic;
|
||||
use Psalm\Type\Atomic\TArray;
|
||||
use Psalm\Type\Atomic\TCallable;
|
||||
use Psalm\Type\Atomic\TCallableArray;
|
||||
use Psalm\Type\Atomic\TClassString;
|
||||
use Psalm\Type\Atomic\TClosure;
|
||||
use Psalm\Type\Atomic\TKeyedArray;
|
||||
@ -188,15 +187,6 @@ class CallableTypeComparator
|
||||
if (!$input_type_part->type_params[1]->hasString()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$input_type_part instanceof TCallableArray) {
|
||||
if ($atomic_comparison_result) {
|
||||
$atomic_comparison_result->type_coerced_from_mixed = true;
|
||||
$atomic_comparison_result->type_coerced = true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
} elseif ($input_type_part instanceof TKeyedArray) {
|
||||
$method_id = self::getCallableMethodIdFromTKeyedArray($input_type_part);
|
||||
|
||||
|
@ -34,7 +34,6 @@ use Psalm\Type\Atomic\TArray;
|
||||
use Psalm\Type\Atomic\TArrayKey;
|
||||
use Psalm\Type\Atomic\TBool;
|
||||
use Psalm\Type\Atomic\TCallable;
|
||||
use Psalm\Type\Atomic\TCallableArray;
|
||||
use Psalm\Type\Atomic\TCallableKeyedArray;
|
||||
use Psalm\Type\Atomic\TCallableObject;
|
||||
use Psalm\Type\Atomic\TCallableString;
|
||||
@ -2305,7 +2304,7 @@ class SimpleAssertionReconciler extends Reconciler
|
||||
} elseif ($type instanceof TCallable) {
|
||||
$array_types[] = new TCallableKeyedArray([
|
||||
new Union([new TClassString, new TObject]),
|
||||
Type::getString(),
|
||||
Type::getNonEmptyString(),
|
||||
]);
|
||||
|
||||
$redundant = false;
|
||||
@ -2429,7 +2428,7 @@ class SimpleAssertionReconciler extends Reconciler
|
||||
} elseif ($type instanceof TCallable) {
|
||||
$array_types[] = new TCallableKeyedArray([
|
||||
new Union([new TClassString, new TObject]),
|
||||
Type::getString(),
|
||||
Type::getNonEmptyString(),
|
||||
]);
|
||||
|
||||
$redundant = false;
|
||||
@ -2652,7 +2651,7 @@ class SimpleAssertionReconciler extends Reconciler
|
||||
$callable_types[] = $type;
|
||||
$redundant = false;
|
||||
} elseif ($type instanceof TArray) {
|
||||
$type = new TCallableArray($type->type_params);
|
||||
$type = new TCallableKeyedArray($type->type_params);
|
||||
$callable_types[] = $type;
|
||||
$redundant = false;
|
||||
} elseif ($type instanceof TKeyedArray && count($type->properties) === 2) {
|
||||
|
@ -26,9 +26,10 @@ use Psalm\Type\Atomic\TArray;
|
||||
use Psalm\Type\Atomic\TArrayKey;
|
||||
use Psalm\Type\Atomic\TBool;
|
||||
use Psalm\Type\Atomic\TCallable;
|
||||
use Psalm\Type\Atomic\TCallableArray;
|
||||
use Psalm\Type\Atomic\TCallableKeyedArray;
|
||||
use Psalm\Type\Atomic\TCallableObject;
|
||||
use Psalm\Type\Atomic\TCallableString;
|
||||
use Psalm\Type\Atomic\TClassString;
|
||||
use Psalm\Type\Atomic\TEmptyMixed;
|
||||
use Psalm\Type\Atomic\TEmptyNumeric;
|
||||
use Psalm\Type\Atomic\TEmptyScalar;
|
||||
@ -1187,9 +1188,9 @@ class SimpleNegatedAssertionReconciler extends Reconciler
|
||||
$non_object_types[] = $type;
|
||||
}
|
||||
} elseif ($type instanceof TCallable) {
|
||||
$non_object_types[] = new TCallableArray([
|
||||
Type::getArrayKey(),
|
||||
Type::getMixed(),
|
||||
$non_object_types[] = new TCallableKeyedArray([
|
||||
new Union([new TClassString, new TObject]),
|
||||
Type::getNonEmptyString(),
|
||||
]);
|
||||
$non_object_types[] = new TCallableString();
|
||||
$redundant = false;
|
||||
@ -1586,9 +1587,9 @@ class SimpleNegatedAssertionReconciler extends Reconciler
|
||||
$non_string_types[] = new TInt();
|
||||
$redundant = false;
|
||||
} elseif ($type instanceof TCallable) {
|
||||
$non_string_types[] = new TCallableArray([
|
||||
Type::getArrayKey(),
|
||||
Type::getMixed(),
|
||||
$non_string_types[] = new TCallableKeyedArray([
|
||||
new Union([new TClassString, new TObject]),
|
||||
Type::getNonEmptyString(),
|
||||
]);
|
||||
$non_string_types[] = new TCallableObject();
|
||||
$redundant = false;
|
||||
|
@ -11,7 +11,6 @@ use Psalm\Type\Atomic\TArray;
|
||||
use Psalm\Type\Atomic\TArrayKey;
|
||||
use Psalm\Type\Atomic\TBool;
|
||||
use Psalm\Type\Atomic\TCallable;
|
||||
use Psalm\Type\Atomic\TCallableArray;
|
||||
use Psalm\Type\Atomic\TCallableKeyedArray;
|
||||
use Psalm\Type\Atomic\TCallableObject;
|
||||
use Psalm\Type\Atomic\TCallableString;
|
||||
@ -400,7 +399,6 @@ class TypeCombiner
|
||||
bool $allow_mixed_union,
|
||||
int $literal_limit
|
||||
): ?Union {
|
||||
|
||||
if ($type instanceof TMixed) {
|
||||
if ($type->from_loop_isset) {
|
||||
if ($combination->mixed_from_loop_isset === null) {
|
||||
@ -544,11 +542,17 @@ class TypeCombiner
|
||||
}
|
||||
}
|
||||
|
||||
if ($type instanceof TArray && $type_key === 'array') {
|
||||
if ($type instanceof TCallableArray && isset($combination->value_types['callable'])) {
|
||||
if ($type instanceof TCallableKeyedArray) {
|
||||
if (isset($combination->value_types['callable'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($combination->all_arrays_callable !== false) {
|
||||
$combination->all_arrays_callable = true;
|
||||
} else {
|
||||
$combination->all_arrays_callable = false;
|
||||
}
|
||||
}
|
||||
if ($type instanceof TArray && $type_key === 'array') {
|
||||
foreach ($type->type_params as $i => $type_param) {
|
||||
// See https://github.com/vimeo/psalm/pull/9439#issuecomment-1464563015
|
||||
/** @psalm-suppress PropertyTypeCoercion */
|
||||
@ -587,14 +591,7 @@ class TypeCombiner
|
||||
$combination->all_arrays_class_string_maps = false;
|
||||
}
|
||||
|
||||
if ($type instanceof TCallableArray) {
|
||||
if ($combination->all_arrays_callable !== false) {
|
||||
$combination->all_arrays_callable = true;
|
||||
}
|
||||
} else {
|
||||
$combination->all_arrays_callable = false;
|
||||
}
|
||||
|
||||
$combination->all_arrays_callable = false;
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -956,8 +953,8 @@ class TypeCombiner
|
||||
if ($type instanceof TCallable && $type_key === 'callable') {
|
||||
if (($combination->value_types['string'] ?? null) instanceof TCallableString) {
|
||||
unset($combination->value_types['string']);
|
||||
} elseif (!empty($combination->array_type_params) && $combination->all_arrays_callable) {
|
||||
$combination->array_type_params = [];
|
||||
} elseif (!empty($combination->objectlike_entries) && $combination->all_arrays_callable) {
|
||||
$combination->objectlike_entries = [];
|
||||
} elseif (isset($combination->value_types['callable-object'])) {
|
||||
unset($combination->value_types['callable-object']);
|
||||
}
|
||||
@ -1412,7 +1409,6 @@ class TypeCombiner
|
||||
$sealed || $fallback_key_type === null || $fallback_value_type === null
|
||||
? null
|
||||
: [$fallback_key_type, $fallback_value_type],
|
||||
(bool)$combination->all_arrays_lists,
|
||||
$from_docblock,
|
||||
);
|
||||
} else {
|
||||
@ -1525,7 +1521,7 @@ class TypeCombiner
|
||||
}
|
||||
|
||||
if ($combination->all_arrays_callable) {
|
||||
$array_type = new TCallableArray($generic_type_params);
|
||||
$array_type = new TCallableKeyedArray($generic_type_params);
|
||||
} elseif ($combination->array_always_filled
|
||||
|| ($combination->array_sometimes_filled && $overwrite_empty_array)
|
||||
|| ($combination->objectlike_entries
|
||||
|
@ -16,7 +16,6 @@ use Psalm\Type\Atomic\TArray;
|
||||
use Psalm\Type\Atomic\TArrayKey;
|
||||
use Psalm\Type\Atomic\TBool;
|
||||
use Psalm\Type\Atomic\TCallable;
|
||||
use Psalm\Type\Atomic\TCallableArray;
|
||||
use Psalm\Type\Atomic\TCallableKeyedArray;
|
||||
use Psalm\Type\Atomic\TCallableObject;
|
||||
use Psalm\Type\Atomic\TCallableString;
|
||||
@ -258,9 +257,19 @@ abstract class Atomic implements TypeNode
|
||||
]);
|
||||
|
||||
case 'callable-array':
|
||||
return new TCallableArray([
|
||||
new Union([new TArrayKey($from_docblock)]),
|
||||
new Union([new TMixed(false, $from_docblock)]),
|
||||
$classString = new TClassString(
|
||||
'object',
|
||||
null,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
);
|
||||
$object = new TObject(true);
|
||||
$string = new TNonEmptyString(true);
|
||||
return new TCallableKeyedArray([
|
||||
new Union([$classString, $object]),
|
||||
new Union([$string]),
|
||||
]);
|
||||
|
||||
case 'list':
|
||||
@ -459,7 +468,6 @@ abstract class Atomic implements TypeNode
|
||||
return $this instanceof TCallable
|
||||
|| $this instanceof TCallableObject
|
||||
|| $this instanceof TCallableString
|
||||
|| $this instanceof TCallableArray
|
||||
|| $this instanceof TCallableKeyedArray
|
||||
|| $this instanceof TClosure;
|
||||
}
|
||||
|
@ -1,16 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Psalm\Type\Atomic;
|
||||
|
||||
/**
|
||||
* Denotes an array that is _also_ `callable`.
|
||||
*
|
||||
* @psalm-immutable
|
||||
*/
|
||||
final class TCallableArray extends TNonEmptyArray
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $value = 'callable-array';
|
||||
}
|
@ -2,6 +2,8 @@
|
||||
|
||||
namespace Psalm\Type\Atomic;
|
||||
|
||||
use Psalm\Type\Union;
|
||||
|
||||
/**
|
||||
* Denotes an object-like array that is _also_ `callable`.
|
||||
*
|
||||
@ -10,5 +12,27 @@ namespace Psalm\Type\Atomic;
|
||||
final class TCallableKeyedArray extends TKeyedArray
|
||||
{
|
||||
protected const NAME_ARRAY = 'callable-array';
|
||||
protected const NAME_LIST = 'callable-list';
|
||||
protected const NAME_LIST = 'callable-array';
|
||||
|
||||
/**
|
||||
* Constructs a new instance of a generic type
|
||||
*
|
||||
* @param non-empty-array<string|int, Union> $properties
|
||||
* @param array{Union, Union}|null $fallback_params
|
||||
* @param array<string, bool> $class_strings
|
||||
*/
|
||||
public function __construct(
|
||||
array $properties,
|
||||
?array $class_strings = null,
|
||||
?array $fallback_params = null,
|
||||
bool $from_docblock = false
|
||||
) {
|
||||
parent::__construct(
|
||||
$properties,
|
||||
$class_strings,
|
||||
$fallback_params,
|
||||
true,
|
||||
$from_docblock,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1827,16 +1827,16 @@ class CallableTest extends TestCase
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/** @param Closure(int, int): int $f */
|
||||
function int_int(Closure $f): void {}
|
||||
|
||||
|
||||
/** @param Closure(int, int, int): int $f */
|
||||
function int_int_int(Closure $f): void {}
|
||||
|
||||
|
||||
/** @param Closure(int, int, int, int): int $f */
|
||||
function int_int_int_int(Closure $f): void {}
|
||||
|
||||
|
||||
int_int(withVariadic(...));
|
||||
int_int_int(withVariadic(...));
|
||||
int_int_int_int(withVariadic(...));',
|
||||
@ -1844,6 +1844,18 @@ class CallableTest extends TestCase
|
||||
'ignored_issues' => [],
|
||||
'php_version' => '8.0',
|
||||
],
|
||||
'callableArrayTypes' => [
|
||||
'code' => '<?php
|
||||
/** @var callable-array $c */
|
||||
$c;
|
||||
[$a, $b] = $c;
|
||||
',
|
||||
'assertions' => [
|
||||
'$a' => 'class-string|object',
|
||||
'$b' => 'string',
|
||||
'$c' => 'list{class-string|object, string}',
|
||||
],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@ -2291,16 +2303,16 @@ class CallableTest extends TestCase
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/** @param Closure(int, int, string, int, int): int $f */
|
||||
function int_int_string_int_int(Closure $f): void {}
|
||||
|
||||
|
||||
/** @param Closure(int, int, int, string, int): int $f */
|
||||
function int_int_int_string_int(Closure $f): void {}
|
||||
|
||||
|
||||
/** @param Closure(int, int, int, int, string): int $f */
|
||||
function int_int_int_int_string(Closure $f): void {}
|
||||
|
||||
|
||||
int_int_string_int_int(add(...));
|
||||
int_int_int_string_int(add(...));
|
||||
int_int_int_int_string(add(...));',
|
||||
|
@ -480,7 +480,7 @@ class TypeParseTest extends TestCase
|
||||
public function testTKeyedCallableArrayNonList(): void
|
||||
{
|
||||
$this->assertSame(
|
||||
'callable-array{0: class-string, 1: string}',
|
||||
'callable-array{class-string, string}',
|
||||
(string)Type::parseString('callable-array{0: class-string, 1: string}'),
|
||||
);
|
||||
}
|
||||
|
@ -156,7 +156,7 @@ class ReconcilerTest extends TestCase
|
||||
'nullableClassStringTruthy' => ['class-string<SomeClass>', new Truthy(), 'class-string<SomeClass>|null'],
|
||||
'iterableToArray' => ['array<int, int>', new IsType(new TArray([Type::getArrayKey(), Type::getMixed()])), 'iterable<int, int>'],
|
||||
'iterableToTraversable' => ['Traversable<int, int>', new IsType(new TNamedObject('Traversable')), 'iterable<int, int>'],
|
||||
'callableToCallableArray' => ['callable-array{0: class-string|object, 1: string}', new IsType(new TArray([Type::getArrayKey(), Type::getMixed()])), 'callable'],
|
||||
'callableToCallableArray' => ['callable-array{class-string|object, non-empty-string}', new IsType(new TArray([Type::getArrayKey(), Type::getMixed()])), 'callable'],
|
||||
'SmallKeyedArrayAndCallable' => ['array{test: string}', new IsType(new TKeyedArray(['test' => Type::getString()])), 'callable'],
|
||||
'BigKeyedArrayAndCallable' => ['array{foo: string, test: string, thing: string}', new IsType(new TKeyedArray(['foo' => Type::getString(), 'test' => Type::getString(), 'thing' => Type::getString()])), 'callable'],
|
||||
'callableOrArrayToCallableArray' => ['array<array-key, mixed>', new IsType(new TArray([Type::getArrayKey(), Type::getMixed()])), 'callable|array'],
|
||||
|
Loading…
Reference in New Issue
Block a user