From 165504cf9c4ae6e2dcfc2c3570a631441a411615 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Sat, 11 Sep 2021 22:33:11 +0200 Subject: [PATCH] Bleeding edge - teach CatchWithUnthrownExceptionRule everything what DeadCatchRule does --- conf/config.level4.neon | 8 +++++++- src/Analyser/NodeScopeResolver.php | 2 +- src/Node/CatchWithUnthrownExceptionNode.php | 10 +++++++++- .../Exceptions/CatchWithUnthrownExceptionRule.php | 9 +++++++++ src/Rules/Exceptions/DeadCatchRule.php | 11 +++++++++++ .../Exceptions/CatchWithUnthrownExceptionRuleTest.php | 10 ++++++++++ tests/PHPStan/Rules/Exceptions/data/dead-catch.php | 4 ++-- 7 files changed, 49 insertions(+), 5 deletions(-) diff --git a/conf/config.level4.neon b/conf/config.level4.neon index 16fdac9f1b..cb7540c5eb 100644 --- a/conf/config.level4.neon +++ b/conf/config.level4.neon @@ -6,7 +6,6 @@ rules: - PHPStan\Rules\Comparison\NumberComparisonOperatorsConstantConditionRule - PHPStan\Rules\DeadCode\NoopRule - PHPStan\Rules\DeadCode\UnreachableStatementRule - - PHPStan\Rules\Exceptions\DeadCatchRule - PHPStan\Rules\Functions\CallToFunctionStamentWithoutSideEffectsRule - PHPStan\Rules\Methods\CallToConstructorStatementWithoutSideEffectsRule - PHPStan\Rules\Methods\CallToMethodStamentWithoutSideEffectsRule @@ -153,6 +152,13 @@ services: - class: PHPStan\Rules\Exceptions\CatchWithUnthrownExceptionRule + - + class: PHPStan\Rules\Exceptions\DeadCatchRule + arguments: + bleedingEdge: %featureToggles.bleedingEdge% + tags: + - phpstan.rules.rule + - class: PHPStan\Rules\Exceptions\OverwrittenExitPointByFinallyRule diff --git a/src/Analyser/NodeScopeResolver.php b/src/Analyser/NodeScopeResolver.php index 70d9306458..05765a86f8 100644 --- a/src/Analyser/NodeScopeResolver.php +++ b/src/Analyser/NodeScopeResolver.php @@ -1233,7 +1233,7 @@ private function processStmtNode( } if (count($throwableThrowPoints) === 0) { - $nodeCallback(new CatchWithUnthrownExceptionNode($catchNode, $catchType), $scope); + $nodeCallback(new CatchWithUnthrownExceptionNode($catchNode, $catchType, $originalCatchType), $scope); continue; } diff --git a/src/Node/CatchWithUnthrownExceptionNode.php b/src/Node/CatchWithUnthrownExceptionNode.php index 01ad10629b..6c367e32d4 100644 --- a/src/Node/CatchWithUnthrownExceptionNode.php +++ b/src/Node/CatchWithUnthrownExceptionNode.php @@ -14,11 +14,14 @@ class CatchWithUnthrownExceptionNode extends NodeAbstract implements VirtualNode private Type $caughtType; - public function __construct(Catch_ $originalNode, Type $caughtType) + private Type $originalCaughtType; + + public function __construct(Catch_ $originalNode, Type $caughtType, Type $originalCaughtType) { parent::__construct($originalNode->getAttributes()); $this->originalNode = $originalNode; $this->caughtType = $caughtType; + $this->originalCaughtType = $originalCaughtType; } public function getOriginalNode(): Catch_ @@ -31,6 +34,11 @@ public function getCaughtType(): Type return $this->caughtType; } + public function getOriginalCaughtType(): Type + { + return $this->originalCaughtType; + } + public function getType(): string { return 'PHPStan_Node_CatchWithUnthrownExceptionNode'; diff --git a/src/Rules/Exceptions/CatchWithUnthrownExceptionRule.php b/src/Rules/Exceptions/CatchWithUnthrownExceptionRule.php index 025e87690d..5e9040e761 100644 --- a/src/Rules/Exceptions/CatchWithUnthrownExceptionRule.php +++ b/src/Rules/Exceptions/CatchWithUnthrownExceptionRule.php @@ -7,6 +7,7 @@ use PHPStan\Node\CatchWithUnthrownExceptionNode; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; +use PHPStan\Type\NeverType; use PHPStan\Type\VerbosityLevel; /** @@ -22,6 +23,14 @@ public function getNodeType(): string public function processNode(Node $node, Scope $scope): array { + if ($node->getCaughtType() instanceof NeverType) { + return [ + RuleErrorBuilder::message( + sprintf('Dead catch - %s is already caught above.', $node->getOriginalCaughtType()->describe(VerbosityLevel::typeOnly())) + )->line($node->getLine())->build(), + ]; + } + return [ RuleErrorBuilder::message( sprintf('Dead catch - %s is never thrown in the try block.', $node->getCaughtType()->describe(VerbosityLevel::typeOnly())) diff --git a/src/Rules/Exceptions/DeadCatchRule.php b/src/Rules/Exceptions/DeadCatchRule.php index d05a634792..1761bbb637 100644 --- a/src/Rules/Exceptions/DeadCatchRule.php +++ b/src/Rules/Exceptions/DeadCatchRule.php @@ -17,6 +17,13 @@ class DeadCatchRule implements Rule { + private bool $bleedingEdge; + + public function __construct(bool $bleedingEdge = false) + { + $this->bleedingEdge = $bleedingEdge; + } + public function getNodeType(): string { return Node\Stmt\TryCatch::class; @@ -24,6 +31,10 @@ public function getNodeType(): string public function processNode(Node $node, Scope $scope): array { + if ($this->bleedingEdge) { + return []; + } + $catchTypes = array_map(static function (Node\Stmt\Catch_ $catch): Type { return TypeCombinator::union(...array_map(static function (Node\Name $className): ObjectType { return new ObjectType($className->toString()); diff --git a/tests/PHPStan/Rules/Exceptions/CatchWithUnthrownExceptionRuleTest.php b/tests/PHPStan/Rules/Exceptions/CatchWithUnthrownExceptionRuleTest.php index 1c1ddb0a44..15c4930e07 100644 --- a/tests/PHPStan/Rules/Exceptions/CatchWithUnthrownExceptionRuleTest.php +++ b/tests/PHPStan/Rules/Exceptions/CatchWithUnthrownExceptionRuleTest.php @@ -171,4 +171,14 @@ public function testThrowExpression(): void ]); } + public function testDeadCatch(): void + { + $this->analyse([__DIR__ . '/data/dead-catch.php'], [ + [ + 'Dead catch - TypeError is already caught above.', + 27, + ], + ]); + } + } diff --git a/tests/PHPStan/Rules/Exceptions/data/dead-catch.php b/tests/PHPStan/Rules/Exceptions/data/dead-catch.php index a2765c41d9..788cb88584 100644 --- a/tests/PHPStan/Rules/Exceptions/data/dead-catch.php +++ b/tests/PHPStan/Rules/Exceptions/data/dead-catch.php @@ -8,7 +8,7 @@ class Foo public function doFoo() { try { - + doFoo(); } catch (\Exception $e) { } catch (\TypeError $e) { @@ -21,7 +21,7 @@ public function doFoo() public function doBar() { try { - + doFoo(); } catch (\Throwable $e) { } catch (\TypeError $e) {