diff --git a/config/set/dead-code/dead-code.yaml b/config/set/dead-code/dead-code.yaml index 9482142212e9..750419a447d7 100644 --- a/config/set/dead-code/dead-code.yaml +++ b/config/set/dead-code/dead-code.yaml @@ -35,3 +35,4 @@ services: Rector\DeadCode\Rector\ClassConst\RemoveUnusedClassConstantRector: null Rector\DeadCode\Rector\Assign\RemoveUnusedVariableAssignRector: null Rector\DeadCode\Rector\FunctionLike\RemoveDuplicatedIfReturnRector: null + Rector\DeadCode\Rector\Function_\RemoveUnusedFunctionRector: null diff --git a/docs/AllRectorsOverview.md b/docs/AllRectorsOverview.md index 0370acceff30..ec7e4eb788d3 100644 --- a/docs/AllRectorsOverview.md +++ b/docs/AllRectorsOverview.md @@ -1,4 +1,4 @@ -# All 467 Rectors Overview +# All 468 Rectors Overview - [Projects](#projects) - [General](#general) @@ -3062,6 +3062,27 @@ Remove unused key in foreach
+### `RemoveUnusedFunctionRector` + +- class: [`Rector\DeadCode\Rector\Function_\RemoveUnusedFunctionRector`](/../master/rules/dead-code/src/Rector/Function_/RemoveUnusedFunctionRector.php) +- [test fixtures](/../master/rules/dead-code/tests/Rector/Function_/RemoveUnusedFunctionRector/Fixture) + +Remove unused function + +```diff +-function removeMe() +-{ +-} +- + function useMe() + { + } + + useMe(); +``` + +
+ ### `RemoveUnusedParameterRector` - class: [`Rector\DeadCode\Rector\ClassMethod\RemoveUnusedParameterRector`](/../master/rules/dead-code/src/Rector/ClassMethod/RemoveUnusedParameterRector.php) diff --git a/packages/node-collector/src/NodeCollector/ParsedFunctionLikeNodeCollector.php b/packages/node-collector/src/NodeCollector/ParsedFunctionLikeNodeCollector.php index 9438eceb3e2a..9178b960a1fb 100644 --- a/packages/node-collector/src/NodeCollector/ParsedFunctionLikeNodeCollector.php +++ b/packages/node-collector/src/NodeCollector/ParsedFunctionLikeNodeCollector.php @@ -7,6 +7,7 @@ use PhpParser\Node; use PhpParser\Node\Expr\Array_; use PhpParser\Node\Expr\ClassConstFetch; +use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Expr\Variable; @@ -36,6 +37,11 @@ final class ParsedFunctionLikeNodeCollector */ private $functionsByName = []; + /** + * @var FuncCall[][] + */ + private $funcCallsByName = []; + /** * @var MethodCall[][][]|StaticCall[][][] */ @@ -100,12 +106,13 @@ public function collect(Node $node): void if ($node instanceof Function_) { $functionName = $this->nodeNameResolver->getName($node); - if ($functionName === null) { - return; - } - $this->functionsByName[$functionName] = $node; } + + if ($node instanceof FuncCall) { + $functionName = $this->nodeNameResolver->getName($node); + $this->funcCallsByName[$functionName][] = $node; + } } public function findFunction(string $name): ?Function_ @@ -145,6 +152,11 @@ public function findMethod(string $className, string $methodName): ?ClassMethod return null; } + public function isFunctionUsed(string $functionName): bool + { + return isset($this->funcCallsByName[$functionName]); + } + private function addMethod(ClassMethod $classMethod): void { $className = $classMethod->getAttribute(AttributeKey::CLASS_NAME); diff --git a/packages/node-name-resolver/src/NodeNameResolver/FuncCallNameResolver.php b/packages/node-name-resolver/src/NodeNameResolver/FuncCallNameResolver.php index 681eca0b38ad..56bcd70b5776 100644 --- a/packages/node-name-resolver/src/NodeNameResolver/FuncCallNameResolver.php +++ b/packages/node-name-resolver/src/NodeNameResolver/FuncCallNameResolver.php @@ -31,7 +31,6 @@ public function resolve(Node $node): ?string } $functionName = $node->name; - if (! $functionName instanceof Name) { return (string) $functionName; } diff --git a/packages/node-name-resolver/src/NodeNameResolver/FunctionNameResolver.php b/packages/node-name-resolver/src/NodeNameResolver/FunctionNameResolver.php new file mode 100644 index 000000000000..595d62f103d8 --- /dev/null +++ b/packages/node-name-resolver/src/NodeNameResolver/FunctionNameResolver.php @@ -0,0 +1,34 @@ +name; + + $namespaceName = $node->getAttribute(AttributeKey::NAMESPACE_NAME); + + if ($namespaceName) { + return $namespaceName . '\\' . $bareName; + } + + return $bareName; + } +} diff --git a/rules/dead-code/src/Rector/Function_/RemoveUnusedFunctionRector.php b/rules/dead-code/src/Rector/Function_/RemoveUnusedFunctionRector.php new file mode 100644 index 000000000000..0db746f02cd6 --- /dev/null +++ b/rules/dead-code/src/Rector/Function_/RemoveUnusedFunctionRector.php @@ -0,0 +1,81 @@ +parsedFunctionLikeNodeCollector = $parsedFunctionLikeNodeCollector; + } + + public function getDefinition(): RectorDefinition + { + return new RectorDefinition('Remove unused function', [ + new CodeSample( + <<<'PHP' +function removeMe() +{ +} + +function useMe() +{ +} + +useMe(); +PHP +, + <<<'PHP' +function useMe() +{ +} + +useMe(); +PHP + + ), + ]); + } + + /** + * @return string[] + */ + public function getNodeTypes(): array + { + return [Function_::class]; + } + + /** + * @param Function_ $node + */ + public function refactor(Node $node): ?Node + { + /** @var string $functionName */ + $functionName = $this->getName($node); + + if ($this->parsedFunctionLikeNodeCollector->isFunctionUsed($functionName)) { + return null; + } + + $this->removeNode($node); + + return $node; + } +} diff --git a/rules/dead-code/src/Rector/MethodCall/RemoveDefaultArgumentValueRector.php b/rules/dead-code/src/Rector/MethodCall/RemoveDefaultArgumentValueRector.php index f4a024521b8e..8d108b58ceaf 100644 --- a/rules/dead-code/src/Rector/MethodCall/RemoveDefaultArgumentValueRector.php +++ b/rules/dead-code/src/Rector/MethodCall/RemoveDefaultArgumentValueRector.php @@ -116,7 +116,7 @@ private function shouldSkip(Node $node): bool return true; } - $functionName = $this->getName($node->name); + $functionName = $this->getName($node); if ($functionName === null) { return false; } @@ -137,8 +137,7 @@ private function shouldSkip(Node $node): bool */ private function resolveDefaultValuesFromCall(Node $node): array { - /** @var string|null $nodeName */ - $nodeName = $this->getName($node->name); + $nodeName = $this->resolveNodeName($node); if ($nodeName === null) { return []; } @@ -242,4 +241,16 @@ private function resolveDefaultParamValuesFromFunctionLike(FunctionLike $functio return $defaultValues; } + + /** + * @param StaticCall|FuncCall|MethodCall $node + */ + private function resolveNodeName(Node $node): ?string + { + if ($node instanceof FuncCall) { + return $this->getName($node); + } + + return $this->getName($node->name); + } } diff --git a/rules/dead-code/tests/Rector/Function_/RemoveUnusedFunctionRector/Fixture/fixture.php.inc b/rules/dead-code/tests/Rector/Function_/RemoveUnusedFunctionRector/Fixture/fixture.php.inc new file mode 100644 index 000000000000..2be90bbd4c6e --- /dev/null +++ b/rules/dead-code/tests/Rector/Function_/RemoveUnusedFunctionRector/Fixture/fixture.php.inc @@ -0,0 +1,26 @@ + +----- + diff --git a/rules/dead-code/tests/Rector/Function_/RemoveUnusedFunctionRector/RemoveUnusedFunctionRectorTest.php b/rules/dead-code/tests/Rector/Function_/RemoveUnusedFunctionRector/RemoveUnusedFunctionRectorTest.php new file mode 100644 index 000000000000..bd1ab30407ef --- /dev/null +++ b/rules/dead-code/tests/Rector/Function_/RemoveUnusedFunctionRector/RemoveUnusedFunctionRectorTest.php @@ -0,0 +1,30 @@ +doTestFile($file); + } + + public function provideData(): Iterator + { + return $this->yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + protected function getRectorClass(): string + { + return RemoveUnusedFunctionRector::class; + } +}