Skip to content

Commit

Permalink
UnusedPrivateMethodRule - fix detection with [$this, 'method']
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Jul 16, 2020
1 parent d7d2004 commit 192b20a
Show file tree
Hide file tree
Showing 7 changed files with 98 additions and 4 deletions.
4 changes: 4 additions & 0 deletions src/Analyser/NodeScopeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,10 @@ private function processStmtNode(
}
return;
}
if ($node instanceof Array_ && count($node->items) === 2) {
$methodCalls[] = new \PHPStan\Node\Method\MethodCall($node, $scope);
return;
}
if ($node instanceof Expr\ClassConstFetch) {
$constantFetches[] = new ClassConstantFetch($node, $scope);
return;
Expand Down
4 changes: 4 additions & 0 deletions src/Node/ClassPropertiesNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace PHPStan\Node;

use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
Expand Down Expand Up @@ -185,6 +186,9 @@ private function getMethodsCalledFromConstructor(
$originalCount = count($methods);
foreach ($methodCalls as $methodCall) {
$methodCallNode = $methodCall->getNode();
if ($methodCallNode instanceof Array_) {
continue;
}
if (!$methodCallNode->name instanceof Identifier) {
continue;
}
Expand Down
7 changes: 4 additions & 3 deletions src/Node/Method/MethodCall.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,20 @@

namespace PHPStan\Node\Method;

use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\StaticCall;
use PHPStan\Analyser\Scope;

class MethodCall
{

/** @var \PhpParser\Node\Expr\MethodCall|StaticCall */
/** @var \PhpParser\Node\Expr\MethodCall|StaticCall|Array_ */
private $node;

private Scope $scope;

/**
* @param \PhpParser\Node\Expr\MethodCall|StaticCall$node
* @param \PhpParser\Node\Expr\MethodCall|StaticCall|Array_ $node
* @param Scope $scope
*/
public function __construct($node, Scope $scope)
Expand All @@ -24,7 +25,7 @@ public function __construct($node, Scope $scope)
}

/**
* @return \PhpParser\Node\Expr\MethodCall|StaticCall
* @return \PhpParser\Node\Expr\MethodCall|StaticCall|Array_
*/
public function getNode()
{
Expand Down
43 changes: 43 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\Reflection\MethodReflection;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\Type\Constant\ConstantArrayType;
use PHPStan\Type\MixedType;
use PHPStan\Type\ObjectType;

Expand Down Expand Up @@ -49,8 +50,13 @@ public function processNode(Node $node, Scope $scope): array
$methods[$method->name->toString()] = $method;
}

$arrayCalls = [];
foreach ($node->getMethodCalls() as $methodCall) {
$methodCallNode = $methodCall->getNode();
if ($methodCallNode instanceof Node\Expr\Array_) {
$arrayCalls[] = $methodCall;
continue;
}
if (!$methodCallNode->name instanceof Identifier) {
continue;
}
Expand Down Expand Up @@ -80,6 +86,43 @@ public function processNode(Node $node, Scope $scope): array
unset($methods[$methodName]);
}

if (count($methods) > 0) {
foreach ($arrayCalls as $arrayCall) {
/** @var Node\Expr\Array_ $array */
$array = $arrayCall->getNode();
$arrayScope = $arrayCall->getScope();
$arrayType = $scope->getType($array);
if (!$arrayType instanceof ConstantArrayType) {
continue;
}
$typeAndMethod = $arrayType->findTypeAndMethodName();
if ($typeAndMethod === null) {
continue;
}
if ($typeAndMethod->isUnknown()) {
return [];
}
if (!$typeAndMethod->getCertainty()->yes()) {
return [];
}
$calledOnType = $typeAndMethod->getType();
if ($classType->isSuperTypeOf($calledOnType)->no()) {
continue;
}
if ($calledOnType instanceof MixedType) {
continue;
}
$inMethod = $arrayScope->getFunction();
if (!$inMethod instanceof MethodReflection) {
continue;
}
if ($inMethod->getName() === $typeAndMethod->getMethod()) {
continue;
}
unset($methods[$typeAndMethod->getMethod()]);
}
}

$errors = [];
foreach ($methods as $methodName => $methodNode) {
$errors[] = RuleErrorBuilder::message(sprintf('Class %s has an unused method %s().', $classReflection->getDisplayName(), $methodName))->line($methodNode->getLine())->build();
Expand Down
2 changes: 1 addition & 1 deletion src/Type/Constant/ConstantArrayType.php
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ public function getCallableParametersAcceptors(ClassMemberAccessAnswerer $scope)
return $method->getVariants();
}

private function findTypeAndMethodName(): ?ConstantArrayTypeAndMethod
public function findTypeAndMethodName(): ?ConstantArrayTypeAndMethod
{
if (count($this->keyTypes) !== 2) {
return null;
Expand Down
4 changes: 4 additions & 0 deletions tests/PHPStan/Rules/DeadCode/UnusedPrivateMethodRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ public function testRule(): void
'Class UnusedPrivateMethod\Foo has an unused method unusedStaticMethod().',
44,
],
[
'Class UnusedPrivateMethod\Bar has an unused method doBaz().',
59,
],
]);
}

Expand Down
38 changes: 38 additions & 0 deletions tests/PHPStan/Rules/DeadCode/data/unused-private-method.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,41 @@ private static function unusedStaticMethod()
}

}

class Bar
{

private function doFoo()
{

}

private function doBaz()
{
$cb = [$this, 'doBaz'];
$cb();
}

public function doBar()
{
$cb = [$this, 'doFoo'];
$cb();
}

}

class Baz
{

private function doFoo()
{

}

public function doBar(string $name)
{
$cb = [$this, $name];
$cb();
}

}

0 comments on commit 192b20a

Please sign in to comment.