Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 31a5ad8

Browse files
Avoid false error on is_subclass_of
1 parent 9405233 commit 31a5ad8

File tree

7 files changed

+79
-13
lines changed

7 files changed

+79
-13
lines changed

‎src/Type/Php/IsAFunctionTypeSpecifyingHelper.php‎

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,18 @@ public function determineType(
3838

3939
return TypeTraverser::map(
4040
$classType,
41-
static function (Type $type, callable $traverse) use ($objectOrClassTypeClassNames, $allowString, $allowSameClass): Type {
41+
static function (Type $type, callable $traverse) use ($objectOrClassType, $objectOrClassTypeClassNames, $allowString, $allowSameClass): Type {
4242
if ($type instanceof UnionType || $type instanceof IntersectionType) {
4343
return $traverse($type);
4444
}
4545
if ($type instanceof ConstantStringType) {
4646
if (!$allowSameClass && $objectOrClassTypeClassNames === [$type->getValue()]) {
47-
return new NeverType();
47+
// For objectType we cannot be sure since 'Foo' is used for both
48+
// - the Foo class
49+
// - a child of foo class
50+
if ($objectOrClassType->isString()->yes()) {
51+
return new NeverType();
52+
}
4853
}
4954
if ($allowString) {
5055
return TypeCombinator::union(

‎src/Type/Php/IsSubclassOfFunctionTypeSpecifyingExtension.php‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public function specifyTypes(FunctionReflection $functionReflection, FuncCall $n
5353
$resultType = $this->isAFunctionTypeSpecifyingHelper->determineType($objectOrClassType, $classType, $allowString, false);
5454

5555
// prevent false-positives in IsAFunctionTypeSpecifyingHelper
56-
if ($classType->getConstantStrings() === [] && $resultType->isSuperTypeOf($objectOrClassType)->yes()) {
56+
if ($resultType->isSuperTypeOf($objectOrClassType)->yes()) {
5757
return new SpecifiedTypes([], []);
5858
}
5959

‎tests/PHPStan/Analyser/nsrt/bug-6305.php‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,5 @@ class B extends A {}
1515
}
1616

1717
if (is_subclass_of($b, B::class)) {
18-
assertType('*NEVER*', $b);
18+
assertType('Bug6305Types\B', $b);// Could be NEVER
1919
}

‎tests/PHPStan/Analyser/nsrt/is-subclass-of.php‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
function (Bar $a, Bar $b, Bar $c, Bar $d) {
66
if (is_subclass_of($a, Bar::class)) {
7-
\PHPStan\Testing\assertType('*NEVER*', $a);
7+
\PHPStan\Testing\assertType('IsSubclassOf\Bar', $a);// Can still be a Bar child
88
}
99

1010
if (is_subclass_of($b, Foo::class)) {

‎tests/PHPStan/Rules/Comparison/ImpossibleCheckTypeFunctionCallRuleTest.php‎

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -389,17 +389,29 @@ public function testBug6305(): void
389389
{
390390
$this->treatPhpDocTypesAsCertain = true;
391391
$this->analyse([__DIR__ . '/data/bug-6305.php'], [
392-
[
393-
'Call to function is_subclass_of() with Bug6305\B and \'Bug6305\\\A\' will always evaluate to true.',
394-
11,
395-
],
396-
[
397-
'Call to function is_subclass_of() with Bug6305\B and \'Bug6305\\\B\' will always evaluate to false.',
398-
14,
399-
],
392+
// [
393+
// 'Call to function is_subclass_of() with Bug6305\B and \'Bug6305\\\A\' will always evaluate to true.',
394+
// 11,
395+
// ],
396+
// [
397+
// 'Call to function is_subclass_of() with Bug6305\B and \'Bug6305\\\B\' will always evaluate to false.',
398+
// 14,
399+
// ],
400400
]);
401401
}
402402

403+
public function testBug6305b(): void
404+
{
405+
$this->treatPhpDocTypesAsCertain = true;
406+
$this->analyse([__DIR__ . '/data/bug-6305b.php'], []);
407+
}
408+
409+
public function testBug13713(): void
410+
{
411+
$this->treatPhpDocTypesAsCertain = true;
412+
$this->analyse([__DIR__ . '/data/bug-13713.php'], []);
413+
}
414+
403415
public function testBug6698(): void
404416
{
405417
$this->treatPhpDocTypesAsCertain = true;
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
namespace Bug13713;
6+
7+
function debug(object $test): void {
8+
if ($test instanceof \stdClass) {
9+
echo var_export(\is_subclass_of($test, \stdClass::class, false), true) . \PHP_EOL;
10+
}
11+
}
12+
13+
class test extends \stdClass {}
14+
debug(new test);
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
namespace Bug6305b;
4+
5+
class A {}
6+
7+
class B extends A {}
8+
9+
$b = mt_rand(0, 1) === 0 ? new B() : new A();
10+
11+
if (is_subclass_of($b, A::class)) {
12+
if (is_subclass_of($b, A::class)) {
13+
echo 'x';
14+
}
15+
}
16+
17+
if (is_subclass_of($b, B::class)) {
18+
if (is_subclass_of($b, B::class)) {
19+
echo 'y';
20+
}
21+
}
22+
23+
$b = mt_rand(0, 1) === 0 ? A::class : B::class;
24+
25+
if (is_subclass_of($b, A::class)) {
26+
if (is_subclass_of($b, A::class)) {
27+
echo 'x';
28+
}
29+
}
30+
31+
if (is_subclass_of($b, B::class)) {
32+
if (is_subclass_of($b, B::class)) {
33+
echo 'y';
34+
}
35+
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /