Skip to content

Commit d0e7aca

Browse files
schlndhondrejmirtes
authored andcommitted
don't add null for array_map(null, $a, $a)
1 parent 8038a29 commit d0e7aca

File tree

2 files changed

+32
-10
lines changed

2 files changed

+32
-10
lines changed

src/Type/Php/ArrayMapFunctionReturnTypeExtension.php

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use PHPStan\Type\TypeCombinator;
2020
use PHPStan\Type\TypeUtils;
2121
use function array_map;
22+
use function array_reduce;
2223
use function array_slice;
2324
use function count;
2425

@@ -55,19 +56,18 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
5556
)->getReturnType();
5657
} elseif ($callableIsNull) {
5758
$arrayBuilder = ConstantArrayTypeBuilder::createEmpty();
58-
$argIterableValueTypes = [];
59-
$addNull = false;
59+
$argTypes = [];
60+
$areAllSameSize = true;
6061
$expectedSize = null;
6162
foreach (array_slice($functionCall->getArgs(), 1) as $index => $arg) {
62-
$argType = $scope->getType($arg->value);
63-
$argIterableValueTypes[$index] = $argType->getIterableValueType();
64-
if ($addNull) {
63+
$argTypes[$index] = $argType = $scope->getType($arg->value);
64+
if (!$areAllSameSize || $numArgs === 2) {
6565
continue;
6666
}
6767

6868
$arraySizes = $argType->getArraySize()->getConstantScalarValues();
6969
if ($arraySizes === []) {
70-
$addNull = true;
70+
$areAllSameSize = false;
7171
continue;
7272
}
7373

@@ -77,12 +77,30 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
7777
continue;
7878
}
7979

80-
$addNull = true;
81-
break;
80+
$areAllSameSize = false;
81+
continue 2;
8282
}
8383
}
8484

85-
foreach ($argIterableValueTypes as $index => $offsetValueType) {
85+
if (!$areAllSameSize) {
86+
$firstArr = $functionCall->getArgs()[1]->value;
87+
$identities = [];
88+
foreach (array_slice($functionCall->getArgs(), 2) as $arg) {
89+
$identities[] = new Node\Expr\BinaryOp\Identical($firstArr, $arg->value);
90+
}
91+
92+
$and = array_reduce(
93+
$identities,
94+
static fn (Node\Expr $a, Node\Expr $b) => new Node\Expr\BinaryOp\BooleanAnd($a, $b),
95+
new Node\Expr\ConstFetch(new Node\Name('true')),
96+
);
97+
$areAllSameSize = $scope->getType($and)->isTrue()->yes();
98+
}
99+
100+
$addNull = !$areAllSameSize;
101+
102+
foreach ($argTypes as $index => $argType) {
103+
$offsetValueType = $argType->getIterableValueType();
86104
if ($addNull) {
87105
$offsetValueType = TypeCombinator::addNull($offsetValueType);
88106
}

tests/PHPStan/Analyser/nsrt/array_map_multiple.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,12 @@ public function arrayMapNull(array $array, array $other): void
2929
assertType('non-empty-array<int, array{1|2|3, 4|5|6}>', array_map(null, [1, 2, 3], [4, 5, 6]));
3030

3131
assertType('non-empty-array<string, int>', array_map(null, $array));
32-
assertType('non-empty-array<int, array{int|null, int|null}>', array_map(null, $array, $array));
32+
assertType('non-empty-array<int, array{int, int}>', array_map(null, $array, $array));
33+
assertType('non-empty-array<int, array{int, int, int}>', array_map(null, $array, $array, $array));
3334
assertType('non-empty-array<int, array{int|null, bool|null}>', array_map(null, $array, $other));
35+
36+
assertType('array{1}|array{true}', array_map(null, rand() ? [1] : [true]));
37+
assertType('array{1}|array{true, false}', array_map(null, rand() ? [1] : [true, false]));
3438
}
3539

3640
}

0 commit comments

Comments
 (0)