Skip to content

Commit cbdb30d

Browse files
committed
Do not report unused class elements if the element is used on an uncertain type like mixed
1 parent 6c941ae commit cbdb30d

File tree

7 files changed

+120
-0
lines changed

7 files changed

+120
-0
lines changed

src/Rules/DeadCode/UnusedPrivateConstantRule.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use PHPStan\Rules\Constants\AlwaysUsedClassConstantsExtensionProvider;
99
use PHPStan\Rules\Rule;
1010
use PHPStan\Rules\RuleErrorBuilder;
11+
use PHPStan\Type\ObjectType;
1112
use function sprintf;
1213

1314
/**
@@ -32,6 +33,7 @@ public function processNode(Node $node, Scope $scope): array
3233
}
3334

3435
$classReflection = $node->getClassReflection();
36+
$classType = new ObjectType($classReflection->getName());
3537

3638
$constants = [];
3739
foreach ($node->getConstants() as $constant) {
@@ -68,10 +70,16 @@ public function processNode(Node $node, Scope $scope): array
6870

6971
$constantReflection = $fetchScope->getConstantReflection($fetchedOnClass, $fetchNode->name->toString());
7072
if ($constantReflection === null) {
73+
if (!$classType->isSuperTypeOf($fetchedOnClass)->no()) {
74+
unset($constants[$fetchNode->name->toString()]);
75+
}
7176
continue;
7277
}
7378

7479
if ($constantReflection->getDeclaringClass()->getName() !== $classReflection->getName()) {
80+
if (!$classType->isSuperTypeOf($fetchedOnClass)->no()) {
81+
unset($constants[$fetchNode->name->toString()]);
82+
}
7583
continue;
7684
}
7785

src/Rules/DeadCode/UnusedPrivateMethodRule.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use PHPStan\Rules\Rule;
1111
use PHPStan\Rules\RuleErrorBuilder;
1212
use PHPStan\Type\Constant\ConstantStringType;
13+
use PHPStan\Type\ObjectType;
1314
use function array_map;
1415
use function count;
1516
use function sprintf;
@@ -32,6 +33,7 @@ public function processNode(Node $node, Scope $scope): array
3233
return [];
3334
}
3435
$classReflection = $node->getClassReflection();
36+
$classType = new ObjectType($classReflection->getName());
3537
$constructor = null;
3638
if ($classReflection->hasConstructor()) {
3739
$constructor = $classReflection->getConstructor();
@@ -93,9 +95,15 @@ public function processNode(Node $node, Scope $scope): array
9395
foreach ($methodNames as $methodName) {
9496
$methodReflection = $callScope->getMethodReflection($calledOnType, $methodName);
9597
if ($methodReflection === null) {
98+
if (!$classType->isSuperTypeOf($calledOnType)->no()) {
99+
unset($methods[strtolower($methodName)]);
100+
}
96101
continue;
97102
}
98103
if ($methodReflection->getDeclaringClass()->getName() !== $classReflection->getName()) {
104+
if (!$classType->isSuperTypeOf($calledOnType)->no()) {
105+
unset($methods[strtolower($methodName)]);
106+
}
99107
continue;
100108
}
101109
if ($inMethod->getName() === $methodName) {

src/Rules/DeadCode/UnusedPrivatePropertyRule.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use PHPStan\Rules\Rule;
1111
use PHPStan\Rules\RuleErrorBuilder;
1212
use PHPStan\Type\Constant\ConstantStringType;
13+
use PHPStan\Type\ObjectType;
1314
use function array_key_exists;
1415
use function array_map;
1516
use function count;
@@ -46,6 +47,7 @@ public function processNode(Node $node, Scope $scope): array
4647
return [];
4748
}
4849
$classReflection = $node->getClassReflection();
50+
$classType = new ObjectType($classReflection->getName());
4951
$properties = [];
5052
foreach ($node->getProperties() as $property) {
5153
if (!$property->isPrivate()) {
@@ -139,9 +141,23 @@ public function processNode(Node $node, Scope $scope): array
139141
}
140142
$propertyReflection = $usage->getScope()->getPropertyReflection($fetchedOnType, $propertyName);
141143
if ($propertyReflection === null) {
144+
if (!$classType->isSuperTypeOf($fetchedOnType)->no()) {
145+
if ($usage instanceof PropertyRead) {
146+
$properties[$propertyName]['read'] = true;
147+
} else {
148+
$properties[$propertyName]['written'] = true;
149+
}
150+
}
142151
continue;
143152
}
144153
if ($propertyReflection->getDeclaringClass()->getName() !== $classReflection->getName()) {
154+
if (!$classType->isSuperTypeOf($fetchedOnType)->no()) {
155+
if ($usage instanceof PropertyRead) {
156+
$properties[$propertyName]['read'] = true;
157+
} else {
158+
$properties[$propertyName]['written'] = true;
159+
}
160+
}
145161
continue;
146162
}
147163

tests/PHPStan/Rules/DeadCode/UnusedPrivateConstantRuleTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,9 @@ public function testBug9005(): void
9292
$this->analyse([__DIR__ . '/data/bug-9005.php'], []);
9393
}
9494

95+
public function testBug9765(): void
96+
{
97+
$this->analyse([__DIR__ . '/data/bug-9765.php'], []);
98+
}
99+
95100
}

tests/PHPStan/Rules/DeadCode/UnusedPrivateMethodRuleTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,4 +109,9 @@ public function testBug6039(): void
109109
$this->analyse([__DIR__ . '/data/bug-6039.php'], []);
110110
}
111111

112+
public function testBug9765(): void
113+
{
114+
$this->analyse([__DIR__ . '/data/bug-9765.php'], []);
115+
}
116+
112117
}

tests/PHPStan/Rules/DeadCode/UnusedPrivatePropertyRuleTest.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,4 +286,11 @@ public function testBug9409(): void
286286
$this->analyse([__DIR__ . '/data/bug-9409.php'], []);
287287
}
288288

289+
public function testBug9765(): void
290+
{
291+
$this->alwaysWrittenTags = [];
292+
$this->alwaysReadTags = [];
293+
$this->analyse([__DIR__ . '/data/bug-9765.php'], []);
294+
}
295+
289296
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Bug9765;
4+
5+
class HelloWorld
6+
{
7+
8+
public static function runner(): \Closure
9+
{
10+
return function (int $arg) {
11+
return $this->add($arg);
12+
};
13+
}
14+
15+
public function do(): void
16+
{
17+
$c = self::runner();
18+
print $c->bindTo($this)(5);
19+
}
20+
21+
private function add(int $a): int
22+
{
23+
return $a + 1;
24+
}
25+
26+
}
27+
28+
class HelloWorld2
29+
{
30+
31+
/** @var int */
32+
private $foo;
33+
34+
public static function runner(): \Closure
35+
{
36+
return function (int $arg) {
37+
if ($arg > 0) {
38+
$this->foo = $arg;
39+
} else {
40+
echo $this->foo;
41+
}
42+
};
43+
}
44+
45+
public function do(): void
46+
{
47+
$c = self::runner();
48+
print $c->bindTo($this)(5);
49+
}
50+
51+
}
52+
53+
class HelloWorld3
54+
{
55+
56+
private const FOO = 1;
57+
58+
public static function runner(): \Closure
59+
{
60+
return function (int $arg) {
61+
echo $this::FOO;
62+
};
63+
}
64+
65+
public function do(): void
66+
{
67+
$c = self::runner();
68+
print $c->bindTo($this)(5);
69+
}
70+
71+
}

0 commit comments

Comments
 (0)