Skip to content

Commit

Permalink
Merge pull request #8924 from danog/array_merge
Browse files Browse the repository at this point in the history
More array_merge improvements
  • Loading branch information
orklah committed Dec 18, 2022
2 parents 7d95f15 + c454a18 commit 9f5e4f5
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 11 deletions.
10 changes: 5 additions & 5 deletions psalm-baseline.xml
Expand Up @@ -56,17 +56,17 @@
<code>$source_parts[1]</code>
</PossiblyUndefinedIntArrayOffset>
</file>
<file src="src/Psalm/Internal/Analyzer/Statements/Block/ForAnalyzer.php">
<ArgumentTypeCoercion occurrences="1">
<code>$stmt-&gt;cond</code>
</ArgumentTypeCoercion>
</file>
<file src="src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php">
<ConflictingReferenceConstraint occurrences="2">
<code>if (AtomicTypeComparator::isContainedBy(</code>
<code>if (AtomicTypeComparator::isContainedBy(</code>
</ConflictingReferenceConstraint>
</file>
<file src="src/Psalm/Internal/Analyzer/Statements/Block/LoopAnalyzer.php">
<PossiblyUndefinedIntArrayOffset occurrences="1">
<code>$pre_conditions[0]</code>
</PossiblyUndefinedIntArrayOffset>
</file>
<file src="src/Psalm/Internal/Analyzer/Statements/Block/SwitchAnalyzer.php">
<InvalidPropertyAssignmentValue occurrences="1">
<code>$context-&gt;assigned_var_ids += $switch_scope-&gt;new_assigned_var_ids</code>
Expand Down
4 changes: 2 additions & 2 deletions src/Psalm/Internal/Analyzer/Statements/Block/LoopAnalyzer.php
Expand Up @@ -33,8 +33,8 @@ class LoopAnalyzer
/**
* Checks an array of statements in a loop
*
* @param array<PhpParser\Node\Stmt> $stmts
* @param PhpParser\Node\Expr[] $pre_conditions
* @param list<PhpParser\Node\Stmt> $stmts
* @param list<PhpParser\Node\Expr> $pre_conditions
* @param PhpParser\Node\Expr[] $post_expressions
* @return false|null
*/
Expand Down
Expand Up @@ -59,6 +59,7 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev
$all_int_offsets = true;
$all_nonempty_lists = true;
$any_nonempty = false;
$all_empty = true;

$max_keyed_array_size = 0;

Expand All @@ -75,17 +76,31 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev
$unpacking_possibly_empty = false;
if ($call_arg->unpack) {
if ($type_part instanceof TKeyedArray) {
$unpacked_type_parts = $type_part->getGenericValueType();
$unpacking_indefinite_number_of_args = $type_part->fallback_params !== null;
if (!$type_part->fallback_params
&& $type_part->getMinCount() === $type_part->getMaxCount()
) {
$unpacked_type_parts = [];
foreach ($type_part->properties as $t) {
$unpacked_type_parts = array_merge(
$unpacked_type_parts,
$t->getAtomicTypes()
);
}
} else {
$unpacked_type_parts = $type_part
->getGenericValueType()
->getAtomicTypes();
$unpacking_indefinite_number_of_args = true;
}
$unpacking_possibly_empty = !$type_part->isNonEmpty();
} elseif ($type_part instanceof TArray) {
$unpacked_type_parts = $type_part->type_params[1];
$unpacking_indefinite_number_of_args = true;
$unpacking_possibly_empty = !$type_part instanceof TNonEmptyArray;
$unpacked_type_parts = $unpacked_type_parts->getAtomicTypes();
} else {
return Type::getArray();
}
$unpacked_type_parts = $unpacked_type_parts->getAtomicTypes();
} else {
$unpacked_type_parts = [$type_part];
}
Expand All @@ -100,6 +115,8 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev
}

if ($unpacked_type_part instanceof TKeyedArray) {
$all_empty = false;

$max_keyed_array_size = max(
$max_keyed_array_size,
count($unpacked_type_part->properties)
Expand Down Expand Up @@ -214,6 +231,8 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev
return Type::getArray();
}

$all_empty = false;

$inner_key_types = array_merge(
$inner_key_types,
array_values($unpacked_type_part->type_params[0]->getAtomicTypes())
Expand Down Expand Up @@ -256,6 +275,10 @@ public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $ev
return new Union([$objectlike]);
}

if ($all_empty) {
return Type::getEmptyArray();
}

if ($inner_value_type) {
if ($all_int_offsets) {
if ($any_nonempty) {
Expand Down
18 changes: 18 additions & 0 deletions stubs/CoreGenericFunctions.phpstub
Expand Up @@ -336,6 +336,8 @@ function array_key_exists($key, array $array) : bool
/**
* @psalm-pure
*
* @no-named-arguments
*
* @psalm-template TKey as array-key
* @psalm-template TValue
*
Expand All @@ -347,6 +349,22 @@ function array_merge_recursive(array ...$arrays)
{
}

/**
* @psalm-pure
*
* @no-named-arguments
*
* @psalm-template TKey as array-key
* @psalm-template TValue
*
* @param array<TKey, TValue> ...$arrays
*
* @return array<TKey, TValue>
*/
function array_merge(array ...$arrays)
{
}

/**
* @psalm-pure
*
Expand Down
30 changes: 29 additions & 1 deletion tests/ArrayFunctionCallTest.php
Expand Up @@ -301,6 +301,20 @@ public function merge($a, $b): array
'$d===' => "list{string, ...<int<0, max>, int|string>}",
],
],
'arrayMergeEmpty' => [
'code' => '<?php
$test = [[]];
$a = array_merge(...$test);
$test = [[], ["test" => 0]];
$b = array_merge(...$test);
',
'assertions' => [
'$a===' => 'array<never, never>',
'$b===' => 'array{test: 0}',
]
],
'arrayReplaceIntArrays' => [
'code' => '<?php
$d = array_replace(["a", "b", "c", "d"], [1, 2, 3]);',
Expand Down Expand Up @@ -2119,7 +2133,7 @@ function makeAList(int $ofThisInteger): array {
'arrayMapWithEmptyArrayReturn' => [
'code' => '<?php
/**
* @param array<array<string>> $elements
* @param array<int, array<string>> $elements
* @return list<string>
*/
function resolvePossibleFilePaths($elements) : array
Expand Down Expand Up @@ -2754,6 +2768,20 @@ function merger(array $a, array $b) : array {
array_combine(["a", "b"], [1, 2, 3]);',
'error_message' => 'InvalidArgument',
],
'arrayMergeNoNamed' => [
'code' => '<?php
$map = ["a" => []];
array_merge(...$map);
',
'error_message' => 'NamedArgumentNotAllowed'
],
'arrayMergeRecursiveNoNamed' => [
'code' => '<?php
$map = ["a" => []];
array_merge_recursive(...$map);
',
'error_message' => 'NamedArgumentNotAllowed'
]
];
}
}

0 comments on commit 9f5e4f5

Please sign in to comment.