Skip to content

Commit 49dcc50

Browse files
committed
Fix generic variance with BenevolentUnionType
1 parent 9f51f8e commit 49dcc50

File tree

2 files changed

+110
-5
lines changed

2 files changed

+110
-5
lines changed

src/Type/Generic/TemplateTypeVariance.php

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -102,12 +102,16 @@ public function isValidVariance(Type $a, Type $b): TrinaryLogic
102102
return TrinaryLogic::createYes();
103103
}
104104

105-
if ($b instanceof BenevolentUnionType && !$a instanceof BenevolentUnionType) {
106-
$results = [];
107-
foreach ($b->getTypes() as $innerType) {
108-
$results[] = $this->isValidVariance($a, $innerType);
105+
if ($a instanceof BenevolentUnionType) {
106+
if (!$a->isSuperTypeOf($b)->no()) {
107+
return TrinaryLogic::createYes();
108+
}
109+
}
110+
111+
if ($b instanceof BenevolentUnionType) {
112+
if (!$b->isSuperTypeOf($a)->no()) {
113+
return TrinaryLogic::createYes();
109114
}
110-
return TrinaryLogic::maxMin(...$results);
111115
}
112116

113117
if ($b instanceof MixedType && !$b instanceof TemplateType) {
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Type\Generic;
4+
5+
use PHPStan\TrinaryLogic;
6+
use PHPStan\Type\BenevolentUnionType;
7+
use PHPStan\Type\IntegerType;
8+
use PHPStan\Type\StringType;
9+
use PHPStan\Type\Type;
10+
use PHPStan\Type\UnionType;
11+
use PHPStan\Type\VerbosityLevel;
12+
use PHPUnit\Framework\TestCase;
13+
14+
class TemplateTypeVarianceTest extends TestCase
15+
{
16+
17+
public function dataIsValidVariance(): iterable
18+
{
19+
foreach ([TemplateTypeVariance::createInvariant(), TemplateTypeVariance::createCovariant()] as $variance) {
20+
yield [
21+
$variance,
22+
new BenevolentUnionType([new IntegerType(), new StringType()]),
23+
new BenevolentUnionType([new IntegerType(), new StringType()]),
24+
TrinaryLogic::createYes(),
25+
TrinaryLogic::createYes(),
26+
];
27+
28+
yield [
29+
$variance,
30+
new IntegerType(),
31+
new BenevolentUnionType([new IntegerType(), new StringType()]),
32+
TrinaryLogic::createYes(),
33+
TrinaryLogic::createYes(),
34+
];
35+
36+
yield [
37+
$variance,
38+
new BenevolentUnionType([new IntegerType(), new StringType()]),
39+
new IntegerType(),
40+
TrinaryLogic::createYes(),
41+
TrinaryLogic::createYes(),
42+
];
43+
44+
yield [
45+
$variance,
46+
new StringType(),
47+
new BenevolentUnionType([new IntegerType(), new StringType()]),
48+
TrinaryLogic::createYes(),
49+
TrinaryLogic::createYes(),
50+
];
51+
52+
yield [
53+
$variance,
54+
new BenevolentUnionType([new IntegerType(), new StringType()]),
55+
new StringType(),
56+
TrinaryLogic::createYes(),
57+
TrinaryLogic::createYes(),
58+
];
59+
60+
yield [
61+
$variance,
62+
new BenevolentUnionType([new IntegerType(), new StringType()]),
63+
new UnionType([new IntegerType(), new StringType()]),
64+
TrinaryLogic::createYes(),
65+
TrinaryLogic::createYes(),
66+
];
67+
68+
yield [
69+
$variance,
70+
new UnionType([new IntegerType(), new StringType()]),
71+
new BenevolentUnionType([new IntegerType(), new StringType()]),
72+
TrinaryLogic::createYes(),
73+
TrinaryLogic::createYes(),
74+
];
75+
}
76+
}
77+
78+
/**
79+
* @dataProvider dataIsValidVariance
80+
*/
81+
public function testIsValidVariance(
82+
TemplateTypeVariance $variance,
83+
Type $a,
84+
Type $b,
85+
TrinaryLogic $expected,
86+
TrinaryLogic $expectedInversed
87+
): void
88+
{
89+
$this->assertSame(
90+
$expected->describe(),
91+
$variance->isValidVariance($a, $b)->describe(),
92+
sprintf('%s->isValidVariance(%s, %s)', $variance->describe(), $a->describe(VerbosityLevel::precise()), $b->describe(VerbosityLevel::precise()))
93+
);
94+
$this->assertSame(
95+
$expectedInversed->describe(),
96+
$variance->isValidVariance($b, $a)->describe(),
97+
sprintf('%s->isValidVariance(%s, %s)', $variance->describe(), $b->describe(VerbosityLevel::precise()), $a->describe(VerbosityLevel::precise()))
98+
);
99+
}
100+
101+
}

0 commit comments

Comments
 (0)