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 5500473

Browse files
Rework
1 parent f2663f1 commit 5500473

File tree

6 files changed

+62
-36
lines changed

6 files changed

+62
-36
lines changed

‎src/Type/Php/IsAFunctionTypeSpecifyingExtension.php‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,7 @@ public function specifyTypes(FunctionReflection $functionReflection, FuncCall $n
5050
$allowString = !$allowStringType->equals(new ConstantBooleanType(false));
5151

5252
$resultType = $this->isAFunctionTypeSpecifyingHelper->determineType($objectOrClassType, $classType, $allowString, true);
53-
54-
// prevent false-positives in IsAFunctionTypeSpecifyingHelper
55-
if ($classType->getConstantStrings() === [] && $resultType->isSuperTypeOf($objectOrClassType)->yes()) {
53+
if ($resultType === null) {
5654
return new SpecifiedTypes([], []);
5755
}
5856

‎src/Type/Php/IsAFunctionTypeSpecifyingHelper.php‎

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use PHPStan\Type\UnionType;
1717
use function array_unique;
1818
use function array_values;
19+
use function in_array;
1920

2021
#[AutowiredService]
2122
final class IsAFunctionTypeSpecifyingHelper
@@ -26,7 +27,7 @@ public function determineType(
2627
Type $classType,
2728
bool $allowString,
2829
bool $allowSameClass,
29-
): Type
30+
): ?Type
3031
{
3132
$objectOrClassTypeClassNames = $objectOrClassType->getObjectClassNames();
3233
if ($allowString) {
@@ -36,20 +37,35 @@ public function determineType(
3637
$objectOrClassTypeClassNames = array_values(array_unique($objectOrClassTypeClassNames));
3738
}
3839

39-
return TypeTraverser::map(
40+
$isUncertain = $classType->getConstantStrings() === [];
41+
42+
$resultType = TypeTraverser::map(
4043
$classType,
41-
static function (Type $type, callable $traverse) use ($objectOrClassType, $objectOrClassTypeClassNames, $allowString, $allowSameClass): Type {
44+
static function (Type $type, callable $traverse) use ($objectOrClassType, $objectOrClassTypeClassNames, $allowString, $allowSameClass, &$isUncertain): Type {
4245
if ($type instanceof UnionType || $type instanceof IntersectionType) {
4346
return $traverse($type);
4447
}
4548
if ($type instanceof ConstantStringType) {
46-
if (!$allowSameClass && $objectOrClassTypeClassNames === [$type->getValue()]) {
49+
if (!$allowSameClass) {
4750
// For objectType we cannot be sure since 'Foo' is used for both
4851
// - the Foo class
4952
// - a child of foo class
50-
if ($objectOrClassType->isString()->yes()) {
53+
if (
54+
$objectOrClassTypeClassNames === [$type->getValue()]
55+
&& $objectOrClassType->isString()->yes()
56+
) {
5157
return new NeverType();
5258
}
59+
60+
if (
61+
// For object, as soon as the exact same type is provided
62+
// in the list we cannot be sure of the result
63+
in_array($type->getValue(), $objectOrClassTypeClassNames, true)
64+
// This also occurs for generic class string
65+
|| ($allowString && $objectOrClassTypeClassNames === [] && $objectOrClassType->isSuperTypeOf($type)->yes()))
66+
{
67+
$isUncertain = true;
68+
}
5369
}
5470
if ($allowString) {
5571
return TypeCombinator::union(
@@ -80,6 +96,13 @@ static function (Type $type, callable $traverse) use ($objectOrClassType, $objec
8096
return new ObjectWithoutClassType();
8197
},
8298
);
99+
100+
// prevent false-positives
101+
if ($isUncertain && $resultType->isSuperTypeOf($objectOrClassType)->yes()) {
102+
return null;
103+
}
104+
105+
return $resultType;
83106
}
84107

85108
}

‎src/Type/Php/IsSubclassOfFunctionTypeSpecifyingExtension.php‎

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,8 @@ public function specifyTypes(FunctionReflection $functionReflection, FuncCall $n
4545
$allowStringType = isset($node->getArgs()[2]) ? $scope->getType($node->getArgs()[2]->value) : new ConstantBooleanType(true);
4646
$allowString = !$allowStringType->equals(new ConstantBooleanType(false));
4747

48-
// prevent false-positives in IsAFunctionTypeSpecifyingHelper
49-
if ($objectOrClassType instanceof GenericClassStringType && $classType instanceof GenericClassStringType) {
50-
return new SpecifiedTypes([], []);
51-
}
52-
5348
$resultType = $this->isAFunctionTypeSpecifyingHelper->determineType($objectOrClassType, $classType, $allowString, false);
54-
55-
// prevent false-positives in IsAFunctionTypeSpecifyingHelper
56-
if ($resultType->isSuperTypeOf($objectOrClassType)->yes()) {
49+
if (null === $resultType) {
5750
return new SpecifiedTypes([], []);
5851
}
5952

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

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -389,10 +389,10 @@ 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-
// ],
392+
[
393+
'Call to function is_subclass_of() with Bug6305\B and \'Bug6305\\\A\' will always evaluate to true.',
394+
11,
395+
],
396396
// [
397397
// 'Call to function is_subclass_of() with Bug6305\B and \'Bug6305\\\B\' will always evaluate to false.',
398398
// 14,
@@ -409,7 +409,17 @@ public function testBug6305b(): void
409409
public function testBug13713(): void
410410
{
411411
$this->treatPhpDocTypesAsCertain = true;
412-
$this->analyse([__DIR__ . '/data/bug-13713.php'], []);
412+
$this->analyse([__DIR__ . '/data/bug-13713.php'], [
413+
[
414+
"Call to function is_subclass_of() with arguments Bug13713\\test, 'stdClass' and false will always evaluate to true.",
415+
12,
416+
],
417+
[
418+
"Call to function is_subclass_of() with arguments class-string<Bug13713\\test>, 'stdClass' and true will always evaluate to true.",
419+
25,
420+
'Because the type is coming from a PHPDoc, you can turn off this check by setting <fg=cyan>treatPhpDocTypesAsCertain: false</> in your <fg=cyan>%configurationFile%</>.',
421+
],
422+
]);
413423
}
414424

415425
public function testBug6698(): void

‎tests/PHPStan/Rules/Comparison/data/bug-13713.php‎

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,25 @@
44

55
namespace Bug13713;
66

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;
7+
function debug(object $object): void {
8+
if ($object instanceof \stdClass) {
9+
echo var_export(\is_subclass_of($object, \stdClass::class, false), true) . \PHP_EOL;
10+
}
11+
if ($object instanceof test) {
12+
echo var_export(\is_subclass_of($object, \stdClass::class, false), true) . \PHP_EOL;
1013
}
1114
}
1215

1316
class test extends \stdClass {}
1417
debug(new test);
18+
19+
/**
20+
* @param class-string<\stdClass> $stdClass
21+
* @param class-string<test> $test
22+
*/
23+
function debugWithClass(string $stdClass, string $test): void {
24+
echo var_export(\is_subclass_of($stdClass, \stdClass::class, true), true) . \PHP_EOL;
25+
echo var_export(\is_subclass_of($test, \stdClass::class, true), true) . \PHP_EOL;
26+
}
27+
28+
debugWithClass(test::class, test::class);

‎tests/PHPStan/Rules/Comparison/data/bug-6305b.php‎

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,27 +9,15 @@ class B extends A {}
99
$b = mt_rand(0, 1) === 0 ? new B() : new A();
1010

1111
if (is_subclass_of($b, A::class)) {
12-
if (is_subclass_of($b, A::class)) {
13-
echo 'x';
14-
}
1512
}
1613

1714
if (is_subclass_of($b, B::class)) {
18-
if (is_subclass_of($b, B::class)) {
19-
echo 'y';
20-
}
2115
}
2216

2317
$b = mt_rand(0, 1) === 0 ? A::class : B::class;
2418

2519
if (is_subclass_of($b, A::class)) {
26-
if (is_subclass_of($b, A::class)) {
27-
echo 'x';
28-
}
2920
}
3021

3122
if (is_subclass_of($b, B::class)) {
32-
if (is_subclass_of($b, B::class)) {
33-
echo 'y';
34-
}
3523
}

0 commit comments

Comments
(0)

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