Skip to content

Commit a1b7b38

Browse files
committed
Eliminate non-generic types before inferring from unions
1 parent 46d4118 commit a1b7b38

File tree

3 files changed

+90
-1
lines changed

3 files changed

+90
-1
lines changed

src/Type/UnionType.php

+21-1
Original file line numberDiff line numberDiff line change
@@ -554,8 +554,28 @@ public function toArray(): Type
554554
public function inferTemplateTypes(Type $receivedType): TemplateTypeMap
555555
{
556556
$types = TemplateTypeMap::createEmpty();
557+
if ($receivedType instanceof UnionType) {
558+
$myTypes = [];
559+
$remainingReceivedTypes = [];
560+
foreach ($receivedType->getTypes() as $receivedInnerType) {
561+
foreach ($this->types as $type) {
562+
if ($type->isSuperTypeOf($receivedInnerType)->yes()) {
563+
$types = $types->union($type->inferTemplateTypes($receivedInnerType));
564+
continue 2;
565+
}
566+
$myTypes[] = $type;
567+
}
568+
$remainingReceivedTypes[] = $receivedInnerType;
569+
}
570+
if (count($remainingReceivedTypes) === 0) {
571+
return $types;
572+
}
573+
$receivedType = TypeCombinator::union(...$remainingReceivedTypes);
574+
} else {
575+
$myTypes = $this->types;
576+
}
557577

558-
foreach ($this->types as $type) {
578+
foreach ($myTypes as $type) {
559579
$types = $types->union($type->inferTemplateTypes($receivedType));
560580
}
561581

tests/PHPStan/Analyser/NodeScopeResolverTest.php

+6
Original file line numberDiff line numberDiff line change
@@ -5646,6 +5646,11 @@ public function dataBug4423(): array
56465646
return $this->gatherAssertTypes(__DIR__ . '/data/bug-4423.php');
56475647
}
56485648

5649+
public function dataGenericUnions(): array
5650+
{
5651+
return $this->gatherAssertTypes(__DIR__ . '/data/generic-unions.php');
5652+
}
5653+
56495654
/**
56505655
* @dataProvider dataArrayFunctions
56515656
* @param string $description
@@ -11258,6 +11263,7 @@ private function gatherAssertTypes(string $file): array
1125811263
* @dataProvider dataPseudoTypeOverrides
1125911264
* @dataProvider dataGenericTraits
1126011265
* @dataProvider dataBug4423
11266+
* @dataProvider dataGenericUnions
1126111267
* @param string $assertType
1126211268
* @param string $file
1126311269
* @param mixed ...$args
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
3+
namespace GenericUnions;
4+
5+
use function PHPStan\Analyser\assertType;
6+
7+
class Foo
8+
{
9+
10+
/**
11+
* @template T
12+
* @param T|null $p
13+
* @return T
14+
*/
15+
public function doFoo($p)
16+
{
17+
if ($p === null) {
18+
throw new \Exception();
19+
}
20+
21+
return $p;
22+
}
23+
24+
/**
25+
* @template T
26+
* @param T $p
27+
* @return T
28+
*/
29+
public function doBar($p)
30+
{
31+
return $p;
32+
}
33+
34+
/**
35+
* @template T
36+
* @param T|int|float $p
37+
* @return T
38+
*/
39+
public function doBaz($p)
40+
{
41+
return $p;
42+
}
43+
44+
/**
45+
* @param int|string $stringOrInt
46+
*/
47+
public function foo(
48+
?string $nullableString,
49+
$stringOrInt
50+
): void
51+
{
52+
assertType('string', $this->doFoo($nullableString));
53+
assertType('int|string', $this->doFoo($stringOrInt));
54+
55+
assertType('string|null', $this->doBar($nullableString));
56+
57+
assertType('int', $this->doBaz(1));
58+
assertType('string', $this->doBaz('foo'));
59+
assertType('float', $this->doBaz(1.2));
60+
assertType('string', $this->doBaz($stringOrInt));
61+
}
62+
63+
}

0 commit comments

Comments
 (0)