Skip to content

Commit

Permalink
Bleeding edge - report catch with exception that is not thrown in the…
Browse files Browse the repository at this point in the history
… try block
  • Loading branch information
ondrejmirtes committed Mar 28, 2021
1 parent 79642dc commit ce9299c
Show file tree
Hide file tree
Showing 7 changed files with 196 additions and 0 deletions.
5 changes: 5 additions & 0 deletions conf/config.level4.neon
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ rules:
- PHPStan\Rules\TooWideTypehints\TooWideFunctionReturnTypehintRule

conditionalTags:
PHPStan\Rules\Exceptions\CatchWithUnthrownExceptionRule:
phpstan.rules.rule: %featureToggles.preciseExceptionTracking%
PHPStan\Rules\DeadCode\UnusedPrivateConstantRule:
phpstan.rules.rule: %featureToggles.unusedClassElements%
PHPStan\Rules\DeadCode\UnusedPrivateMethodRule:
Expand Down Expand Up @@ -147,6 +149,9 @@ services:
tags:
- phpstan.rules.rule

-
class: PHPStan\Rules\Exceptions\CatchWithUnthrownExceptionRule

-
class: PHPStan\Rules\TooWideTypehints\TooWideMethodReturnTypehintRule
arguments:
Expand Down
10 changes: 10 additions & 0 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,16 @@ parameters:
count: 1
path: src/PhpDoc/Tag/VarTag.php

-
message: "#^Dead catch \\- PHPStan\\\\BetterReflection\\\\Identifier\\\\Exception\\\\InvalidIdentifierName is never thrown in the try block\\.$#"
count: 2
path: src/Reflection/BetterReflection/BetterReflectionProvider.php

-
message: "#^Dead catch \\- PHPStan\\\\BetterReflection\\\\NodeCompiler\\\\Exception\\\\UnableToCompileNode\\|PHPStan\\\\BetterReflection\\\\Reflection\\\\Exception\\\\NotAClassReflection\\|PHPStan\\\\BetterReflection\\\\Reflection\\\\Exception\\\\NotAnInterfaceReflection is never thrown in the try block\\.$#"
count: 1
path: src/Reflection/BetterReflection/BetterReflectionProvider.php

-
message: "#^Only booleans are allowed in a negated boolean, int\\|false given\\.$#"
count: 1
Expand Down
2 changes: 2 additions & 0 deletions src/Analyser/NodeScopeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
use PHPStan\File\FileReader;
use PHPStan\Node\BooleanAndNode;
use PHPStan\Node\BooleanOrNode;
use PHPStan\Node\CatchWithUnthrownExceptionNode;
use PHPStan\Node\ClassConstantsNode;
use PHPStan\Node\ClassMethodsNode;
use PHPStan\Node\ClassPropertiesNode;
Expand Down Expand Up @@ -1183,6 +1184,7 @@ private function processStmtNode(
$throwPoints = $newThrowPoints;

if (count($matchingThrowPoints) === 0) {
$nodeCallback(new CatchWithUnthrownExceptionNode($catchNode, $catchType), $scope);
continue;
}

Expand Down
46 changes: 46 additions & 0 deletions src/Node/CatchWithUnthrownExceptionNode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php declare(strict_types = 1);

namespace PHPStan\Node;

use PhpParser\Node\Stmt\Catch_;
use PhpParser\NodeAbstract;
use PHPStan\Type\Type;

class CatchWithUnthrownExceptionNode extends NodeAbstract implements VirtualNode
{

private Catch_ $originalNode;

private Type $caughtType;

public function __construct(Catch_ $originalNode, Type $caughtType)
{
parent::__construct($originalNode->getAttributes());
$this->originalNode = $originalNode;
$this->caughtType = $caughtType;
}

public function getOriginalNode(): Catch_
{
return $this->originalNode;
}

public function getCaughtType(): Type
{
return $this->caughtType;
}

public function getType(): string
{
return 'PHPStan_Node_CatchWithUnthrownExceptionNode';
}

/**
* @return string[]
*/
public function getSubNodeNames(): array
{
return [];
}

}
32 changes: 32 additions & 0 deletions src/Rules/Exceptions/CatchWithUnthrownExceptionRule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php declare(strict_types = 1);

namespace PHPStan\Rules\Exceptions;

use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Node\CatchWithUnthrownExceptionNode;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use PHPStan\Type\VerbosityLevel;

/**
* @implements Rule<CatchWithUnthrownExceptionNode>
*/
class CatchWithUnthrownExceptionRule implements Rule
{

public function getNodeType(): string
{
return CatchWithUnthrownExceptionNode::class;
}

public function processNode(Node $node, Scope $scope): array
{
return [
RuleErrorBuilder::message(
sprintf('Dead catch - %s is never thrown in the try block.', $node->getCaughtType()->describe(VerbosityLevel::typeOnly()))
)->line($node->getLine())->build(),
];
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php declare(strict_types = 1);

namespace PHPStan\Rules\Exceptions;

use PHPStan\Rules\Rule;
use PHPStan\Testing\RuleTestCase;

class CatchWithUnthrownExceptionRuleTest extends RuleTestCase
{

protected function getRule(): Rule
{
return new CatchWithUnthrownExceptionRule();
}

public function testRule(): void
{
$this->analyse([__DIR__ . '/data/unthrown-exception.php'], [
[
'Dead catch - Throwable is never thrown in the try block.',
12,
],
[
'Dead catch - Exception is never thrown in the try block.',
21,
],
[
'Dead catch - Throwable is never thrown in the try block.',
38,
],
[
'Dead catch - RuntimeException is never thrown in the try block.',
47,
],
]);
}

}
63 changes: 63 additions & 0 deletions tests/PHPStan/Rules/Exceptions/data/unthrown-exception.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

namespace UnthrownException;

class Foo
{

public function doFoo(): void
{
try {
$foo = 1;
} catch (\Throwable $e) {
// pass
}
}

public function doBar(): void
{
try {
$foo = 1;
} catch (\Exception $e) {
// pass
}
}

/** @throws \InvalidArgumentException */
public function throwIae(): void
{

}

public function doBaz(): void
{
try {
$this->throwIae();
} catch (\InvalidArgumentException $e) {

} catch (\Throwable $e) {
// dead
}
}

public function doLorem(): void
{
try {
$this->throwIae();
} catch (\RuntimeException $e) {
// dead
} catch (\Throwable $e) {

}
}

public function doIpsum(): void
{
try {
$this->throwIae();
} catch (\Throwable $e) {

}
}

}

0 comments on commit ce9299c

Please sign in to comment.