1
0
mirror of https://github.com/danog/psalm.git synced 2024-12-02 17:52:45 +01:00

Improve type combination when evaluating array types

This commit is contained in:
Matthew Brown 2016-09-12 23:24:26 -04:00
parent 27b731a062
commit 884648b56c
2 changed files with 23 additions and 8 deletions

View File

@ -1679,7 +1679,7 @@ class StatementsChecker
if (isset($item->value->inferredType)) { if (isset($item->value->inferredType)) {
if ($item_value_type) { if ($item_value_type) {
$item_value_type = Type::combineUnionTypes($item->value->inferredType, $item_value_type); $item_value_type = Type::combineUnionTypes($item->value->inferredType, $item_value_type, true);
} }
else { else {
$item_value_type = $item->value->inferredType; $item_value_type = $item->value->inferredType;
@ -1692,7 +1692,9 @@ class StatementsChecker
'array', 'array',
[ [
$item_key_type ?: new Type\Union([new Type\Atomic('int'), new Type\Atomic('string')]), $item_key_type ?: new Type\Union([new Type\Atomic('int'), new Type\Atomic('string')]),
$item_value_type && count($item_value_type->types) === 1 ? $item_value_type : Type::getMixed() $item_value_type && count($item_value_type->types) === ($item_value_type->isNullable() ? 2 : 1)
? $item_value_type
: Type::getMixed()
] ]
) )
]); ]);

View File

@ -485,11 +485,12 @@ abstract class Type
* Combines two union types into one * Combines two union types into one
* @param Union $type_1 * @param Union $type_1
* @param Union $type_2 * @param Union $type_2
* @param bool $combine_to_mixed if true, combine differing types A and B to mixed, not A|B
* @return Union * @return Union
*/ */
public static function combineUnionTypes(Union $type_1, Union $type_2) public static function combineUnionTypes(Union $type_1, Union $type_2, $combine_to_mixed = false)
{ {
return self::combineTypes(array_merge(array_values($type_1->types), array_values($type_2->types))); return self::combineTypes(array_merge(array_values($type_1->types), array_values($type_2->types)), $combine_to_mixed);
} }
/** /**
@ -502,9 +503,10 @@ abstract class Type
* and array + array<string> = array<mixed> * and array + array<string> = array<mixed>
* *
* @param array<Atomic> $types * @param array<Atomic> $types
* @param bool $combine_to_mixed if true, combine differing types A and B to mixed, not A|B
* @return Union * @return Union
*/ */
public static function combineTypes(array $types) public static function combineTypes(array $types, $combine_to_mixed = false)
{ {
if (in_array(null, $types)) { if (in_array(null, $types)) {
return Type::getMixed(); return Type::getMixed();
@ -595,7 +597,7 @@ abstract class Type
// if we're continuing, also add the correspoinding key type param if it exists // if we're continuing, also add the correspoinding key type param if it exists
if ($expanded_key_types) { if ($expanded_key_types) {
array_unshift($generic_type_params, self::combineTypes($expanded_key_types)); array_unshift($generic_type_params, self::combineTypes($expanded_key_types, $combine_to_mixed));
} }
$new_types[] = $value_type_param ? new Generic($generic_type, $generic_type_params) : new Atomic($generic_type); $new_types[] = $value_type_param ? new Generic($generic_type, $generic_type_params) : new Atomic($generic_type);
@ -604,8 +606,11 @@ abstract class Type
$expanded_value_types = []; $expanded_value_types = [];
$has_null = false;
foreach ($value_type as $expandable_value_type) { foreach ($value_type as $expandable_value_type) {
if ($expandable_value_type) { if ($expandable_value_type) {
$has_null = $has_null || $expandable_value_type->isNullable();
$expanded_value_types = array_merge($expanded_value_types, array_values($expandable_value_type->types)); $expanded_value_types = array_merge($expanded_value_types, array_values($expandable_value_type->types));
} }
else { else {
@ -613,10 +618,18 @@ abstract class Type
} }
} }
$generic_type_params = [self::combineTypes($expanded_value_types)]; // if $combine_to_mixed is true, we want to combine multiple non-null types to a single mixed type.
if ($combine_to_mixed) {
if (count($expanded_value_types) > ($has_null ? 2 : 1)) {
$expanded_value_types = [Type::getMixed()->types['mixed']];
}
}
$generic_type_params = [self::combineTypes($expanded_value_types, $combine_to_mixed)];
if ($expanded_key_types) { if ($expanded_key_types) {
array_unshift($generic_type_params, self::combineTypes($expanded_key_types)); array_unshift($generic_type_params, self::combineTypes($expanded_key_types, $combine_to_mixed));
} }
// we have a generic type with // we have a generic type with