Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions src/Rules/DeadCode/UnusedPrivateMethodRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use PHPStan\DependencyInjection\ExtensionsCollection;
use PHPStan\DependencyInjection\RegisteredRule;
use PHPStan\Node\ClassMethodsNode;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Rules\Methods\AlwaysUsedMethodExtension;
use PHPStan\Rules\Rule;
Expand Down Expand Up @@ -70,6 +71,10 @@ public function processNode(Node $node, Scope $scope): array
continue;
}

if ($this->isOverridingPrivateTraitMethod($classReflection, $methodName)) {
continue;
}

$methodReflection = $classReflection->getNativeMethod($methodName);
foreach ($this->extensions->getAll() as $extension) {
if ($extension->isAlwaysUsed($methodReflection)) {
Expand Down Expand Up @@ -203,4 +208,26 @@ public function processNode(Node $node, Scope $scope): array
return $errors;
}

/**
* A private method overriding a private method of a used trait is called from
* the trait's own methods. Those call sites are invisible when the trait is not
* part of the analysed files.
*/
private function isOverridingPrivateTraitMethod(ClassReflection $classReflection, string $methodName): bool
{
foreach ($classReflection->getTraits() as $trait) {
if (!$trait->hasNativeMethod($methodName)) {
continue;
}

if (!$trait->getNativeMethod($methodName)->isPrivate()) {
continue;
}

return true;
}

return false;
}

}
10 changes: 10 additions & 0 deletions tests/PHPStan/Rules/DeadCode/UnusedPrivateMethodRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,16 @@ public function testBug11802(): void
]);
}

public function testBug12201(): void
{
$this->analyse([__DIR__ . '/data/bug-12201.php'], [
[
'Method Bug12201\AnotherKernel::doNothing() is unused.',
24,
],
]);
}

public function testBug14880(): void
{
$this->analyse([__DIR__ . '/data/bug-14880.php'], []);
Expand Down
27 changes: 27 additions & 0 deletions tests/PHPStan/Rules/DeadCode/data/bug-12201-traits.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php declare(strict_types = 1);

namespace Bug12201;

trait KernelTrait
{
/**
* @return string[]
*/
private function getAllowedEnvs(): array
{
return [];
}

/**
* @return string[]
*/
protected function getKernelParameters(): array
{
return $this->getAllowedEnvs();
}
}

trait MicroKernelTrait
{
use KernelTrait;
}
27 changes: 27 additions & 0 deletions tests/PHPStan/Rules/DeadCode/data/bug-12201.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php declare(strict_types = 1);

namespace Bug12201;

// The traits live in bug-12201-traits.php which is not analysed on purpose:
// it stands for a dependency living outside of the analysed paths.
class AppKernel
{
use MicroKernelTrait;

/**
* @return list<string>
*/
private function getAllowedEnvs(): array
{
return ['prod', 'dev', 'test'];
}
}

class AnotherKernel
{
use MicroKernelTrait;

private function doNothing(): void
{
}
}
Loading