1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-22 13:51:54 +01:00

Merge pull request #8924 from danog/array_merge

More array_merge improvements
This commit is contained in:
orklah 2022-12-18 18:00:59 +01:00 committed by GitHub
commit 9f5e4f58b2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 80 additions and 11 deletions

View File

@ -56,17 +56,17 @@
<code>$source_parts[1]</code> <code>$source_parts[1]</code>
</PossiblyUndefinedIntArrayOffset> </PossiblyUndefinedIntArrayOffset>
</file> </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"> <file src="src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php">
<ConflictingReferenceConstraint occurrences="2"> <ConflictingReferenceConstraint occurrences="2">
<code>if (AtomicTypeComparator::isContainedBy(</code> <code>if (AtomicTypeComparator::isContainedBy(</code>
<code>if (AtomicTypeComparator::isContainedBy(</code> <code>if (AtomicTypeComparator::isContainedBy(</code>
</ConflictingReferenceConstraint> </ConflictingReferenceConstraint>
</file> </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"> <file src="src/Psalm/Internal/Analyzer/Statements/Block/SwitchAnalyzer.php">
<InvalidPropertyAssignmentValue occurrences="1"> <InvalidPropertyAssignmentValue occurrences="1">
<code>$context-&gt;assigned_var_ids += $switch_scope-&gt;new_assigned_var_ids</code> <code>$context-&gt;assigned_var_ids += $switch_scope-&gt;new_assigned_var_ids</code>

View File

@ -33,8 +33,8 @@ class LoopAnalyzer
/** /**
* Checks an array of statements in a loop * Checks an array of statements in a loop
* *
* @param array<PhpParser\Node\Stmt> $stmts * @param list<PhpParser\Node\Stmt> $stmts
* @param PhpParser\Node\Expr[] $pre_conditions * @param list<PhpParser\Node\Expr> $pre_conditions
* @param PhpParser\Node\Expr[] $post_expressions * @param PhpParser\Node\Expr[] $post_expressions
* @return false|null * @return false|null
*/ */

View File

@ -59,6 +59,7 @@ class ArrayMergeReturnTypeProvider implements FunctionReturnTypeProviderInterfac
$all_int_offsets = true; $all_int_offsets = true;
$all_nonempty_lists = true; $all_nonempty_lists = true;
$any_nonempty = false; $any_nonempty = false;
$all_empty = true;
$max_keyed_array_size = 0; $max_keyed_array_size = 0;
@ -75,17 +76,31 @@ class ArrayMergeReturnTypeProvider implements FunctionReturnTypeProviderInterfac
$unpacking_possibly_empty = false; $unpacking_possibly_empty = false;
if ($call_arg->unpack) { if ($call_arg->unpack) {
if ($type_part instanceof TKeyedArray) { if ($type_part instanceof TKeyedArray) {
$unpacked_type_parts = $type_part->getGenericValueType(); if (!$type_part->fallback_params
$unpacking_indefinite_number_of_args = $type_part->fallback_params !== null; && $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(); $unpacking_possibly_empty = !$type_part->isNonEmpty();
} elseif ($type_part instanceof TArray) { } elseif ($type_part instanceof TArray) {
$unpacked_type_parts = $type_part->type_params[1]; $unpacked_type_parts = $type_part->type_params[1];
$unpacking_indefinite_number_of_args = true; $unpacking_indefinite_number_of_args = true;
$unpacking_possibly_empty = !$type_part instanceof TNonEmptyArray; $unpacking_possibly_empty = !$type_part instanceof TNonEmptyArray;
$unpacked_type_parts = $unpacked_type_parts->getAtomicTypes();
} else { } else {
return Type::getArray(); return Type::getArray();
} }
$unpacked_type_parts = $unpacked_type_parts->getAtomicTypes();
} else { } else {
$unpacked_type_parts = [$type_part]; $unpacked_type_parts = [$type_part];
} }
@ -100,6 +115,8 @@ class ArrayMergeReturnTypeProvider implements FunctionReturnTypeProviderInterfac
} }
if ($unpacked_type_part instanceof TKeyedArray) { if ($unpacked_type_part instanceof TKeyedArray) {
$all_empty = false;
$max_keyed_array_size = max( $max_keyed_array_size = max(
$max_keyed_array_size, $max_keyed_array_size,
count($unpacked_type_part->properties) count($unpacked_type_part->properties)
@ -214,6 +231,8 @@ class ArrayMergeReturnTypeProvider implements FunctionReturnTypeProviderInterfac
return Type::getArray(); return Type::getArray();
} }
$all_empty = false;
$inner_key_types = array_merge( $inner_key_types = array_merge(
$inner_key_types, $inner_key_types,
array_values($unpacked_type_part->type_params[0]->getAtomicTypes()) array_values($unpacked_type_part->type_params[0]->getAtomicTypes())
@ -256,6 +275,10 @@ class ArrayMergeReturnTypeProvider implements FunctionReturnTypeProviderInterfac
return new Union([$objectlike]); return new Union([$objectlike]);
} }
if ($all_empty) {
return Type::getEmptyArray();
}
if ($inner_value_type) { if ($inner_value_type) {
if ($all_int_offsets) { if ($all_int_offsets) {
if ($any_nonempty) { if ($any_nonempty) {

View File

@ -336,6 +336,8 @@ function array_key_exists($key, array $array) : bool
/** /**
* @psalm-pure * @psalm-pure
* *
* @no-named-arguments
*
* @psalm-template TKey as array-key * @psalm-template TKey as array-key
* @psalm-template TValue * @psalm-template TValue
* *
@ -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 * @psalm-pure
* *

View File

@ -301,6 +301,20 @@ class ArrayFunctionCallTest extends TestCase
'$d===' => "list{string, ...<int<0, max>, int|string>}", '$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' => [ 'arrayReplaceIntArrays' => [
'code' => '<?php 'code' => '<?php
$d = array_replace(["a", "b", "c", "d"], [1, 2, 3]);', $d = array_replace(["a", "b", "c", "d"], [1, 2, 3]);',
@ -2119,7 +2133,7 @@ class ArrayFunctionCallTest extends TestCase
'arrayMapWithEmptyArrayReturn' => [ 'arrayMapWithEmptyArrayReturn' => [
'code' => '<?php 'code' => '<?php
/** /**
* @param array<array<string>> $elements * @param array<int, array<string>> $elements
* @return list<string> * @return list<string>
*/ */
function resolvePossibleFilePaths($elements) : array function resolvePossibleFilePaths($elements) : array
@ -2754,6 +2768,20 @@ class ArrayFunctionCallTest extends TestCase
array_combine(["a", "b"], [1, 2, 3]);', array_combine(["a", "b"], [1, 2, 3]);',
'error_message' => 'InvalidArgument', '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'
]
]; ];
} }
} }