Skip to content

Commit 58b9360

Browse files
Report unused protected when class is final
1 parent b05cb5e commit 58b9360

9 files changed

+177
-4
lines changed

src/Rules/DeadCode/UnusedPrivateConstantRule.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,12 @@ public function processNode(Node $node, Scope $scope): array
3535
}
3636

3737
$classReflection = $node->getClassReflection();
38+
$isClassFinal = $classReflection->isFinalByKeyword();
3839
$classType = new ObjectType($classReflection->getName(), classReflection: $classReflection);
3940

4041
$constants = [];
4142
foreach ($node->getConstants() as $constant) {
42-
if (!$constant->isPrivate()) {
43+
if ($constant->isPublic() || ($constant->isProtected() && !$isClassFinal)) {
4344
continue;
4445
}
4546

src/Rules/DeadCode/UnusedPrivateMethodRule.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ public function processNode(Node $node, Scope $scope): array
4040
return [];
4141
}
4242
$classReflection = $node->getClassReflection();
43+
$isClassFinal = $classReflection->isFinalByKeyword();
4344
$classType = new ObjectType($classReflection->getName(), classReflection: $classReflection);
4445
$constructor = null;
4546
if ($classReflection->hasConstructor()) {
@@ -48,13 +49,15 @@ public function processNode(Node $node, Scope $scope): array
4849

4950
$methods = [];
5051
foreach ($node->getMethods() as $method) {
51-
if (!$method->getNode()->isPrivate()) {
52+
$methodNode = $method->getNode();
53+
if ($methodNode->isPublic() || ($methodNode->isProtected() && !$isClassFinal)) {
5254
continue;
5355
}
5456
if ($method->isDeclaredInTrait()) {
5557
continue;
5658
}
57-
$methodName = $method->getNode()->name->toString();
59+
60+
$methodName = $methodNode->name->toString();
5861
if ($constructor !== null && $constructor->getName() === $methodName) {
5962
continue;
6063
}

src/Rules/DeadCode/UnusedPrivatePropertyRule.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,11 @@ public function processNode(Node $node, Scope $scope): array
5656
return [];
5757
}
5858
$classReflection = $node->getClassReflection();
59+
$isClassFinal = $classReflection->isFinalByKeyword();
5960
$classType = new ObjectType($classReflection->getName(), classReflection: $classReflection);
6061
$properties = [];
6162
foreach ($node->getProperties() as $property) {
62-
if (!$property->isPrivate()) {
63+
if ($property->isPublic() || ($property->isProtected() && !$isClassFinal)) {
6364
continue;
6465
}
6566
if ($property->isDeclaredInTrait()) {

tests/PHPStan/Rules/DeadCode/UnusedPrivateConstantRuleTest.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,22 @@ public function testRule(): void
4949
]);
5050
}
5151

52+
public function testProtected(): void
53+
{
54+
$this->analyse([__DIR__ . '/data/unused-protected-constant.php'], [
55+
[
56+
'Constant UnusedProtectedConstant\Bar::BAR_CONST is unused.',
57+
26,
58+
'See: https://phpstan.org/developing-extensions/always-used-class-constants',
59+
],
60+
[
61+
'Constant UnusedProtectedConstant\Bar::BAZ_CONST is unused.',
62+
28,
63+
'See: https://phpstan.org/developing-extensions/always-used-class-constants',
64+
],
65+
]);
66+
}
67+
5268
public function testBug5651(): void
5369
{
5470
$this->analyse([__DIR__ . '/data/bug-5651.php'], []);

tests/PHPStan/Rules/DeadCode/UnusedPrivateMethodRuleTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,20 @@ public function testRule(): void
6262
]);
6363
}
6464

65+
public function testProtected(): void
66+
{
67+
$this->analyse([__DIR__ . '/data/unused-protected-method.php'], [
68+
[
69+
'Method UnusedProtectedMethod\Bar::unused1() is unused.',
70+
30,
71+
],
72+
[
73+
'Method UnusedProtectedMethod\Bar::unused2() is unused.',
74+
35,
75+
],
76+
]);
77+
}
78+
6579
public function testBug3630(): void
6680
{
6781
$this->analyse([__DIR__ . '/data/bug-3630.php'], []);

tests/PHPStan/Rules/DeadCode/UnusedPrivatePropertyRuleTest.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,24 @@ public function testTrait(): void
186186
$this->analyse([__DIR__ . '/data/private-property-trait.php'], []);
187187
}
188188

189+
public function testProtected(): void
190+
{
191+
$this->alwaysWrittenTags = [];
192+
$this->alwaysReadTags = [];
193+
$this->analyse([__DIR__ . '/data/unused-protected-property.php'], [
194+
[
195+
'Property UnusedProtectedProperty\Bar::$bar is unused.',
196+
31,
197+
'See: https://phpstan.org/developing-extensions/always-read-written-properties',
198+
],
199+
[
200+
'Property UnusedProtectedProperty\Bar::$baz is unused.',
201+
33,
202+
'See: https://phpstan.org/developing-extensions/always-read-written-properties',
203+
],
204+
]);
205+
}
206+
189207
public function testBug3636(): void
190208
{
191209
$this->alwaysWrittenTags = [];
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
namespace UnusedProtectedConstant;
4+
5+
class Foo
6+
{
7+
8+
protected const FOO_CONST = 1;
9+
10+
protected const BAR_CONST = 2;
11+
12+
final protected const BAZ_CONST = 2;
13+
14+
public function doFoo()
15+
{
16+
echo self::FOO_CONST;
17+
}
18+
19+
}
20+
21+
final class Bar
22+
{
23+
24+
protected const FOO_CONST = 1;
25+
26+
protected const BAR_CONST = 2;
27+
28+
final protected const BAZ_CONST = 2;
29+
30+
public function doFoo()
31+
{
32+
echo self::FOO_CONST;
33+
}
34+
35+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
namespace UnusedProtectedMethod;
4+
5+
class Foo
6+
{
7+
protected function used1()
8+
{
9+
}
10+
11+
protected function unused1()
12+
{
13+
$this->used1();
14+
}
15+
16+
final protected function unused2()
17+
{
18+
$this->used1();
19+
}
20+
21+
}
22+
23+
final class Bar
24+
{
25+
26+
protected function used1()
27+
{
28+
}
29+
30+
protected function unused1()
31+
{
32+
$this->used1();
33+
}
34+
35+
final protected function unused2()
36+
{
37+
$this->used1();
38+
}
39+
40+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php // lint >= 8.4
2+
3+
namespace UnusedProtectedProperty;
4+
5+
class Foo
6+
{
7+
8+
protected $foo;
9+
10+
protected string $bar;
11+
12+
final protected string $baz;
13+
14+
public function __construct()
15+
{
16+
$this->foo = 1;
17+
}
18+
19+
public function getFoo()
20+
{
21+
return $this->foo;
22+
}
23+
24+
}
25+
26+
final class Bar
27+
{
28+
29+
protected $foo;
30+
31+
protected string $bar;
32+
33+
final protected string $baz;
34+
35+
public function __construct()
36+
{
37+
$this->foo = 1;
38+
}
39+
40+
public function getFoo()
41+
{
42+
return $this->foo;
43+
}
44+
45+
}

0 commit comments

Comments
 (0)