From 380eaa35176cda1ce84afeae9a24c7f467b0501f Mon Sep 17 00:00:00 2001 From: bilougit Date: Sat, 9 May 2020 20:13:19 +0200 Subject: [PATCH] Trust static method call If the last statement is a call to a static method that takes the catched exception as an argument, consider this as a rethrow. --- src/Rules/Exceptions/MustRethrowRule.php | 47 +++++++++++++++++++++++- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/src/Rules/Exceptions/MustRethrowRule.php b/src/Rules/Exceptions/MustRethrowRule.php index 11393de..f237758 100644 --- a/src/Rules/Exceptions/MustRethrowRule.php +++ b/src/Rules/Exceptions/MustRethrowRule.php @@ -6,9 +6,12 @@ use Exception; use function in_array; use PhpParser\Node; +use PhpParser\NodeFinder; use PhpParser\Node\Stmt\Catch_; use PhpParser\NodeTraverser; use PhpParser\NodeVisitorAbstract; +use PhpParser\Node\Expr\Variable; +use PhpParser\Node\Expr\StaticCall; use PHPStan\Analyser\Scope; use PHPStan\Broker\Broker; use PHPStan\Rules\Rule; @@ -49,15 +52,34 @@ public function processNode(Node $node, Scope $scope): array return []; } + $exceptionVarName = $node->var->name; + // Let's visit and find a throw. - $visitor = new class() extends NodeVisitorAbstract { + $visitor = new class($exceptionVarName) extends NodeVisitorAbstract { /** * @var bool */ private $throwFound = false; + /** + * @var bool + */ + private $throwFoundProbably = false; + + private $exceptionVarName; + + public function __construct(string $exceptionVarName) + { + $this->exceptionVarName = $exceptionVarName; + } + public function leaveNode(Node $node) { + // Only rethrow through static methods are allowed + if ($node instanceof StaticCall) { + $this->throwFoundProbably = $this->isProbablyAThrow($node); + } + if ($node instanceof Node\Stmt\Throw_) { $this->throwFound = true; } @@ -71,6 +93,27 @@ public function isThrowFound(): bool { return $this->throwFound; } + + /** + * @return bool + */ + public function isThrowProbablyFound(): bool + { + return $this->throwFoundProbably; + } + + private function isProbablyAThrow(Node $node) + { + if (!$args = $node->args) { + return false; + } + + $varArgs = array_filter($args, function ($arg) { + return $arg->value instanceof Variable && $arg->value->name === $this->exceptionVarName ; + }); + + return 0 !== count($varArgs); + } }; $traverser = new NodeTraverser(); @@ -81,7 +124,7 @@ public function isThrowFound(): bool $errors = []; - if (!$visitor->isThrowFound()) { + if (!$visitor->isThrowFound() && !$visitor->isThrowProbablyFound()) { $errors[] = sprintf('%scaught "%s" must be rethrown. Either catch a more specific exception or add a "throw" clause in the "catch" block to propagate the exception. More info: http://bit.ly/failloud', PrefixGenerator::generatePrefix($scope), $exceptionType); }