Skip to content

Commit

Permalink
Fixed intersecting and removal from TemplateUnionType
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Feb 5, 2022
1 parent ccffbc3 commit 0d28835
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 6 deletions.
20 changes: 17 additions & 3 deletions src/Type/Generic/TemplateTypeTrait.php
Expand Up @@ -5,8 +5,8 @@
use PHPStan\TrinaryLogic;
use PHPStan\Type\IntersectionType;
use PHPStan\Type\MixedType;
use PHPStan\Type\Traits\NonRemoveableTypeTrait;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use PHPStan\Type\UnionType;
use PHPStan\Type\VerbosityLevel;
use function sprintf;
Expand All @@ -17,8 +17,6 @@
trait TemplateTypeTrait
{

use NonRemoveableTypeTrait;

private string $name;

private TemplateTypeScope $scope;
Expand Down Expand Up @@ -198,6 +196,22 @@ protected function shouldGeneralizeInferredType(): bool
return true;
}

public function tryRemove(Type $typeToRemove): ?Type
{
$removedBound = TypeCombinator::remove($this->getBound(), $typeToRemove);
$type = TemplateTypeFactory::create(
$this->getScope(),
$this->getName(),
$removedBound,
$this->getVariance(),
);
if ($this->isArgument()) {
return TemplateTypeHelper::toArgument($type);
}

return $type;
}

/**
* @param mixed[] $properties
*/
Expand Down
1 change: 1 addition & 0 deletions tests/PHPStan/Analyser/NodeScopeResolverTest.php
Expand Up @@ -694,6 +694,7 @@ public function dataFileAsserts(): iterable
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Comparison/data/bug-6473.php');
}
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-6500.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-6566-types.php');
}

/**
Expand Down
49 changes: 49 additions & 0 deletions tests/PHPStan/Analyser/data/bug-6566-types.php
@@ -0,0 +1,49 @@
<?php

namespace Bug6566Types;

use function PHPStan\Testing\assertType;

class A {
public string $name;
}

class B {
public string $name;
}

class C {

}

/**
* @template T of A|B|C
*/
abstract class HelloWorld
{
public function sayHelloBug(): void
{
$object = $this->getObject();
assertType('T of Bug6566Types\A|Bug6566Types\B|Bug6566Types\C (class Bug6566Types\HelloWorld, argument)', $object);
if ($object instanceof C) {
assertType('T of Bug6566Types\C (class Bug6566Types\HelloWorld, argument)', $object);
return;
}
assertType('T of Bug6566Types\A|Bug6566Types\B (class Bug6566Types\HelloWorld, argument)', $object);
if ($object instanceof B) {
assertType('T of Bug6566Types\B (class Bug6566Types\HelloWorld, argument)', $object);
return;
}
assertType('T of Bug6566Types\A (class Bug6566Types\HelloWorld, argument)', $object);
if ($object instanceof A) {
assertType('T of Bug6566Types\A (class Bug6566Types\HelloWorld, argument)', $object);
return;
}
assertType('*NEVER*', $object);
}

/**
* @return T
*/
abstract protected function getObject(): A|B|C;
}
2 changes: 1 addition & 1 deletion tests/PHPStan/Analyser/data/generics.php
Expand Up @@ -1089,7 +1089,7 @@ function testGenericObjectWithoutClassType2($a)
return $a;
}

assertType('T of object (function PHPStan\Generics\FunctionsAssertType\testGenericObjectWithoutClassType2(), argument)', $b);
assertType('T of object~stdClass (function PHPStan\Generics\FunctionsAssertType\testGenericObjectWithoutClassType2(), argument)', $b);

return $a;
}
Expand Down
7 changes: 7 additions & 0 deletions tests/PHPStan/Rules/Properties/AccessPropertiesRuleTest.php
Expand Up @@ -543,4 +543,11 @@ public function testBug6385(): void
]);
}

public function testBug6566(): void
{
$this->checkThisOnly = false;
$this->checkUnionTypes = true;
$this->analyse([__DIR__ . '/data/bug-6566.php'], []);
}

}
34 changes: 34 additions & 0 deletions tests/PHPStan/Rules/Properties/data/bug-6566.php
@@ -0,0 +1,34 @@
<?php

namespace Bug6566;

class A {
public string $name;
}

class B {
public string $name;
}

class C {

}

/**
* @template T of A|B|C
*/
abstract class HelloWorld
{
public function sayHelloBug(): void
{
$object = $this->getObject();
if (!$object instanceof C) {
echo $object->name;
}
}

/**
* @return T
*/
abstract protected function getObject(): A|B|C;
}
4 changes: 2 additions & 2 deletions tests/PHPStan/Type/TypeCombinatorTest.php
Expand Up @@ -3914,8 +3914,8 @@ public function dataRemove(): array
TemplateTypeVariance::createInvariant(),
),
new ConstantBooleanType(false),
TemplateBooleanType::class,
'T of bool (class Foo, parameter)',
TemplateMixedType::class, // should be TemplateConstantBooleanType
'T (class Foo, parameter)', // should be T of true
],
];
}
Expand Down

0 comments on commit 0d28835

Please sign in to comment.