Skip to content

Commit

Permalink
Improve array_merge return type
Browse files Browse the repository at this point in the history
  • Loading branch information
muglug committed Sep 14, 2020
1 parent d1ac38b commit 1b3fbeb
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public static function getFunctionReturnType(
$codebase = $statements_source->getCodebase();

$generic_properties = [];
$all_keyed_arrays = true;
$all_int_offsets = true;
$all_nonempty_lists = true;
$any_nonempty = false;
Expand Down Expand Up @@ -80,26 +81,24 @@ public static function getFunctionReturnType(
}

if ($unpacked_type_part instanceof Type\Atomic\ObjectLike) {
if ($generic_properties !== null) {
foreach ($unpacked_type_part->properties as $key => $type) {
if (!\is_string($key)) {
$generic_properties[] = $type;
continue;
}

if (!isset($generic_properties[$key]) || !$type->possibly_undefined) {
$generic_properties[$key] = $type;
} else {
$was_possibly_undefined = $generic_properties[$key]->possibly_undefined;

$generic_properties[$key] = Type::combineUnionTypes(
$generic_properties[$key],
$type,
$codebase
);

$generic_properties[$key]->possibly_undefined = $was_possibly_undefined;
}
foreach ($unpacked_type_part->properties as $key => $type) {
if (!\is_string($key)) {
$generic_properties[] = $type;
continue;
}

if (!isset($generic_properties[$key]) || !$type->possibly_undefined) {
$generic_properties[$key] = $type;
} else {
$was_possibly_undefined = $generic_properties[$key]->possibly_undefined;

$generic_properties[$key] = Type::combineUnionTypes(
$generic_properties[$key],
$type,
$codebase
);

$generic_properties[$key]->possibly_undefined = $was_possibly_undefined;
}
}

Expand All @@ -111,9 +110,11 @@ public static function getFunctionReturnType(
$any_nonempty = true;
}

$unpacked_type_part = $unpacked_type_part->getGenericArrayType();
} elseif ($unpacked_type_part instanceof Type\Atomic\TList) {
$generic_properties = null;
continue;
}

if ($unpacked_type_part instanceof Type\Atomic\TList) {
$all_keyed_arrays = false;

if (!$unpacked_type_part instanceof Type\Atomic\TNonEmptyList) {
$all_nonempty_lists = false;
Expand All @@ -132,9 +133,19 @@ public static function getFunctionReturnType(
return Type::getArray();
}
}
} elseif (!$unpacked_type_part->type_params[0]->isEmpty()) {
$generic_properties = null;
$all_nonempty_lists = false;
} else {
if (!$unpacked_type_part->type_params[0]->isEmpty()) {
foreach ($generic_properties as $key => $keyed_type) {
$generic_properties[$key] = Type::combineUnionTypes(
$keyed_type,
$unpacked_type_part->type_params[1],
$codebase
);
}

$all_keyed_arrays = false;
$all_nonempty_lists = false;
}
}

if ($unpacked_type_part instanceof Type\Atomic\TArray) {
Expand Down Expand Up @@ -167,19 +178,33 @@ public static function getFunctionReturnType(
}
}

$inner_key_type = null;
$inner_value_type = null;

if ($inner_key_types) {
$inner_key_type = TypeCombination::combineTypes($inner_key_types, $codebase, true);
}

if ($inner_value_types) {
$inner_value_type = TypeCombination::combineTypes($inner_value_types, $codebase, true);
}

if ($generic_properties) {
$objectlike = new Type\Atomic\ObjectLike($generic_properties);

if ($all_nonempty_lists) {
if ($all_nonempty_lists || $all_int_offsets) {
$objectlike->is_list = true;
}

if (!$all_keyed_arrays) {
$objectlike->previous_key_type = $inner_key_type;
$objectlike->previous_value_type = $inner_value_type;
}

return new Type\Union([$objectlike]);
}

if ($inner_value_types) {
$inner_value_type = TypeCombination::combineTypes($inner_value_types, $codebase, true);

if ($inner_value_type) {
if ($all_int_offsets) {
if ($any_nonempty) {
return new Type\Union([
Expand All @@ -192,9 +217,7 @@ public static function getFunctionReturnType(
]);
}

$inner_key_type = $inner_key_types
? TypeCombination::combineTypes($inner_key_types, $codebase, true)
: Type::getArrayKey();
$inner_key_type = $inner_key_type ?: Type::getArrayKey();

if ($any_nonempty) {
return new Type\Union([
Expand Down
55 changes: 47 additions & 8 deletions tests/ArrayFunctionCallTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -184,21 +184,23 @@ function b(array $opts): array {
return array_merge(["host" => 5], $opts);
}',
],
'arrayMergeListResult' => [
'arrayMergeListResultWithArray' => [
'<?php
/**
* @param list<string> $list
* @param array<int, string> $list
* @return list<string>
*/
function foo(array $list) : array {
function bar(array $list) : array {
return array_merge($list, ["test"]);
}
}',
],
'arrayMergeListResultWithList' => [
'<?php
/**
* @param array<int, string> $list
* @param list<string> $list
* @return list<string>
*/
function bar(array $list) : array {
function foo(array $list) : array {
return array_merge($list, ["test"]);
}',
],
Expand Down Expand Up @@ -589,7 +591,7 @@ function foo($a)
foo($a3);',
'assertions' => [
'$a3' => 'array{bye: int, hi: int}',
'$a3' => 'array{hi: int, bye: int}',
],
],
'arrayRand' => [
Expand Down Expand Up @@ -1806,6 +1808,30 @@ function foo(array $slugParts) : void {
if (!empty($slugParts)) {}
}'
],
'arrayMergeKeepLastKeysAndType' => [
'<?php
/**
* @param array{A: int} $a
* @param array<string, string> $b
*
* @return array{A: int}
*/
function merger(array $a, array $b) : array {
return array_merge($b, $a);
}'
],
'arrayMergeKeepFirstKeysSameType' => [
'<?php
/**
* @param array{A: int} $a
* @param array<string, int> $b
*
* @return array{A: int}
*/
function merger(array $a, array $b) : array {
return array_merge($a, $b);
}'
],
];
}

Expand Down Expand Up @@ -2008,6 +2034,19 @@ function (string $a, string $b): int {
);',
'error_message' => 'InvalidArgument',
],
'arrayMergeKeepFirstKeysButNotType' => [
'<?php
/**
* @param array{A: int} $a
* @param array<string, string> $b
*
* @return array{A: int}
*/
function merger(array $a, array $b) : array {
return array_merge($a, $b);
}',
'error_message' => 'LessSpecificReturnStatement - src/somefile.php:9:32 - The type \'array{A: int|string}<string, string>\' is more general',
],
];
}
}

0 comments on commit 1b3fbeb

Please sign in to comment.