Skip to content

Commit 7dcd25b

Browse files
committed
Fix union of template array-key with false losing the template
TypeCombinator flattened TemplateBenevolentUnionType into int|string before adding other members, so TKey|false became int|string|false instead of substituting TKey.
1 parent 57fe204 commit 7dcd25b

3 files changed

Lines changed: 45 additions & 7 deletions

File tree

‎src/Type/TypeCombinator.php‎

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,6 @@ public static function doUnion(Type ...$types): Type
256256
$alreadyNormalizedCounter = 0;
257257

258258
$benevolentTypes = [];
259-
$benevolentUnionObject = null;
260259
$neverCount = 0;
261260
// transform A | (B | C) to A | B | C
262261
for ($i = 0; $i < $typesCount; $i++) {
@@ -273,8 +272,8 @@ public static function doUnion(Type ...$types): Type
273272
continue;
274273
}
275274
if ($types[$i] instanceof BenevolentUnionType) {
276-
if ($types[$i] instanceof TemplateBenevolentUnionType && $benevolentUnionObject === null) {
277-
$benevolentUnionObject = $types[$i];
275+
if ($types[$i] instanceof TemplateType) {
276+
continue;
278277
}
279278
$benevolentTypesCount = 0;
280279
$typesInner = $types[$i]->getTypes();
@@ -528,10 +527,6 @@ public static function doUnion(Type ...$types): Type
528527
}
529528

530529
if ($tempTypes === []) {
531-
if ($benevolentUnionObject instanceof TemplateBenevolentUnionType) {
532-
return $benevolentUnionObject->withTypes(array_values($types));
533-
}
534-
535530
return new BenevolentUnionType(array_values($types), true);
536531
}
537532
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
namespace TemplateArrayKeyUnionFalse;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
/**
8+
* @template TKey of array-key
9+
* @template TValue
10+
*/
11+
class Collection
12+
{
13+
14+
/**
15+
* @param TValue|callable(TValue, TKey): bool $value
16+
* @return TKey|false
17+
*/
18+
public function search($value, bool $strict = false)
19+
{
20+
return false;
21+
}
22+
23+
}
24+
25+
/**
26+
* @param Collection<int, string> $ints
27+
* @param Collection<string, string> $strings
28+
* @param Collection<int|string, string> $keys
29+
*/
30+
function test(Collection $ints, Collection $strings, Collection $keys): void
31+
{
32+
assertType('int|false', $ints->search('foo'));
33+
assertType('string|false', $strings->search('foo'));
34+
assertType('int|string|false', $keys->search('foo'));
35+
}

‎tests/PHPStan/Type/TypeCombinatorTest.php‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1982,6 +1982,14 @@ public static function dataUnion(): iterable
19821982
TemplateBenevolentUnionType::class,
19831983
'T of (int|string) (function foo(), parameter)',
19841984
],
1985+
[
1986+
[
1987+
TemplateTypeFactory::create(TemplateTypeScope::createWithFunction('foo'), 'T', new BenevolentUnionType([new IntegerType(), new StringType()]), TemplateTypeVariance::createInvariant()),
1988+
new ConstantBooleanType(false),
1989+
],
1990+
UnionType::class,
1991+
'T of (int|string) (function foo(), parameter)|false',
1992+
],
19851993
[
19861994
[
19871995
new ConstantStringType(''),

0 commit comments

Comments
 (0)