diff --git a/src/Rules/DeadCode/CallToMethodStatementWithoutImpurePointsRule.php b/src/Rules/DeadCode/CallToMethodStatementWithoutImpurePointsRule.php index 5653e1449b..5c4d597cd9 100644 --- a/src/Rules/DeadCode/CallToMethodStatementWithoutImpurePointsRule.php +++ b/src/Rules/DeadCode/CallToMethodStatementWithoutImpurePointsRule.php @@ -38,19 +38,22 @@ public function processNode(Node $node, Scope $scope): array $errors = []; foreach ($node->get(PossiblyPureMethodCallCollector::class) as $filePath => $data) { - foreach ($data as [$className, $method, $line]) { - $className = strtolower($className); + foreach ($data as [$classNames, $method, $line]) { + $originalMethodName = null; + foreach ($classNames as $className) { + $className = strtolower($className); - if (!array_key_exists($className, $methods)) { - continue; - } + if (!array_key_exists($className, $methods)) { + continue 2; + } - $lowerMethod = strtolower($method); - if (!array_key_exists($lowerMethod, $methods[$className])) { - continue; - } + $lowerMethod = strtolower($method); + if (!array_key_exists($lowerMethod, $methods[$className])) { + continue 2; + } - $originalMethodName = $methods[$className][$lowerMethod]; + $originalMethodName = $methods[$className][$lowerMethod]; + } $errors[] = RuleErrorBuilder::message(sprintf( 'Call to method %s() on a separate line has no effect.', diff --git a/src/Rules/DeadCode/PossiblyPureMethodCallCollector.php b/src/Rules/DeadCode/PossiblyPureMethodCallCollector.php index d63f25d263..256d2bf781 100644 --- a/src/Rules/DeadCode/PossiblyPureMethodCallCollector.php +++ b/src/Rules/DeadCode/PossiblyPureMethodCallCollector.php @@ -6,10 +6,9 @@ use PhpParser\Node\Stmt\Expression; use PHPStan\Analyser\Scope; use PHPStan\Collectors\Collector; -use function count; /** - * @implements Collector + * @implements Collector, string, int}> */ class PossiblyPureMethodCallCollector implements Collector { @@ -34,32 +33,42 @@ public function processNode(Node $node, Scope $scope) $methodName = $node->expr->name->toString(); $calledOnType = $scope->getType($node->expr->var); - $methodReflection = $scope->getMethodReflection($calledOnType, $methodName); - if ($methodReflection === null) { + if (!$calledOnType->hasMethod($methodName)->yes()) { return null; } - if ( - !$methodReflection->isPrivate() - && !$methodReflection->isFinal()->yes() - && !$methodReflection->getDeclaringClass()->isFinal() - ) { - $typeClassReflections = $calledOnType->getObjectClassReflections(); - if (count($typeClassReflections) !== 1) { + + $classNames = []; + $methodReflection = null; + foreach ($calledOnType->getObjectClassReflections() as $classReflection) { + if (!$classReflection->hasMethod($methodName)) { return null; } - if (!$typeClassReflections[0]->isFinal()) { + $methodReflection = $classReflection->getMethod($methodName, $scope); + if ( + !$methodReflection->isPrivate() + && !$methodReflection->isFinal()->yes() + && !$methodReflection->getDeclaringClass()->isFinal() + ) { + if (!$classReflection->isFinal()) { + return null; + } + } + if (!$methodReflection->isPure()->maybe()) { return null; } + if (!$methodReflection->hasSideEffects()->maybe()) { + return null; + } + + $classNames[] = $methodReflection->getDeclaringClass()->getName(); } - if (!$methodReflection->isPure()->maybe()) { - return null; - } - if (!$methodReflection->hasSideEffects()->maybe()) { + + if ($methodReflection === null) { return null; } - return [$methodReflection->getDeclaringClass()->getName(), $methodReflection->getName(), $node->getStartLine()]; + return [$classNames, $methodReflection->getName(), $node->getStartLine()]; } } diff --git a/tests/PHPStan/Rules/DeadCode/CallToMethodStatementWithoutImpurePointsRuleTest.php b/tests/PHPStan/Rules/DeadCode/CallToMethodStatementWithoutImpurePointsRuleTest.php index 4b93edaba2..ad45f6273c 100644 --- a/tests/PHPStan/Rules/DeadCode/CallToMethodStatementWithoutImpurePointsRuleTest.php +++ b/tests/PHPStan/Rules/DeadCode/CallToMethodStatementWithoutImpurePointsRuleTest.php @@ -58,6 +58,16 @@ public function testRule(): void ]); } + public function testBug11011(): void + { + $this->analyse([__DIR__ . '/data/bug-11011.php'], [ + [ + 'Call to method Bug11011\AnotherPureImpl::doFoo() on a separate line has no effect.', + 32, + ], + ]); + } + protected function getCollectors(): array { return [ diff --git a/tests/PHPStan/Rules/DeadCode/data/bug-11011.php b/tests/PHPStan/Rules/DeadCode/data/bug-11011.php new file mode 100644 index 0000000000..d243d1dd0b --- /dev/null +++ b/tests/PHPStan/Rules/DeadCode/data/bug-11011.php @@ -0,0 +1,35 @@ +doFoo(); + return true; + } + + function doBar2(PureImpl|AnotherPureImpl $f): bool { + $f->doFoo(); + return true; + } +}