Skip to content

Commit 40aec1e

Browse files
committed
class_implements - sometimes return BenevolentUnionType
1 parent 865a4ba commit 40aec1e

File tree

3 files changed

+64
-43
lines changed

3 files changed

+64
-43
lines changed

phpstan-baseline.neon

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ parameters:
6161
count: 1
6262
path: src/Collectors/Registry.php
6363

64+
-
65+
message: "#^Only numeric types are allowed in \\+, \\(array\\<string, class\\-string\\>\\|false\\) given on the right side\\.$#"
66+
count: 2
67+
path: src/Collectors/Registry.php
68+
6469
-
6570
message: "#^Property PHPStan\\\\Collectors\\\\Registry\\:\\:\\$cache with generic interface PHPStan\\\\Collectors\\\\Collector does not specify its types\\: TNodeType, TValue$#"
6671
count: 1
@@ -360,6 +365,11 @@ parameters:
360365
count: 1
361366
path: src/Rules/DirectRegistry.php
362367

368+
-
369+
message: "#^Only numeric types are allowed in \\+, \\(array\\<string, class\\-string\\>\\|false\\) given on the right side\\.$#"
370+
count: 2
371+
path: src/Rules/DirectRegistry.php
372+
363373
-
364374
message: "#^Property PHPStan\\\\Rules\\\\DirectRegistry\\:\\:\\$cache with generic interface PHPStan\\\\Rules\\\\Rule does not specify its types\\: TNodeType$#"
365375
count: 1
@@ -385,6 +395,11 @@ parameters:
385395
count: 1
386396
path: src/Rules/LazyRegistry.php
387397

398+
-
399+
message: "#^Only numeric types are allowed in \\+, \\(array\\<string, class\\-string\\>\\|false\\) given on the right side\\.$#"
400+
count: 2
401+
path: src/Rules/LazyRegistry.php
402+
388403
-
389404
message: "#^Property PHPStan\\\\Rules\\\\LazyRegistry\\:\\:\\$cache with generic interface PHPStan\\\\Rules\\\\Rule does not specify its types\\: TNodeType$#"
390405
count: 1

src/Type/Php/ClassImplementsFunctionReturnTypeExtension.php

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@
66
use PHPStan\Analyser\Scope;
77
use PHPStan\Reflection\FunctionReflection;
88
use PHPStan\Reflection\ParametersAcceptorSelector;
9+
use PHPStan\TrinaryLogic;
910
use PHPStan\Type\ClassStringType;
1011
use PHPStan\Type\Constant\ConstantBooleanType;
1112
use PHPStan\Type\DynamicFunctionReturnTypeExtension;
1213
use PHPStan\Type\ObjectWithoutClassType;
1314
use PHPStan\Type\Type;
1415
use PHPStan\Type\TypeCombinator;
16+
use PHPStan\Type\TypeUtils;
1517
use PHPStan\Type\UnionType;
1618
use function count;
1719
use function in_array;
@@ -30,28 +32,32 @@ public function isFunctionSupported(FunctionReflection $functionReflection): boo
3032

3133
public function getTypeFromFunctionCall(FunctionReflection $functionReflection, FuncCall $functionCall, Scope $scope): ?Type
3234
{
33-
if (count($functionCall->getArgs()) < 1) {
35+
$args = $functionCall->getArgs();
36+
if (count($args) < 1) {
3437
return null;
3538
}
3639

37-
$firstArgType = $scope->getType($functionCall->getArgs()[0]->value);
38-
$autoload = !isset($functionCall->getArgs()[1])
39-
|| $scope->getType($functionCall->getArgs()[1]->value)->equals(new ConstantBooleanType(true));
40+
$firstArgType = $scope->getType($args[0]->value);
41+
$autoload = TrinaryLogic::createYes();
42+
if (isset($args[1])) {
43+
$autoload = $scope->getType($args[1]->value)->isTrue();
44+
}
4045

4146
$isObject = (new ObjectWithoutClassType())->isSuperTypeOf($firstArgType);
47+
$variant = ParametersAcceptorSelector::selectFromArgs($scope, $args, $functionReflection->getVariants());
48+
if ($isObject->yes()) {
49+
return TypeCombinator::remove($variant->getReturnType(), new ConstantBooleanType(false));
50+
}
51+
$isClassStringOrObject = (new UnionType([new ObjectWithoutClassType(), new ClassStringType()]))->isSuperTypeOf($firstArgType);
52+
if ($isClassStringOrObject->yes()) {
53+
if ($autoload->yes()) {
54+
return TypeUtils::toBenevolentUnion($variant->getReturnType());
55+
}
4256

43-
$objectOrClassString = (new UnionType([new ObjectWithoutClassType(), new ClassStringType()]));
44-
if (
45-
$autoload && $objectOrClassString->isSuperTypeOf($firstArgType)->yes()
46-
|| $isObject->yes()
47-
) {
48-
return TypeCombinator::remove(
49-
ParametersAcceptorSelector::selectSingle($functionReflection->getVariants())->getReturnType(),
50-
new ConstantBooleanType(false),
51-
);
57+
return $variant->getReturnType();
5258
}
5359

54-
if ($isObject->no() && $firstArgType->isClassStringType()->no()) {
60+
if ($firstArgType->isClassStringType()->no()) {
5561
return new ConstantBooleanType(false);
5662
}
5763

tests/PHPStan/Analyser/data/class-implements.php

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -15,113 +15,113 @@ public function test(
1515
object|string $objectOrClassString,
1616
object|string $objectOrString,
1717
string $classString,
18-
string $className,
18+
string $string,
1919
bool $bool,
2020
mixed $mixed,
2121
): void {
2222
assertType('array<string, class-string>', class_implements($object));
23-
assertType('array<string, class-string>', class_implements($objectOrClassString));
23+
assertType('(array<string, class-string>|false)', class_implements($objectOrClassString));
2424
assertType('array<string, class-string>|false', class_implements($objectOrString));
25-
assertType('array<string, class-string>', class_implements($classString));
26-
assertType('array<string, class-string>|false', class_implements($className));
25+
assertType('(array<string, class-string>|false)', class_implements($classString));
26+
assertType('array<string, class-string>|false', class_implements($string));
2727
assertType('false', class_implements('thisIsNotAClass'));
2828

2929
assertType('array<string, class-string>', class_implements($object, true));
30-
assertType('array<string, class-string>', class_implements($objectOrClassString, true));
30+
assertType('(array<string, class-string>|false)', class_implements($objectOrClassString, true));
3131
assertType('array<string, class-string>|false', class_implements($objectOrString, true));
32-
assertType('array<string, class-string>', class_implements($classString, true));
33-
assertType('array<string, class-string>|false', class_implements($className, true));
32+
assertType('(array<string, class-string>|false)', class_implements($classString, true));
33+
assertType('array<string, class-string>|false', class_implements($string, true));
3434
assertType('false', class_implements('thisIsNotAClass', true));
3535

3636
assertType('array<string, class-string>', class_implements($object, false));
3737
assertType('array<string, class-string>|false', class_implements($objectOrClassString, false));
3838
assertType('array<string, class-string>|false', class_implements($objectOrString, false));
3939
assertType('array<string, class-string>|false', class_implements($classString, false));
40-
assertType('array<string, class-string>|false', class_implements($className, false));
40+
assertType('array<string, class-string>|false', class_implements($string, false));
4141
assertType('false', class_implements('thisIsNotAClass', false));
4242

4343
assertType('array<string, class-string>', class_implements($object, $bool));
4444
assertType('array<string, class-string>|false', class_implements($objectOrClassString, $bool));
4545
assertType('array<string, class-string>|false', class_implements($objectOrString, $bool));
4646
assertType('array<string, class-string>|false', class_implements($classString, $bool));
47-
assertType('array<string, class-string>|false', class_implements($className, $bool));
47+
assertType('array<string, class-string>|false', class_implements($string, $bool));
4848
assertType('false', class_implements('thisIsNotAClass', $bool));
4949

5050
assertType('array<string, class-string>', class_implements($object, $mixed));
5151
assertType('array<string, class-string>|false', class_implements($objectOrClassString, $mixed));
5252
assertType('array<string, class-string>|false', class_implements($objectOrString, $mixed));
5353
assertType('array<string, class-string>|false', class_implements($classString, $mixed));
54-
assertType('array<string, class-string>|false', class_implements($className, $mixed));
54+
assertType('array<string, class-string>|false', class_implements($string, $mixed));
5555
assertType('false', class_implements('thisIsNotAClass', $mixed));
5656

5757
assertType('array<string, class-string>', class_uses($object));
58-
assertType('array<string, class-string>', class_uses($objectOrClassString));
58+
assertType('(array<string, class-string>|false)', class_uses($objectOrClassString));
5959
assertType('array<string, class-string>|false', class_uses($objectOrString));
60-
assertType('array<string, class-string>', class_uses($classString));
61-
assertType('array<string, class-string>|false', class_uses($className));
60+
assertType('(array<string, class-string>|false)', class_uses($classString));
61+
assertType('array<string, class-string>|false', class_uses($string));
6262
assertType('false', class_uses('thisIsNotAClass'));
6363

6464
assertType('array<string, class-string>', class_uses($object, true));
65-
assertType('array<string, class-string>', class_uses($objectOrClassString, true));
65+
assertType('(array<string, class-string>|false)', class_uses($objectOrClassString, true));
6666
assertType('array<string, class-string>|false', class_uses($objectOrString, true));
67-
assertType('array<string, class-string>', class_uses($classString, true));
68-
assertType('array<string, class-string>|false', class_uses($className, true));
67+
assertType('(array<string, class-string>|false)', class_uses($classString, true));
68+
assertType('array<string, class-string>|false', class_uses($string, true));
6969
assertType('false', class_uses('thisIsNotAClass', true));
7070

7171
assertType('array<string, class-string>', class_uses($object, false));
7272
assertType('array<string, class-string>|false', class_uses($objectOrClassString, false));
7373
assertType('array<string, class-string>|false', class_uses($objectOrString, false));
7474
assertType('array<string, class-string>|false', class_uses($classString, false));
75-
assertType('array<string, class-string>|false', class_uses($className, false));
75+
assertType('array<string, class-string>|false', class_uses($string, false));
7676
assertType('false', class_uses('thisIsNotAClass', false));
7777

7878
assertType('array<string, class-string>', class_uses($object, $bool));
7979
assertType('array<string, class-string>|false', class_uses($objectOrClassString, $bool));
8080
assertType('array<string, class-string>|false', class_uses($objectOrString, $bool));
8181
assertType('array<string, class-string>|false', class_uses($classString, $bool));
82-
assertType('array<string, class-string>|false', class_uses($className, $bool));
82+
assertType('array<string, class-string>|false', class_uses($string, $bool));
8383
assertType('false', class_uses('thisIsNotAClass', $bool));
8484

8585
assertType('array<string, class-string>', class_uses($object, $mixed));
8686
assertType('array<string, class-string>|false', class_uses($objectOrClassString, $mixed));
8787
assertType('array<string, class-string>|false', class_uses($objectOrString, $mixed));
8888
assertType('array<string, class-string>|false', class_uses($classString, $mixed));
89-
assertType('array<string, class-string>|false', class_uses($className, $mixed));
89+
assertType('array<string, class-string>|false', class_uses($string, $mixed));
9090
assertType('false', class_uses('thisIsNotAClass', $mixed));
9191

9292
assertType('array<string, class-string>', class_parents($object));
93-
assertType('array<string, class-string>', class_parents($objectOrClassString));
93+
assertType('(array<string, class-string>|false)', class_parents($objectOrClassString));
9494
assertType('array<string, class-string>|false', class_parents($objectOrString));
95-
assertType('array<string, class-string>', class_parents($classString));
96-
assertType('array<string, class-string>|false', class_parents($className));
97-
assertType('false', class_parents('thisIsNotAClass', $className));
95+
assertType('(array<string, class-string>|false)', class_parents($classString));
96+
assertType('array<string, class-string>|false', class_parents($string));
97+
assertType('false', class_parents('thisIsNotAClass'));
9898

9999
assertType('array<string, class-string>', class_parents($object, true));
100-
assertType('array<string, class-string>', class_parents($objectOrClassString, true));
100+
assertType('(array<string, class-string>|false)', class_parents($objectOrClassString, true));
101101
assertType('array<string, class-string>|false', class_parents($objectOrString, true));
102-
assertType('array<string, class-string>', class_parents($classString, true));
103-
assertType('array<string, class-string>|false', class_parents($className, true));
102+
assertType('(array<string, class-string>|false)', class_parents($classString, true));
103+
assertType('array<string, class-string>|false', class_parents($string, true));
104104
assertType('false', class_parents('thisIsNotAClass', true));
105105

106106
assertType('array<string, class-string>', class_parents($object, false));
107107
assertType('array<string, class-string>|false', class_parents($objectOrClassString, false));
108108
assertType('array<string, class-string>|false', class_parents($objectOrString, false));
109109
assertType('array<string, class-string>|false', class_parents($classString, false));
110-
assertType('array<string, class-string>|false', class_parents($className, false));
110+
assertType('array<string, class-string>|false', class_parents($string, false));
111111
assertType('false', class_parents('thisIsNotAClass', false));
112112

113113
assertType('array<string, class-string>', class_parents($object, $bool));
114114
assertType('array<string, class-string>|false', class_parents($objectOrClassString, $bool));
115115
assertType('array<string, class-string>|false', class_parents($objectOrString, $bool));
116116
assertType('array<string, class-string>|false', class_parents($classString, $bool));
117-
assertType('array<string, class-string>|false', class_parents($className, $bool));
117+
assertType('array<string, class-string>|false', class_parents($string, $bool));
118118
assertType('false', class_parents('thisIsNotAClass', $bool));
119119

120120
assertType('array<string, class-string>', class_parents($object, $mixed));
121121
assertType('array<string, class-string>|false', class_parents($objectOrClassString, $mixed));
122122
assertType('array<string, class-string>|false', class_parents($objectOrString, $mixed));
123123
assertType('array<string, class-string>|false', class_parents($classString, $mixed));
124-
assertType('array<string, class-string>|false', class_parents($className, $mixed));
124+
assertType('array<string, class-string>|false', class_parents($string, $mixed));
125125
assertType('false', class_parents('thisIsNotAClass', $mixed));
126126
}
127127

0 commit comments

Comments
 (0)