Skip to content

Commit 165504c

Browse files
committed
Bleeding edge - teach CatchWithUnthrownExceptionRule everything what DeadCatchRule does
1 parent 8cc6e7e commit 165504c

File tree

7 files changed

+49
-5
lines changed

7 files changed

+49
-5
lines changed

conf/config.level4.neon

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ rules:
66
- PHPStan\Rules\Comparison\NumberComparisonOperatorsConstantConditionRule
77
- PHPStan\Rules\DeadCode\NoopRule
88
- PHPStan\Rules\DeadCode\UnreachableStatementRule
9-
- PHPStan\Rules\Exceptions\DeadCatchRule
109
- PHPStan\Rules\Functions\CallToFunctionStamentWithoutSideEffectsRule
1110
- PHPStan\Rules\Methods\CallToConstructorStatementWithoutSideEffectsRule
1211
- PHPStan\Rules\Methods\CallToMethodStamentWithoutSideEffectsRule
@@ -153,6 +152,13 @@ services:
153152
-
154153
class: PHPStan\Rules\Exceptions\CatchWithUnthrownExceptionRule
155154

155+
-
156+
class: PHPStan\Rules\Exceptions\DeadCatchRule
157+
arguments:
158+
bleedingEdge: %featureToggles.bleedingEdge%
159+
tags:
160+
- phpstan.rules.rule
161+
156162
-
157163
class: PHPStan\Rules\Exceptions\OverwrittenExitPointByFinallyRule
158164

src/Analyser/NodeScopeResolver.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1233,7 +1233,7 @@ private function processStmtNode(
12331233
}
12341234

12351235
if (count($throwableThrowPoints) === 0) {
1236-
$nodeCallback(new CatchWithUnthrownExceptionNode($catchNode, $catchType), $scope);
1236+
$nodeCallback(new CatchWithUnthrownExceptionNode($catchNode, $catchType, $originalCatchType), $scope);
12371237
continue;
12381238
}
12391239

src/Node/CatchWithUnthrownExceptionNode.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,14 @@ class CatchWithUnthrownExceptionNode extends NodeAbstract implements VirtualNode
1414

1515
private Type $caughtType;
1616

17-
public function __construct(Catch_ $originalNode, Type $caughtType)
17+
private Type $originalCaughtType;
18+
19+
public function __construct(Catch_ $originalNode, Type $caughtType, Type $originalCaughtType)
1820
{
1921
parent::__construct($originalNode->getAttributes());
2022
$this->originalNode = $originalNode;
2123
$this->caughtType = $caughtType;
24+
$this->originalCaughtType = $originalCaughtType;
2225
}
2326

2427
public function getOriginalNode(): Catch_
@@ -31,6 +34,11 @@ public function getCaughtType(): Type
3134
return $this->caughtType;
3235
}
3336

37+
public function getOriginalCaughtType(): Type
38+
{
39+
return $this->originalCaughtType;
40+
}
41+
3442
public function getType(): string
3543
{
3644
return 'PHPStan_Node_CatchWithUnthrownExceptionNode';

src/Rules/Exceptions/CatchWithUnthrownExceptionRule.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use PHPStan\Node\CatchWithUnthrownExceptionNode;
88
use PHPStan\Rules\Rule;
99
use PHPStan\Rules\RuleErrorBuilder;
10+
use PHPStan\Type\NeverType;
1011
use PHPStan\Type\VerbosityLevel;
1112

1213
/**
@@ -22,6 +23,14 @@ public function getNodeType(): string
2223

2324
public function processNode(Node $node, Scope $scope): array
2425
{
26+
if ($node->getCaughtType() instanceof NeverType) {
27+
return [
28+
RuleErrorBuilder::message(
29+
sprintf('Dead catch - %s is already caught above.', $node->getOriginalCaughtType()->describe(VerbosityLevel::typeOnly()))
30+
)->line($node->getLine())->build(),
31+
];
32+
}
33+
2534
return [
2635
RuleErrorBuilder::message(
2736
sprintf('Dead catch - %s is never thrown in the try block.', $node->getCaughtType()->describe(VerbosityLevel::typeOnly()))

src/Rules/Exceptions/DeadCatchRule.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,24 @@
1717
class DeadCatchRule implements Rule
1818
{
1919

20+
private bool $bleedingEdge;
21+
22+
public function __construct(bool $bleedingEdge = false)
23+
{
24+
$this->bleedingEdge = $bleedingEdge;
25+
}
26+
2027
public function getNodeType(): string
2128
{
2229
return Node\Stmt\TryCatch::class;
2330
}
2431

2532
public function processNode(Node $node, Scope $scope): array
2633
{
34+
if ($this->bleedingEdge) {
35+
return [];
36+
}
37+
2738
$catchTypes = array_map(static function (Node\Stmt\Catch_ $catch): Type {
2839
return TypeCombinator::union(...array_map(static function (Node\Name $className): ObjectType {
2940
return new ObjectType($className->toString());

tests/PHPStan/Rules/Exceptions/CatchWithUnthrownExceptionRuleTest.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,4 +171,14 @@ public function testThrowExpression(): void
171171
]);
172172
}
173173

174+
public function testDeadCatch(): void
175+
{
176+
$this->analyse([__DIR__ . '/data/dead-catch.php'], [
177+
[
178+
'Dead catch - TypeError is already caught above.',
179+
27,
180+
],
181+
]);
182+
}
183+
174184
}

tests/PHPStan/Rules/Exceptions/data/dead-catch.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ class Foo
88
public function doFoo()
99
{
1010
try {
11-
11+
doFoo();
1212
} catch (\Exception $e) {
1313

1414
} catch (\TypeError $e) {
@@ -21,7 +21,7 @@ public function doFoo()
2121
public function doBar()
2222
{
2323
try {
24-
24+
doFoo();
2525
} catch (\Throwable $e) {
2626

2727
} catch (\TypeError $e) {

0 commit comments

Comments
 (0)