Skip to content

Commit 0c8cd2a

Browse files
Handle conditional return type in ArrayMap
1 parent 8c4cb2f commit 0c8cd2a

File tree

3 files changed

+64
-5
lines changed

3 files changed

+64
-5
lines changed

src/Type/Php/ArrayMapFunctionReturnTypeExtension.php

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use PHPStan\Type\Accessory\AccessoryArrayListType;
99
use PHPStan\Type\Accessory\NonEmptyArrayType;
1010
use PHPStan\Type\ArrayType;
11+
use PHPStan\Type\ConditionalTypeForParameter;
1112
use PHPStan\Type\Constant\ConstantArrayTypeBuilder;
1213
use PHPStan\Type\Constant\ConstantIntegerType;
1314
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
@@ -17,6 +18,7 @@
1718
use PHPStan\Type\Type;
1819
use PHPStan\Type\TypeCombinator;
1920
use PHPStan\Type\TypeUtils;
21+
use PHPStan\Type\UnionType;
2022
use function array_slice;
2123
use function count;
2224

@@ -73,7 +75,7 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
7375
foreach ($constantArray->getKeyTypes() as $i => $keyType) {
7476
$returnedArrayBuilder->setOffsetValueType(
7577
$keyType,
76-
$valueType,
78+
$this->mapValueType($constantArray->getOffsetValueType($keyType), $valueType),
7779
$constantArray->isOptionalKey($i),
7880
);
7981
}
@@ -88,24 +90,24 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
8890
} else {
8991
$mappedArrayType = TypeCombinator::intersect(new ArrayType(
9092
$arrayType->getIterableKeyType(),
91-
$valueType,
93+
$this->mapValueType($arrayType->getIterableValueType(), $valueType),
9294
), ...TypeUtils::getAccessoryTypes($arrayType));
9395
}
9496
} elseif ($arrayType->isArray()->yes()) {
9597
$mappedArrayType = TypeCombinator::intersect(new ArrayType(
9698
$arrayType->getIterableKeyType(),
97-
$valueType,
99+
$this->mapValueType($arrayType->getIterableValueType(), $valueType),
98100
), ...TypeUtils::getAccessoryTypes($arrayType));
99101
} else {
100102
$mappedArrayType = new ArrayType(
101103
new MixedType(),
102-
$valueType,
104+
$this->mapValueType($arrayType->getIterableValueType(), $valueType),
103105
);
104106
}
105107
} else {
106108
$mappedArrayType = TypeCombinator::intersect(new ArrayType(
107109
new IntegerType(),
108-
$valueType,
110+
$this->mapValueType($arrayType->getIterableValueType(), $valueType),
109111
), ...TypeUtils::getAccessoryTypes($arrayType));
110112
}
111113

@@ -116,4 +118,26 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
116118
return $mappedArrayType;
117119
}
118120

121+
private function mapValueType(Type $initialValue, Type $returnType): Type
122+
{
123+
$newValues = [];
124+
if ($returnType instanceof UnionType) {
125+
foreach ($returnType->getTypes() as $type) {
126+
if ($type instanceof ConditionalTypeForParameter) {
127+
$newValues[] = $type->toConditional($initialValue);
128+
} else {
129+
$newValues[] = $type;
130+
}
131+
}
132+
133+
return TypeCombinator::union(...$newValues);
134+
}
135+
136+
if ($returnType instanceof ConditionalTypeForParameter) {
137+
return $returnType->toConditional($initialValue);
138+
}
139+
140+
return $returnType;
141+
}
142+
119143
}

tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1044,6 +1044,11 @@ public function testBug3759(): void
10441044
$this->analyse([__DIR__ . '/data/bug-3759.php'], []);
10451045
}
10461046

1047+
public function testBug10715(): void
1048+
{
1049+
$this->analyse([__DIR__ . '/data/bug-10715.php'], []);
1050+
}
1051+
10471052
public function testBug11337(): void
10481053
{
10491054
$this->analyse([__DIR__ . '/data/bug-11337.php'], []);
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)