From 4a2f65d5fdee0e377d958533221d11b008effbb2 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Fri, 4 Oct 2024 15:12:51 +0200 Subject: [PATCH 1/4] Fix unused private property is not sometimes detected --- .../DeadCode/UnusedPrivatePropertyRule.php | 2 +- .../UnusedPrivatePropertyRuleTest.php | 20 +++++++++++++++++++ .../PHPStan/Rules/DeadCode/data/bug-11802.php | 17 ++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 tests/PHPStan/Rules/DeadCode/data/bug-11802.php diff --git a/src/Rules/DeadCode/UnusedPrivatePropertyRule.php b/src/Rules/DeadCode/UnusedPrivatePropertyRule.php index f3373d4c10..1035c27e61 100644 --- a/src/Rules/DeadCode/UnusedPrivatePropertyRule.php +++ b/src/Rules/DeadCode/UnusedPrivatePropertyRule.php @@ -120,7 +120,7 @@ public function processNode(Node $node, Scope $scope): array $propertyNameType = $usage->getScope()->getType($fetch->name); $strings = $propertyNameType->getConstantStrings(); if (count($strings) === 0) { - return []; + continue; } $propertyNames = array_map(static fn (ConstantStringType $type): string => $type->getValue(), $strings); diff --git a/tests/PHPStan/Rules/DeadCode/UnusedPrivatePropertyRuleTest.php b/tests/PHPStan/Rules/DeadCode/UnusedPrivatePropertyRuleTest.php index 9e97e8bc4a..485e611ed0 100644 --- a/tests/PHPStan/Rules/DeadCode/UnusedPrivatePropertyRuleTest.php +++ b/tests/PHPStan/Rules/DeadCode/UnusedPrivatePropertyRuleTest.php @@ -107,6 +107,11 @@ public function testRule(): void 117, $tip, ], + [ + 'Property UnusedPrivateProperty\Ipsum::$foo is never read, only written.', + 136, + $tip, + ], [ 'Property class@anonymous/tests/PHPStan/Rules/DeadCode/data/unused-private-property.php:152::$bar is unused.', 153, @@ -336,4 +341,19 @@ public function testBug7251(): void $this->analyse([__DIR__ . '/data/bug-7251.php'], []); } + public function testBug11802(): void + { + $tip = 'See: https://phpstan.org/developing-extensions/always-read-written-properties'; + + $this->alwaysWrittenTags = []; + $this->alwaysReadTags = []; + $this->analyse([__DIR__ . '/data/bug-11802.php'], [ + [ + 'Property Bug11802\HelloWorld::$isFinal is never read, only written.', + 8, + $tip, + ], + ]); + } + } diff --git a/tests/PHPStan/Rules/DeadCode/data/bug-11802.php b/tests/PHPStan/Rules/DeadCode/data/bug-11802.php new file mode 100644 index 0000000000..55b90ab49c --- /dev/null +++ b/tests/PHPStan/Rules/DeadCode/data/bug-11802.php @@ -0,0 +1,17 @@ +{$y()}; + } +} From 95352d0755920492011d240d168e8d0b2597788e Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Fri, 4 Oct 2024 15:28:32 +0200 Subject: [PATCH 2/4] Fix unused private method is sometimes not detected --- src/Rules/DeadCode/UnusedPrivateMethodRule.php | 6 +++--- .../DeadCode/UnusedPrivateMethodRuleTest.php | 14 ++++++++++++++ tests/PHPStan/Rules/DeadCode/data/bug-11802.php | 2 +- tests/PHPStan/Rules/DeadCode/data/bug-11802b.php | 15 +++++++++++++++ 4 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 tests/PHPStan/Rules/DeadCode/data/bug-11802b.php diff --git a/src/Rules/DeadCode/UnusedPrivateMethodRule.php b/src/Rules/DeadCode/UnusedPrivateMethodRule.php index 52ec29d01a..fe0735993e 100644 --- a/src/Rules/DeadCode/UnusedPrivateMethodRule.php +++ b/src/Rules/DeadCode/UnusedPrivateMethodRule.php @@ -84,7 +84,7 @@ public function processNode(Node $node, Scope $scope): array $methodNameType = $callScope->getType($methodCallNode->name); $strings = $methodNameType->getConstantStrings(); if (count($strings) === 0) { - return []; + continue; } $methodNames = array_map(static fn (ConstantStringType $type): string => $type->getValue(), $strings); @@ -138,10 +138,10 @@ public function processNode(Node $node, Scope $scope): array foreach ($arrayType->getConstantArrays() as $constantArray) { foreach ($constantArray->findTypeAndMethodNames() as $typeAndMethod) { if ($typeAndMethod->isUnknown()) { - return []; + continue; } if (!$typeAndMethod->getCertainty()->yes()) { - return []; + continue; } $calledOnType = $typeAndMethod->getType(); diff --git a/tests/PHPStan/Rules/DeadCode/UnusedPrivateMethodRuleTest.php b/tests/PHPStan/Rules/DeadCode/UnusedPrivateMethodRuleTest.php index 246237f97f..dd71120fa8 100644 --- a/tests/PHPStan/Rules/DeadCode/UnusedPrivateMethodRuleTest.php +++ b/tests/PHPStan/Rules/DeadCode/UnusedPrivateMethodRuleTest.php @@ -55,6 +55,10 @@ public function testRule(): void 'Method UnusedPrivateMethod\Lorem::doBaz() is unused.', 99, ], + [ + 'Method UnusedPrivateMethod\Ipsum::doFoo() is unused.', + 115, + ], [ 'Method UnusedPrivateMethod\IgnoredByExtension::bar() is unused.', 181, @@ -133,4 +137,14 @@ public function testBug9765(): void $this->analyse([__DIR__ . '/data/bug-9765.php'], []); } + public function testBug11802(): void + { + $this->analyse([__DIR__ . '/data/bug-11802b.php'], [ + [ + 'Method Bug11802b\HelloWorld::doBar() is unused.', + 10, + ], + ]); + } + } diff --git a/tests/PHPStan/Rules/DeadCode/data/bug-11802.php b/tests/PHPStan/Rules/DeadCode/data/bug-11802.php index 55b90ab49c..94bd9073fa 100644 --- a/tests/PHPStan/Rules/DeadCode/data/bug-11802.php +++ b/tests/PHPStan/Rules/DeadCode/data/bug-11802.php @@ -1,4 +1,4 @@ -= 8.0 namespace Bug11802; diff --git a/tests/PHPStan/Rules/DeadCode/data/bug-11802b.php b/tests/PHPStan/Rules/DeadCode/data/bug-11802b.php new file mode 100644 index 0000000000..ca907961e9 --- /dev/null +++ b/tests/PHPStan/Rules/DeadCode/data/bug-11802b.php @@ -0,0 +1,15 @@ += 8.0 + +namespace Bug11802b; + +class HelloWorld +{ + public function __construct( + ) {} + + private function doBar():void {} + + public function doFoo(HelloWorld $x, string $y): void { + $s = $x->$y(); + } +} From b0093daa72c1a695705ff93e6cc78415772b64b2 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 6 Oct 2024 13:09:52 +0200 Subject: [PATCH 3/4] handle subtractions of a dynamic property fetch --- src/Rules/DeadCode/UnusedPrivatePropertyRule.php | 8 ++++++++ .../Rules/DeadCode/UnusedPrivatePropertyRuleTest.php | 5 ----- tests/PHPStan/Rules/DeadCode/data/bug-11802.php | 9 ++++++--- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/Rules/DeadCode/UnusedPrivatePropertyRule.php b/src/Rules/DeadCode/UnusedPrivatePropertyRule.php index 1035c27e61..46e6f5fc02 100644 --- a/src/Rules/DeadCode/UnusedPrivatePropertyRule.php +++ b/src/Rules/DeadCode/UnusedPrivatePropertyRule.php @@ -11,6 +11,7 @@ use PHPStan\Rules\RuleErrorBuilder; use PHPStan\Type\Constant\ConstantStringType; use PHPStan\Type\ObjectType; +use PHPStan\Type\SubtractableType; use function array_key_exists; use function array_map; use function count; @@ -120,6 +121,13 @@ public function processNode(Node $node, Scope $scope): array $propertyNameType = $usage->getScope()->getType($fetch->name); $strings = $propertyNameType->getConstantStrings(); if (count($strings) === 0) { + // handle subtractions of a dynamic property fetch + foreach($properties as $propertyName => $data) { + if (!(new ConstantStringType($propertyName))->isSuperTypeOf($propertyNameType)->no()) { + unset($properties[$propertyName]); + } + } + continue; } diff --git a/tests/PHPStan/Rules/DeadCode/UnusedPrivatePropertyRuleTest.php b/tests/PHPStan/Rules/DeadCode/UnusedPrivatePropertyRuleTest.php index 485e611ed0..b4d2f4de0b 100644 --- a/tests/PHPStan/Rules/DeadCode/UnusedPrivatePropertyRuleTest.php +++ b/tests/PHPStan/Rules/DeadCode/UnusedPrivatePropertyRuleTest.php @@ -107,11 +107,6 @@ public function testRule(): void 117, $tip, ], - [ - 'Property UnusedPrivateProperty\Ipsum::$foo is never read, only written.', - 136, - $tip, - ], [ 'Property class@anonymous/tests/PHPStan/Rules/DeadCode/data/unused-private-property.php:152::$bar is unused.', 153, diff --git a/tests/PHPStan/Rules/DeadCode/data/bug-11802.php b/tests/PHPStan/Rules/DeadCode/data/bug-11802.php index 94bd9073fa..a97152f385 100644 --- a/tests/PHPStan/Rules/DeadCode/data/bug-11802.php +++ b/tests/PHPStan/Rules/DeadCode/data/bug-11802.php @@ -5,13 +5,16 @@ class HelloWorld { public function __construct( - private bool $isFinal + private bool $isFinal, + private bool $used ) { } - public function doFoo(HelloWorld $x, string $y): void + public function doFoo(HelloWorld $x, $y): void { - $s = $x->{$y()}; + if ($y !== 'isFinal') { + $s = $x->{$y}; + } } } From 0ed13193d48157cad8f7a75d70f1665a14b87cd7 Mon Sep 17 00:00:00 2001 From: Markus Staab Date: Sun, 6 Oct 2024 13:43:16 +0200 Subject: [PATCH 4/4] handle subtractions of a dynamic method call --- src/Rules/DeadCode/UnusedPrivateMethodRule.php | 13 +++++++++++-- src/Rules/DeadCode/UnusedPrivatePropertyRule.php | 9 +++++---- .../Rules/DeadCode/UnusedPrivateMethodRuleTest.php | 4 ---- tests/PHPStan/Rules/DeadCode/data/bug-11802b.php | 8 ++++++-- 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/Rules/DeadCode/UnusedPrivateMethodRule.php b/src/Rules/DeadCode/UnusedPrivateMethodRule.php index fe0735993e..e5b561af65 100644 --- a/src/Rules/DeadCode/UnusedPrivateMethodRule.php +++ b/src/Rules/DeadCode/UnusedPrivateMethodRule.php @@ -84,6 +84,15 @@ public function processNode(Node $node, Scope $scope): array $methodNameType = $callScope->getType($methodCallNode->name); $strings = $methodNameType->getConstantStrings(); if (count($strings) === 0) { + // handle subtractions of a dynamic method call + foreach ($methods as $lowerMethodName => $method) { + if ((new ConstantStringType($method->getNode()->name->toString()))->isSuperTypeOf($methodNameType)->no()) { + continue; + } + + unset($methods[$lowerMethodName]); + } + continue; } @@ -138,10 +147,10 @@ public function processNode(Node $node, Scope $scope): array foreach ($arrayType->getConstantArrays() as $constantArray) { foreach ($constantArray->findTypeAndMethodNames() as $typeAndMethod) { if ($typeAndMethod->isUnknown()) { - continue; + return []; } if (!$typeAndMethod->getCertainty()->yes()) { - continue; + return []; } $calledOnType = $typeAndMethod->getType(); diff --git a/src/Rules/DeadCode/UnusedPrivatePropertyRule.php b/src/Rules/DeadCode/UnusedPrivatePropertyRule.php index 46e6f5fc02..b41185b5c9 100644 --- a/src/Rules/DeadCode/UnusedPrivatePropertyRule.php +++ b/src/Rules/DeadCode/UnusedPrivatePropertyRule.php @@ -11,7 +11,6 @@ use PHPStan\Rules\RuleErrorBuilder; use PHPStan\Type\Constant\ConstantStringType; use PHPStan\Type\ObjectType; -use PHPStan\Type\SubtractableType; use function array_key_exists; use function array_map; use function count; @@ -122,10 +121,12 @@ public function processNode(Node $node, Scope $scope): array $strings = $propertyNameType->getConstantStrings(); if (count($strings) === 0) { // handle subtractions of a dynamic property fetch - foreach($properties as $propertyName => $data) { - if (!(new ConstantStringType($propertyName))->isSuperTypeOf($propertyNameType)->no()) { - unset($properties[$propertyName]); + foreach ($properties as $propertyName => $data) { + if ((new ConstantStringType($propertyName))->isSuperTypeOf($propertyNameType)->no()) { + continue; } + + unset($properties[$propertyName]); } continue; diff --git a/tests/PHPStan/Rules/DeadCode/UnusedPrivateMethodRuleTest.php b/tests/PHPStan/Rules/DeadCode/UnusedPrivateMethodRuleTest.php index dd71120fa8..ca57318a59 100644 --- a/tests/PHPStan/Rules/DeadCode/UnusedPrivateMethodRuleTest.php +++ b/tests/PHPStan/Rules/DeadCode/UnusedPrivateMethodRuleTest.php @@ -55,10 +55,6 @@ public function testRule(): void 'Method UnusedPrivateMethod\Lorem::doBaz() is unused.', 99, ], - [ - 'Method UnusedPrivateMethod\Ipsum::doFoo() is unused.', - 115, - ], [ 'Method UnusedPrivateMethod\IgnoredByExtension::bar() is unused.', 181, diff --git a/tests/PHPStan/Rules/DeadCode/data/bug-11802b.php b/tests/PHPStan/Rules/DeadCode/data/bug-11802b.php index ca907961e9..68bc97f2ac 100644 --- a/tests/PHPStan/Rules/DeadCode/data/bug-11802b.php +++ b/tests/PHPStan/Rules/DeadCode/data/bug-11802b.php @@ -9,7 +9,11 @@ public function __construct( private function doBar():void {} - public function doFoo(HelloWorld $x, string $y): void { - $s = $x->$y(); + private function doFooBar():void {} + + public function doFoo(HelloWorld $x, $y): void { + if ($y !== 'doBar') { + $s = $x->$y(); + } } }