mirror of
https://github.com/danog/psalm.git
synced 2025-01-22 05:41:20 +01:00
parent
262ba9bdec
commit
55913699d3
@ -19,6 +19,7 @@ use Psalm\Type\Atomic\TFalse;
|
||||
use Psalm\Type\Atomic\TFloat;
|
||||
use Psalm\Type\Atomic\TGenericObject;
|
||||
use Psalm\Type\Atomic\TInt;
|
||||
use Psalm\Type\Atomic\TIterable;
|
||||
use Psalm\Type\Atomic\TLiteralFloat;
|
||||
use Psalm\Type\Atomic\TLiteralInt;
|
||||
use Psalm\Type\Atomic\TLiteralClassString;
|
||||
@ -199,6 +200,33 @@ class TypeCombination
|
||||
$combination->value_types['bool'] = new TBool();
|
||||
}
|
||||
|
||||
if (isset($combination->type_params['array'])
|
||||
&& (isset($combination->named_object_types['Traversable'])
|
||||
|| isset($combination->type_params['Traversable']))
|
||||
) {
|
||||
$array_param_types = $combination->type_params['array'];
|
||||
$traversable_param_types = $combination->type_params['Traversable'] ?? [Type::getMixed(), Type::getMixed()];
|
||||
|
||||
$combined_param_types = [];
|
||||
|
||||
foreach ($array_param_types as $i => $array_param_type) {
|
||||
$combined_param_types[$i] = Type::combineUnionTypes($array_param_type, $traversable_param_types[$i]);
|
||||
}
|
||||
|
||||
$combination->value_types['iterable'] = new TIterable($combined_param_types);
|
||||
|
||||
/**
|
||||
* @psalm-suppress PossiblyNullArrayOffset
|
||||
* @psalm-suppress PossiblyNullArrayAccess
|
||||
*/
|
||||
unset(
|
||||
$combination->value_types['array'],
|
||||
$combination->named_object_types['Traversable'],
|
||||
$combination->type_params['array'],
|
||||
$combination->type_params['Traversable']
|
||||
);
|
||||
}
|
||||
|
||||
if ($combination->empty_mixed && $combination->non_empty_mixed) {
|
||||
$combination->value_types['mixed'] = new TMixed((bool) $combination->mixed_from_loop_isset);
|
||||
}
|
||||
@ -303,9 +331,13 @@ class TypeCombination
|
||||
|
||||
$new_types[] = $array_type;
|
||||
} elseif (!isset($combination->value_types[$generic_type])) {
|
||||
if ($generic_type === 'iterable') {
|
||||
$new_types[] = new TIterable($generic_type_params);
|
||||
} else {
|
||||
$new_types[] = new TGenericObject($generic_type, $generic_type_params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($combination->strings) {
|
||||
$new_types = array_merge($new_types, array_values($combination->strings));
|
||||
@ -417,9 +449,78 @@ class TypeCombination
|
||||
unset($combination->value_types['true']);
|
||||
}
|
||||
|
||||
$type_key = $type->getKey();
|
||||
if ($type instanceof TArray && isset($combination->type_params['iterable'])) {
|
||||
$type_key = 'iterable';
|
||||
} elseif ($type instanceof TArray
|
||||
&& $type->type_params[1]->isMixed()
|
||||
&& isset($combination->value_types['iterable'])
|
||||
) {
|
||||
$type_key = 'iterable';
|
||||
$combination->type_params['iterable'] = [Type::getMixed(), Type::getMixed()];
|
||||
} elseif ($type instanceof TNamedObject
|
||||
&& $type->value === 'Traversable'
|
||||
&& (isset($combination->type_params['iterable']) || isset($combination->value_types['iterable']))
|
||||
) {
|
||||
$type_key = 'iterable';
|
||||
|
||||
if ($type instanceof TArray || $type instanceof TGenericObject) {
|
||||
if (!isset($combination->type_params['iterable'])) {
|
||||
$combination->type_params['iterable'] = [Type::getMixed(), Type::getMixed()];
|
||||
}
|
||||
|
||||
if (!$type instanceof TGenericObject) {
|
||||
$type = new TGenericObject($type->value, [Type::getMixed(), Type::getMixed()]);
|
||||
}
|
||||
} else {
|
||||
$type_key = $type->getKey();
|
||||
}
|
||||
|
||||
if ($type instanceof TIterable
|
||||
&& isset($combination->type_params['array'])
|
||||
&& ($type->has_docblock_params || $combination->type_params['array'][1]->isMixed())
|
||||
) {
|
||||
if (!isset($combination->type_params['iterable'])) {
|
||||
$combination->type_params['iterable'] = $combination->type_params['array'];
|
||||
} else {
|
||||
foreach ($combination->type_params['array'] as $i => $array_type_param) {
|
||||
$iterable_type_param = $combination->type_params['iterable'][$i];
|
||||
$combination->type_params['iterable'][$i] = Type::combineUnionTypes(
|
||||
$iterable_type_param,
|
||||
$array_type_param
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
unset($combination->type_params['array']);
|
||||
}
|
||||
|
||||
if ($type instanceof TIterable
|
||||
&& (isset($combination->named_object_types['Traversable'])
|
||||
|| isset($combination->type_params['Traversable']))
|
||||
) {
|
||||
if (!isset($combination->type_params['iterable'])) {
|
||||
$combination->type_params['iterable']
|
||||
= $combination->type_params['Traversable'] ?? [Type::getMixed(), Type::getMixed()];
|
||||
} else {
|
||||
foreach ($combination->type_params['Traversable'] as $i => $array_type_param) {
|
||||
$iterable_type_param = $combination->type_params['iterable'][$i];
|
||||
$combination->type_params['iterable'][$i] = Type::combineUnionTypes(
|
||||
$iterable_type_param,
|
||||
$array_type_param
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** @psalm-suppress PossiblyNullArrayAccess */
|
||||
unset(
|
||||
$combination->named_object_types['Traversable'],
|
||||
$combination->type_params['Traversable']
|
||||
);
|
||||
}
|
||||
|
||||
if ($type instanceof TArray
|
||||
|| $type instanceof TGenericObject
|
||||
|| ($type instanceof TIterable && $type->has_docblock_params)
|
||||
) {
|
||||
foreach ($type->type_params as $i => $type_param) {
|
||||
if (isset($combination->type_params[$type_key][$i])) {
|
||||
$combination->type_params[$type_key][$i] = Type::combineUnionTypes(
|
||||
@ -489,6 +590,11 @@ class TypeCombination
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($type instanceof TIterable) {
|
||||
$combination->value_types[$type_key] = $type;
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($type instanceof TNamedObject) {
|
||||
if ($combination->named_object_types === null) {
|
||||
return null;
|
||||
|
@ -92,7 +92,7 @@ abstract class Atomic
|
||||
return new TArrayKey();
|
||||
|
||||
case 'iterable':
|
||||
return new TIterable([new Union([new TMixed]), new Union([new TMixed])]);
|
||||
return new TIterable();
|
||||
|
||||
case 'never-return':
|
||||
case 'never-returns':
|
||||
|
@ -14,12 +14,22 @@ class TIterable extends Atomic
|
||||
*/
|
||||
public $value = 'iterable';
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $has_docblock_params = false;
|
||||
|
||||
/**
|
||||
* @param array<int, \Psalm\Type\Union> $type_params
|
||||
*/
|
||||
public function __construct(array $type_params)
|
||||
public function __construct(array $type_params = [])
|
||||
{
|
||||
if ($type_params) {
|
||||
$this->has_docblock_params = true;
|
||||
$this->type_params = $type_params;
|
||||
} else {
|
||||
$this->type_params = [\Psalm\Type::getMixed(), \Psalm\Type::getMixed()];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -195,6 +195,76 @@ class TypeCombinationTest extends TestCase
|
||||
'array<string>',
|
||||
],
|
||||
],
|
||||
'arrayTraversableToIterable' => [
|
||||
'iterable<array-key|mixed, mixed>',
|
||||
[
|
||||
'array',
|
||||
'Traversable',
|
||||
],
|
||||
],
|
||||
'arrayIterableToIterable' => [
|
||||
'iterable<mixed, mixed>',
|
||||
[
|
||||
'array',
|
||||
'iterable',
|
||||
],
|
||||
],
|
||||
'iterableArrayToIterable' => [
|
||||
'iterable<mixed, mixed>',
|
||||
[
|
||||
'iterable',
|
||||
'array',
|
||||
],
|
||||
],
|
||||
'traversableIterableToIterable' => [
|
||||
'iterable<mixed, mixed>',
|
||||
[
|
||||
'Traversable',
|
||||
'iterable',
|
||||
],
|
||||
],
|
||||
'iterableTraversableToIterable' => [
|
||||
'iterable<mixed, mixed>',
|
||||
[
|
||||
'iterable',
|
||||
'Traversable',
|
||||
],
|
||||
],
|
||||
'arrayTraversableToIterableWithParams' => [
|
||||
'iterable<int, string|bool>',
|
||||
[
|
||||
'array<int, string>',
|
||||
'Traversable<int, bool>',
|
||||
],
|
||||
],
|
||||
'arrayIterableToIterableWithParams' => [
|
||||
'iterable<int, string|bool>',
|
||||
[
|
||||
'array<int, string>',
|
||||
'iterable<int, bool>',
|
||||
],
|
||||
],
|
||||
'iterableArrayToIterableWithParams' => [
|
||||
'iterable<int, string|bool>',
|
||||
[
|
||||
'iterable<int, string>',
|
||||
'array<int, bool>',
|
||||
],
|
||||
],
|
||||
'traversableIterableToIterableWithParams' => [
|
||||
'iterable<int, string|bool>',
|
||||
[
|
||||
'Traversable<int, string>',
|
||||
'iterable<int, bool>',
|
||||
],
|
||||
],
|
||||
'iterableTraversableToIterableWithParams' => [
|
||||
'iterable<int, string|bool>',
|
||||
[
|
||||
'iterable<int, string>',
|
||||
'Traversable<int, bool>',
|
||||
],
|
||||
],
|
||||
'falseDestruction' => [
|
||||
'bool',
|
||||
[
|
||||
|
Loading…
x
Reference in New Issue
Block a user