Skip to content

Commit

Permalink
support removing class-strings from GenericClassStringType
Browse files Browse the repository at this point in the history
  • Loading branch information
staabm committed Aug 5, 2022
1 parent 1537424 commit 2ea6efe
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 0 deletions.
22 changes: 22 additions & 0 deletions src/Type/Generic/GenericClassStringType.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
use PHPStan\Type\Constant\ConstantStringType;
use PHPStan\Type\IntersectionType;
use PHPStan\Type\MixedType;
use PHPStan\Type\NeverType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\ObjectWithoutClassType;
use PHPStan\Type\StaticType;
use PHPStan\Type\StringType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
use PHPStan\Type\TypeWithClassName;
use PHPStan\Type\UnionType;
use PHPStan\Type\VerbosityLevel;
use function sprintf;
Expand Down Expand Up @@ -172,4 +174,24 @@ public static function __set_state(array $properties): Type
return new self($properties['type']);
}

public function tryRemove(Type $typeToRemove): ?Type
{
if ($typeToRemove instanceof ConstantStringType && $typeToRemove->isClassString()) {
$generic = $this->getGenericType();

if ($generic instanceof TypeWithClassName) {
$classReflection = $generic->getClassReflection();
if (
$classReflection !== null
&& $classReflection->isFinal()
&& $generic->getClassName() === $typeToRemove->getValue()
) {
return new NeverType();
}
}
}

return parent::tryRemove($typeToRemove);
}

}
1 change: 1 addition & 0 deletions tests/PHPStan/Analyser/NodeScopeResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -962,6 +962,7 @@ public function dataFileAsserts(): iterable
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Arrays/data/bug-5758.php');
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Functions/data/bug-3931.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-5223.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-7698.php');
}

/**
Expand Down
41 changes: 41 additions & 0 deletions tests/PHPStan/Analyser/data/bug-7698.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

namespace Bug7698;

declare(strict_types=1);

use function PHPStan\Testing\assertType;

final class A
{
}

final class B
{
}

final class Test
{
public function __construct(public readonly A|B $value)
{
}
}

function foo()
{
$t = new Test(new A());
$class = $t->value::class;
assertType('class-string<Bug7698\A>|class-string<Bug7698\B>', $class);

if ($class === A::class) {
return;
}

assertType('class-string<Bug7698\B>', $class);

if ($class === B::class) {
return;
}

assertType('*NEVER*', $class);
}
9 changes: 9 additions & 0 deletions tests/PHPStan/Rules/Comparison/MatchExpressionRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -224,4 +224,13 @@ public function testBug7622(): void
$this->analyse([__DIR__ . '/data/bug-7622.php'], []);
}

public function testBug7698(): void
{
if (PHP_VERSION_ID < 80100) {
$this->markTestSkipped('Test requires PHP 8.1.');
}
$this->treatPhpDocTypesAsCertain = false;
$this->analyse([__DIR__ . '/data/bug-7698.php'], []);
}

}
51 changes: 51 additions & 0 deletions tests/PHPStan/Rules/Comparison/data/bug-7698.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php // lint >= 8.1

namespace Bug7698Match;

final class A
{
}

final class B
{
}

final class C
{
}

final class Test
{
public function __construct(public readonly A|B $value)
{
}
}

function matchIt()
{
$t = new Test(new A());
$class = $t->value::class;
echo match ($class) {
A::class => 'A',
B::class => 'B'
};
}

function matchGetClassString()
{
$t = new Test(new A());
echo match (get_class($t->value)) {
A::class => 'A',
B::class => 'B'
};
}

function test(A|B|C $abc): string
{
$class = $abc::class;
return match ($class) {
A::class => 'A',
B::class => 'B',
C::class => 'C',
};
}

0 comments on commit 2ea6efe

Please sign in to comment.