Skip to content

Commit a50b75a

Browse files
rvanvelzenondrejmirtes
authored andcommitted
Fix conditional types in array_map() return value
1 parent 9815bbb commit a50b75a

File tree

3 files changed

+57
-7
lines changed

3 files changed

+57
-7
lines changed

src/Type/Php/ArrayMapFunctionReturnTypeExtension.php

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22

33
namespace PHPStan\Type\Php;
44

5+
use PhpParser\Node;
56
use PhpParser\Node\Expr\FuncCall;
67
use PHPStan\Analyser\Scope;
78
use PHPStan\Reflection\FunctionReflection;
9+
use PHPStan\Reflection\ParametersAcceptorSelector;
810
use PHPStan\Type\Accessory\AccessoryArrayListType;
911
use PHPStan\Type\Accessory\NonEmptyArrayType;
1012
use PHPStan\Type\ArrayType;
@@ -13,10 +15,10 @@
1315
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
1416
use PHPStan\Type\IntegerType;
1517
use PHPStan\Type\MixedType;
16-
use PHPStan\Type\NeverType;
1718
use PHPStan\Type\Type;
1819
use PHPStan\Type\TypeCombinator;
1920
use PHPStan\Type\TypeUtils;
21+
use function array_map;
2022
use function array_slice;
2123
use function count;
2224

@@ -38,12 +40,18 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
3840
$callableType = $scope->getType($functionCall->getArgs()[0]->value);
3941
$callableIsNull = $callableType->isNull()->yes();
4042

43+
$callableParametersAcceptors = null;
44+
4145
if ($callableType->isCallable()->yes()) {
42-
$valueTypes = [new NeverType()];
43-
foreach ($callableType->getCallableParametersAcceptors($scope) as $parametersAcceptor) {
44-
$valueTypes[] = $parametersAcceptor->getReturnType();
45-
}
46-
$valueType = TypeCombinator::union(...$valueTypes);
46+
$callableParametersAcceptors = $callableType->getCallableParametersAcceptors($scope);
47+
$valueType = ParametersAcceptorSelector::selectFromTypes(
48+
array_map(
49+
static fn (Node\Arg $arg) => $scope->getType($arg->value)->getIterableValueType(),
50+
array_slice($functionCall->getArgs(), 1),
51+
),
52+
$callableParametersAcceptors,
53+
false,
54+
)->getReturnType();
4755
} elseif ($callableIsNull) {
4856
$arrayBuilder = ConstantArrayTypeBuilder::createEmpty();
4957
foreach (array_slice($functionCall->getArgs(), 1) as $index => $arg) {
@@ -70,10 +78,17 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
7078
if ($totalCount < ConstantArrayTypeBuilder::ARRAY_COUNT_LIMIT) {
7179
foreach ($constantArrays as $constantArray) {
7280
$returnedArrayBuilder = ConstantArrayTypeBuilder::createEmpty();
81+
$valueTypes = $constantArray->getValueTypes();
7382
foreach ($constantArray->getKeyTypes() as $i => $keyType) {
7483
$returnedArrayBuilder->setOffsetValueType(
7584
$keyType,
76-
$valueType,
85+
$callableParametersAcceptors !== null
86+
? ParametersAcceptorSelector::selectFromTypes(
87+
[$valueTypes[$i]],
88+
$callableParametersAcceptors,
89+
false,
90+
)->getReturnType()
91+
: $valueType,
7792
$constantArray->isOptionalKey($i),
7893
);
7994
}

tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,4 +1049,9 @@ public function testBug11337(): void
10491049
$this->analyse([__DIR__ . '/data/bug-11337.php'], []);
10501050
}
10511051

1052+
public function testBug10715(): void
1053+
{
1054+
$this->analyse([__DIR__ . '/data/bug-10715.php'], []);
1055+
}
1056+
10521057
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug10715;
4+
5+
class Test
6+
{
7+
/**
8+
* @param string|array<string> $word
9+
*
10+
* @return ($word is array<string> ? array<string> : string)
11+
*/
12+
public static function wgtrim(string|array $word): string|array
13+
{
14+
if (\is_array($word)) {
15+
return array_map(static::wgtrim(...), $word);
16+
}
17+
18+
return 'word';
19+
}
20+
21+
/**
22+
* @param array{foo: array<string>, bar: string} $array
23+
*
24+
* @return array{foo: array<string>, bar: string}
25+
*/
26+
public static function example(array $array): array
27+
{
28+
return array_map(static::wgtrim(...), $array);
29+
}
30+
}

0 commit comments

Comments
 (0)