Skip to content

Commit

Permalink
Fix generic variance with BenevolentUnionType
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Mar 7, 2021
1 parent 9f51f8e commit 49dcc50
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 5 deletions.
14 changes: 9 additions & 5 deletions src/Type/Generic/TemplateTypeVariance.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,16 @@ public function isValidVariance(Type $a, Type $b): TrinaryLogic
return TrinaryLogic::createYes();
}

if ($b instanceof BenevolentUnionType && !$a instanceof BenevolentUnionType) {
$results = [];
foreach ($b->getTypes() as $innerType) {
$results[] = $this->isValidVariance($a, $innerType);
if ($a instanceof BenevolentUnionType) {
if (!$a->isSuperTypeOf($b)->no()) {
return TrinaryLogic::createYes();
}
}

if ($b instanceof BenevolentUnionType) {
if (!$b->isSuperTypeOf($a)->no()) {
return TrinaryLogic::createYes();
}
return TrinaryLogic::maxMin(...$results);
}

if ($b instanceof MixedType && !$b instanceof TemplateType) {
Expand Down
101 changes: 101 additions & 0 deletions tests/PHPStan/Type/Generic/TemplateTypeVarianceTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<?php declare(strict_types = 1);

namespace PHPStan\Type\Generic;

use PHPStan\TrinaryLogic;
use PHPStan\Type\BenevolentUnionType;
use PHPStan\Type\IntegerType;
use PHPStan\Type\StringType;
use PHPStan\Type\Type;
use PHPStan\Type\UnionType;
use PHPStan\Type\VerbosityLevel;
use PHPUnit\Framework\TestCase;

class TemplateTypeVarianceTest extends TestCase
{

public function dataIsValidVariance(): iterable
{
foreach ([TemplateTypeVariance::createInvariant(), TemplateTypeVariance::createCovariant()] as $variance) {
yield [
$variance,
new BenevolentUnionType([new IntegerType(), new StringType()]),
new BenevolentUnionType([new IntegerType(), new StringType()]),
TrinaryLogic::createYes(),
TrinaryLogic::createYes(),
];

yield [
$variance,
new IntegerType(),
new BenevolentUnionType([new IntegerType(), new StringType()]),
TrinaryLogic::createYes(),
TrinaryLogic::createYes(),
];

yield [
$variance,
new BenevolentUnionType([new IntegerType(), new StringType()]),
new IntegerType(),
TrinaryLogic::createYes(),
TrinaryLogic::createYes(),
];

yield [
$variance,
new StringType(),
new BenevolentUnionType([new IntegerType(), new StringType()]),
TrinaryLogic::createYes(),
TrinaryLogic::createYes(),
];

yield [
$variance,
new BenevolentUnionType([new IntegerType(), new StringType()]),
new StringType(),
TrinaryLogic::createYes(),
TrinaryLogic::createYes(),
];

yield [
$variance,
new BenevolentUnionType([new IntegerType(), new StringType()]),
new UnionType([new IntegerType(), new StringType()]),
TrinaryLogic::createYes(),
TrinaryLogic::createYes(),
];

yield [
$variance,
new UnionType([new IntegerType(), new StringType()]),
new BenevolentUnionType([new IntegerType(), new StringType()]),
TrinaryLogic::createYes(),
TrinaryLogic::createYes(),
];
}
}

/**
* @dataProvider dataIsValidVariance
*/
public function testIsValidVariance(
TemplateTypeVariance $variance,
Type $a,
Type $b,
TrinaryLogic $expected,
TrinaryLogic $expectedInversed
): void
{
$this->assertSame(
$expected->describe(),
$variance->isValidVariance($a, $b)->describe(),
sprintf('%s->isValidVariance(%s, %s)', $variance->describe(), $a->describe(VerbosityLevel::precise()), $b->describe(VerbosityLevel::precise()))
);
$this->assertSame(
$expectedInversed->describe(),
$variance->isValidVariance($b, $a)->describe(),
sprintf('%s->isValidVariance(%s, %s)', $variance->describe(), $b->describe(VerbosityLevel::precise()), $a->describe(VerbosityLevel::precise()))
);
}

}

0 comments on commit 49dcc50

Please sign in to comment.