Skip to content

Commit 1b47baf

Browse files
authored
Fix ClassConstFetch Identical specification with Yoda conditions
1 parent 874e5a4 commit 1b47baf

File tree

6 files changed

+90
-3
lines changed

6 files changed

+90
-3
lines changed

phpstan-baseline.neon

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ parameters:
8686

8787
-
8888
message: "#^Doing instanceof PHPStan\\\\Type\\\\Constant\\\\ConstantStringType is error\\-prone and deprecated\\. Use Type\\:\\:getConstantStrings\\(\\) instead\\.$#"
89-
count: 2
89+
count: 3
9090
path: src/Analyser/TypeSpecifier.php
9191

9292
-

src/Analyser/TypeSpecifier.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ public function specifyTypesInCondition(
253253
}
254254

255255
if (
256+
$context->true() &&
256257
$leftExpr instanceof ClassConstFetch &&
257258
$leftExpr->class instanceof Expr &&
258259
$leftExpr->name instanceof Node\Identifier &&
@@ -270,6 +271,28 @@ public function specifyTypesInCondition(
270271
$rootExpr,
271272
)->unionWith($this->create($expr->left, $rightType, $context, false, $scope, $rootExpr));
272273
}
274+
275+
$leftType = $scope->getType($leftExpr);
276+
if (
277+
$context->true() &&
278+
$rightExpr instanceof ClassConstFetch &&
279+
$rightExpr->class instanceof Expr &&
280+
$rightExpr->name instanceof Node\Identifier &&
281+
$leftExpr instanceof ClassConstFetch &&
282+
$leftType instanceof ConstantStringType &&
283+
strtolower($rightExpr->name->toString()) === 'class'
284+
) {
285+
return $this->specifyTypesInCondition(
286+
$scope,
287+
new Instanceof_(
288+
$rightExpr->class,
289+
new Name($leftType->getValue()),
290+
),
291+
$context,
292+
$rootExpr,
293+
)->unionWith($this->create($expr->right, $leftType, $context, false, $scope, $rootExpr));
294+
}
295+
273296
if ($context->false()) {
274297
$identicalType = $scope->getType($expr);
275298
if ($identicalType instanceof ConstantBooleanType) {

tests/PHPStan/Analyser/NodeScopeResolverTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1302,6 +1302,7 @@ public function dataFileAsserts(): iterable
13021302

13031303
yield from $this->gatherAssertTypes(__DIR__ . '/data/gettype.php');
13041304
yield from $this->gatherAssertTypes(__DIR__ . '/data/array_splice.php');
1305+
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Methods/data/bug-9542.php');
13051306
}
13061307

13071308
/**

tests/PHPStan/Analyser/data/bug-5843.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ function doFoo(object $object): void
1515
assertType(\DateTime::class, $object);
1616
break;
1717
case \Throwable::class:
18-
assertType('Throwable~DateTime', $object);
18+
assertType('Throwable', $object);
1919
break;
2020
}
2121
}
@@ -29,7 +29,7 @@ function doFoo(object $object): void
2929
{
3030
match ($object::class) {
3131
\DateTime::class => assertType(\DateTime::class, $object),
32-
\Throwable::class => assertType('Throwable~DateTime', $object),
32+
\Throwable::class => assertType('Throwable', $object),
3333
};
3434
}
3535

tests/PHPStan/Rules/Methods/CallMethodsRuleTest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2911,6 +2911,15 @@ public function testBug8888(): void
29112911
$this->analyse([__DIR__ . '/data/bug-8888.php'], []);
29122912
}
29132913

2914+
public function testBug9542(): void
2915+
{
2916+
$this->checkThisOnly = false;
2917+
$this->checkNullables = true;
2918+
$this->checkUnionTypes = true;
2919+
$this->checkExplicitMixed = true;
2920+
$this->analyse([__DIR__ . '/data/bug-9542.php'], []);
2921+
}
2922+
29142923
public function testTrickyCallables(): void
29152924
{
29162925
$this->checkThisOnly = false;
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug9542;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
interface TranslatableInterface
8+
{
9+
10+
}
11+
12+
class TranslatableMessage implements TranslatableInterface
13+
{
14+
public function getMessage(): void
15+
{
16+
return;
17+
}
18+
}
19+
20+
class HelloWorld
21+
{
22+
public function testInstanceOf(TranslatableInterface $translatable): void
23+
{
24+
if (! $translatable instanceof TranslatableMessage) {
25+
assertType('Bug9542\TranslatableInterface~Bug9542\TranslatableMessage', $translatable);
26+
return;
27+
}
28+
29+
assertType('Bug9542\TranslatableMessage', $translatable);
30+
$translatable->getMessage();
31+
}
32+
33+
public function testClass(TranslatableInterface $translatable): void
34+
{
35+
if ($translatable::class !== TranslatableMessage::class) {
36+
assertType('Bug9542\TranslatableInterface', $translatable);
37+
return;
38+
}
39+
40+
assertType('Bug9542\TranslatableMessage', $translatable);
41+
$translatable->getMessage();
42+
}
43+
44+
public function testClassReverse(TranslatableInterface $translatable): void
45+
{
46+
if (TranslatableMessage::class !== $translatable::class) {
47+
assertType('Bug9542\TranslatableInterface', $translatable);
48+
return;
49+
}
50+
51+
assertType('Bug9542\TranslatableMessage', $translatable);
52+
$translatable->getMessage();
53+
}
54+
}

0 commit comments

Comments
 (0)