diff --git a/src/Rules/DeadCode/UnusedPrivateConstantRule.php b/src/Rules/DeadCode/UnusedPrivateConstantRule.php index 024cbbf322..504a0cb616 100644 --- a/src/Rules/DeadCode/UnusedPrivateConstantRule.php +++ b/src/Rules/DeadCode/UnusedPrivateConstantRule.php @@ -8,6 +8,7 @@ use PHPStan\Rules\Constants\AlwaysUsedClassConstantsExtensionProvider; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; +use PHPStan\Type\ObjectType; use function sprintf; /** @@ -32,6 +33,7 @@ public function processNode(Node $node, Scope $scope): array } $classReflection = $node->getClassReflection(); + $classType = new ObjectType($classReflection->getName()); $constants = []; foreach ($node->getConstants() as $constant) { @@ -68,10 +70,16 @@ public function processNode(Node $node, Scope $scope): array $constantReflection = $fetchScope->getConstantReflection($fetchedOnClass, $fetchNode->name->toString()); if ($constantReflection === null) { + if (!$classType->isSuperTypeOf($fetchedOnClass)->no()) { + unset($constants[$fetchNode->name->toString()]); + } continue; } if ($constantReflection->getDeclaringClass()->getName() !== $classReflection->getName()) { + if (!$classType->isSuperTypeOf($fetchedOnClass)->no()) { + unset($constants[$fetchNode->name->toString()]); + } continue; } diff --git a/src/Rules/DeadCode/UnusedPrivateMethodRule.php b/src/Rules/DeadCode/UnusedPrivateMethodRule.php index 83d2f2313f..df805f3731 100644 --- a/src/Rules/DeadCode/UnusedPrivateMethodRule.php +++ b/src/Rules/DeadCode/UnusedPrivateMethodRule.php @@ -10,6 +10,7 @@ use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; use PHPStan\Type\Constant\ConstantStringType; +use PHPStan\Type\ObjectType; use function array_map; use function count; use function sprintf; @@ -32,6 +33,7 @@ public function processNode(Node $node, Scope $scope): array return []; } $classReflection = $node->getClassReflection(); + $classType = new ObjectType($classReflection->getName()); $constructor = null; if ($classReflection->hasConstructor()) { $constructor = $classReflection->getConstructor(); @@ -93,9 +95,15 @@ public function processNode(Node $node, Scope $scope): array foreach ($methodNames as $methodName) { $methodReflection = $callScope->getMethodReflection($calledOnType, $methodName); if ($methodReflection === null) { + if (!$classType->isSuperTypeOf($calledOnType)->no()) { + unset($methods[strtolower($methodName)]); + } continue; } if ($methodReflection->getDeclaringClass()->getName() !== $classReflection->getName()) { + if (!$classType->isSuperTypeOf($calledOnType)->no()) { + unset($methods[strtolower($methodName)]); + } continue; } if ($inMethod->getName() === $methodName) { diff --git a/src/Rules/DeadCode/UnusedPrivatePropertyRule.php b/src/Rules/DeadCode/UnusedPrivatePropertyRule.php index 98da6a68ed..2678b2e42b 100644 --- a/src/Rules/DeadCode/UnusedPrivatePropertyRule.php +++ b/src/Rules/DeadCode/UnusedPrivatePropertyRule.php @@ -10,6 +10,7 @@ use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; use PHPStan\Type\Constant\ConstantStringType; +use PHPStan\Type\ObjectType; use function array_key_exists; use function array_map; use function count; @@ -46,6 +47,7 @@ public function processNode(Node $node, Scope $scope): array return []; } $classReflection = $node->getClassReflection(); + $classType = new ObjectType($classReflection->getName()); $properties = []; foreach ($node->getProperties() as $property) { if (!$property->isPrivate()) { @@ -139,9 +141,23 @@ public function processNode(Node $node, Scope $scope): array } $propertyReflection = $usage->getScope()->getPropertyReflection($fetchedOnType, $propertyName); if ($propertyReflection === null) { + if (!$classType->isSuperTypeOf($fetchedOnType)->no()) { + if ($usage instanceof PropertyRead) { + $properties[$propertyName]['read'] = true; + } else { + $properties[$propertyName]['written'] = true; + } + } continue; } if ($propertyReflection->getDeclaringClass()->getName() !== $classReflection->getName()) { + if (!$classType->isSuperTypeOf($fetchedOnType)->no()) { + if ($usage instanceof PropertyRead) { + $properties[$propertyName]['read'] = true; + } else { + $properties[$propertyName]['written'] = true; + } + } continue; } diff --git a/tests/PHPStan/Rules/DeadCode/UnusedPrivateConstantRuleTest.php b/tests/PHPStan/Rules/DeadCode/UnusedPrivateConstantRuleTest.php index 73076bf285..f4940f2af3 100644 --- a/tests/PHPStan/Rules/DeadCode/UnusedPrivateConstantRuleTest.php +++ b/tests/PHPStan/Rules/DeadCode/UnusedPrivateConstantRuleTest.php @@ -92,4 +92,9 @@ public function testBug9005(): void $this->analyse([__DIR__ . '/data/bug-9005.php'], []); } + public function testBug9765(): void + { + $this->analyse([__DIR__ . '/data/bug-9765.php'], []); + } + } diff --git a/tests/PHPStan/Rules/DeadCode/UnusedPrivateMethodRuleTest.php b/tests/PHPStan/Rules/DeadCode/UnusedPrivateMethodRuleTest.php index ac7ff6d7cb..83a1e8ef83 100644 --- a/tests/PHPStan/Rules/DeadCode/UnusedPrivateMethodRuleTest.php +++ b/tests/PHPStan/Rules/DeadCode/UnusedPrivateMethodRuleTest.php @@ -109,4 +109,9 @@ public function testBug6039(): void $this->analyse([__DIR__ . '/data/bug-6039.php'], []); } + public function testBug9765(): void + { + $this->analyse([__DIR__ . '/data/bug-9765.php'], []); + } + } diff --git a/tests/PHPStan/Rules/DeadCode/UnusedPrivatePropertyRuleTest.php b/tests/PHPStan/Rules/DeadCode/UnusedPrivatePropertyRuleTest.php index f93caea9f0..dbabf5526c 100644 --- a/tests/PHPStan/Rules/DeadCode/UnusedPrivatePropertyRuleTest.php +++ b/tests/PHPStan/Rules/DeadCode/UnusedPrivatePropertyRuleTest.php @@ -286,4 +286,11 @@ public function testBug9409(): void $this->analyse([__DIR__ . '/data/bug-9409.php'], []); } + public function testBug9765(): void + { + $this->alwaysWrittenTags = []; + $this->alwaysReadTags = []; + $this->analyse([__DIR__ . '/data/bug-9765.php'], []); + } + } diff --git a/tests/PHPStan/Rules/DeadCode/data/bug-9765.php b/tests/PHPStan/Rules/DeadCode/data/bug-9765.php new file mode 100644 index 0000000000..10fbbcf45d --- /dev/null +++ b/tests/PHPStan/Rules/DeadCode/data/bug-9765.php @@ -0,0 +1,71 @@ +add($arg); + }; + } + + public function do(): void + { + $c = self::runner(); + print $c->bindTo($this)(5); + } + + private function add(int $a): int + { + return $a + 1; + } + +} + +class HelloWorld2 +{ + + /** @var int */ + private $foo; + + public static function runner(): \Closure + { + return function (int $arg) { + if ($arg > 0) { + $this->foo = $arg; + } else { + echo $this->foo; + } + }; + } + + public function do(): void + { + $c = self::runner(); + print $c->bindTo($this)(5); + } + +} + +class HelloWorld3 +{ + + private const FOO = 1; + + public static function runner(): \Closure + { + return function (int $arg) { + echo $this::FOO; + }; + } + + public function do(): void + { + $c = self::runner(); + print $c->bindTo($this)(5); + } + +}