Skip to content

Commit e8b3111

Browse files
committed
Fix array_map with accessory HasOffsetValueType
1 parent dcda566 commit e8b3111

File tree

2 files changed

+109
-3
lines changed

2 files changed

+109
-3
lines changed

src/Type/Php/ArrayMapFunctionReturnTypeExtension.php

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
use PHPStan\Node\Expr\TypeExpr;
1010
use PHPStan\Reflection\FunctionReflection;
1111
use PHPStan\Type\Accessory\AccessoryArrayListType;
12+
use PHPStan\Type\Accessory\AccessoryType;
13+
use PHPStan\Type\Accessory\HasOffsetValueType;
1214
use PHPStan\Type\Accessory\NonEmptyArrayType;
1315
use PHPStan\Type\ArrayType;
1416
use PHPStan\Type\Constant\ConstantArrayTypeBuilder;
@@ -149,13 +151,13 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
149151
$mappedArrayType = TypeCombinator::intersect(new ArrayType(
150152
$arrayType->getIterableKeyType(),
151153
$valueType,
152-
), ...TypeUtils::getAccessoryTypes($arrayType));
154+
), ...$this->getAccessoryTypes($arrayType, $valueType));
153155
}
154156
} elseif ($arrayType->isArray()->yes()) {
155157
$mappedArrayType = TypeCombinator::intersect(new ArrayType(
156158
$arrayType->getIterableKeyType(),
157159
$valueType,
158-
), ...TypeUtils::getAccessoryTypes($arrayType));
160+
), ...$this->getAccessoryTypes($arrayType, $valueType));
159161
} else {
160162
$mappedArrayType = new ArrayType(
161163
new MixedType(),
@@ -166,7 +168,7 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
166168
$mappedArrayType = TypeCombinator::intersect(new ArrayType(
167169
new IntegerType(),
168170
$valueType,
169-
), new AccessoryArrayListType(), ...TypeUtils::getAccessoryTypes($arrayType));
171+
), new AccessoryArrayListType(), ...$this->getAccessoryTypes($arrayType, $valueType));
170172
}
171173

172174
if ($arrayType->isIterableAtLeastOnce()->yes()) {
@@ -176,4 +178,22 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
176178
return $mappedArrayType;
177179
}
178180

181+
/**
182+
* @return AccessoryType[]
183+
*/
184+
private function getAccessoryTypes(Type $arrayType, Type $valueType): array
185+
{
186+
$accessoryTypes = [];
187+
foreach (TypeUtils::getAccessoryTypes($arrayType) as $accessoryType) {
188+
if (!$accessoryType instanceof HasOffsetValueType) {
189+
$accessoryTypes[] = $accessoryType;
190+
continue;
191+
}
192+
193+
$accessoryTypes[] = new HasOffsetValueType($accessoryType->getOffsetType(), $valueType);
194+
}
195+
196+
return $accessoryTypes;
197+
}
198+
179199
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Discussion13395;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
final readonly class View
8+
{
9+
public function __construct(
10+
public string $value,
11+
) {
12+
}
13+
}
14+
15+
class HelloWorld
16+
{
17+
/**
18+
* @param non-empty-list<array{
19+
* value: ?string,
20+
* }> $rows
21+
* @return non-empty-list<View>|null
22+
*/
23+
public function fetch(
24+
array $rows,
25+
) : ?array {
26+
return $this->buildViews($rows);
27+
}
28+
29+
/**
30+
* @param non-empty-list<array{
31+
* value: ?string,
32+
* }> $rows
33+
* @return non-empty-list<View>|null
34+
*/
35+
private function buildViews(array $rows) : ?array
36+
{
37+
if ($rows[0]['value'] === null) {
38+
return null;
39+
}
40+
41+
$views = \array_map(
42+
static function (array $row) : View {
43+
\assert($row['value'] !== null);
44+
return new View(
45+
$row['value'],
46+
);
47+
},
48+
$rows,
49+
);
50+
51+
assertType('non-empty-list<Discussion13395\\View>&hasOffsetValue(0, Discussion13395\View)', $views); // could be just non-empty-list<Discussion13395\\View>
52+
53+
return $views;
54+
}
55+
56+
/**
57+
* @param non-empty-list<array{
58+
* value: ?string,
59+
* }> $rows
60+
* @return non-empty-list<View>|null
61+
*/
62+
private function buildViews2(array $rows) : ?array
63+
{
64+
if ($rows[0]['value'] === null) {
65+
return null;
66+
}
67+
68+
if ($rows[1]['value'] === null) {
69+
return null;
70+
}
71+
72+
$views = \array_map(
73+
static function (array $row) : View {
74+
\assert($row['value'] !== null);
75+
return new View(
76+
$row['value'],
77+
);
78+
},
79+
$rows,
80+
);
81+
82+
assertType('non-empty-list<Discussion13395\View>&hasOffsetValue(0, Discussion13395\View)&hasOffsetValue(1, Discussion13395\View)', $views); // could be just &hasOffsetValue(1, Discussion13395\View)
83+
84+
return $views;
85+
}
86+
}

0 commit comments

Comments
 (0)