Skip to content

Commit 8463afd

Browse files
committed
Fix CatchWithUnthrownExceptionRule for new $expr()
1 parent 566b44b commit 8463afd

File tree

3 files changed

+150
-0
lines changed

3 files changed

+150
-0
lines changed

src/Analyser/NodeScopeResolver.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2384,10 +2384,22 @@ static function () use ($expr, $rightResult): MutatingScope {
23842384
$hasYield = false;
23852385
$throwPoints = [];
23862386
if ($expr->class instanceof Expr) {
2387+
$objectClasses = TypeUtils::getDirectClassNames($scope->getType($expr));
2388+
if (count($objectClasses) === 1) {
2389+
$objectExprResult = $this->processExprNode(new New_(new Name($objectClasses[0])), $scope, static function (): void {
2390+
}, $context->enterDeep());
2391+
$additionalThrowPoints = $objectExprResult->getThrowPoints();
2392+
} else {
2393+
$additionalThrowPoints = [ThrowPoint::createImplicit($scope)];
2394+
}
2395+
23872396
$result = $this->processExprNode($expr->class, $scope, $nodeCallback, $context->enterDeep());
23882397
$scope = $result->getScope();
23892398
$hasYield = $result->hasYield();
23902399
$throwPoints = $result->getThrowPoints();
2400+
foreach ($additionalThrowPoints as $throwPoint) {
2401+
$throwPoints[] = $throwPoint;
2402+
}
23912403
} elseif ($expr->class instanceof Class_) {
23922404
$this->reflectionProvider->getAnonymousClassReflection($expr->class, $scope); // populates $expr->class->name
23932405
$this->processStmtNode($expr->class, $scope, $nodeCallback);

tests/PHPStan/Rules/Exceptions/CatchWithUnthrownExceptionRuleTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,4 +54,18 @@ public function testRule(): void
5454
]);
5555
}
5656

57+
public function testBug4806(): void
58+
{
59+
$this->analyse([__DIR__ . '/data/bug-4806.php'], [
60+
[
61+
'Dead catch - ArgumentCountError is never thrown in the try block.',
62+
65,
63+
],
64+
[
65+
'Dead catch - Throwable is never thrown in the try block.',
66+
119,
67+
],
68+
]);
69+
}
70+
5771
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
<?php
2+
3+
namespace Bug4806;
4+
5+
class NeverThrows
6+
{
7+
/**
8+
* @throws void
9+
*/
10+
final public function __construct()
11+
{
12+
}
13+
}
14+
15+
class HasNoConstructor
16+
{
17+
18+
}
19+
20+
class MayThrowArgumentCountError
21+
{
22+
/**
23+
* @throws \ArgumentCountError
24+
*/
25+
public function __construct()
26+
{
27+
throw new \ArgumentCountError();
28+
}
29+
}
30+
31+
class ImplicitThrow
32+
{
33+
34+
public function __construct()
35+
{
36+
37+
}
38+
39+
}
40+
41+
class Foo
42+
{
43+
44+
/**
45+
* @param class-string $class
46+
*/
47+
function createNotSpecified(string $class): object
48+
{
49+
try {
50+
$object = new $class();
51+
} catch (\ArgumentCountError $error) {
52+
53+
}
54+
55+
return $object;
56+
}
57+
58+
/**
59+
* @param class-string<NeverThrows> $class
60+
*/
61+
function createNeverThrows(string $class): object
62+
{
63+
try {
64+
$object = new $class();
65+
} catch (\ArgumentCountError $throwable) {
66+
67+
}
68+
69+
return $object;
70+
}
71+
72+
/**
73+
* @param class-string<MayThrowArgumentCountError> $class
74+
*/
75+
function createMayThrowArgumentCountError(string $class): object
76+
{
77+
try {
78+
$object = new $class();
79+
} catch (\ArgumentCountError $error) {
80+
81+
}
82+
83+
return $object;
84+
}
85+
86+
/**
87+
* @param class-string<MayThrowArgumentCountError> $class
88+
*/
89+
function createMayThrowArgumentCountErrorB(string $class): object
90+
{
91+
try {
92+
$object = new $class();
93+
} catch (\Throwable $throwable) {
94+
95+
}
96+
97+
return $object;
98+
}
99+
100+
/**
101+
* @param class-string<ImplicitThrow> $class
102+
*/
103+
function implicitThrow(string $class): void
104+
{
105+
try {
106+
$object = new $class();
107+
} catch (\Throwable $throwable) {
108+
109+
}
110+
}
111+
112+
/**
113+
* @param class-string<HasNoConstructor> $class
114+
*/
115+
function hasNoConstructor(string $class): void
116+
{
117+
try {
118+
$object = new $class();
119+
} catch (\Throwable $throwable) {
120+
121+
}
122+
}
123+
124+
}

0 commit comments

Comments
 (0)