Skip to content

Commit 1181717

Browse files
committed
Improve Exception::getCode() type
See discussion: #795 (comment)
1 parent cae07c8 commit 1181717

File tree

2 files changed

+65
-4
lines changed

2 files changed

+65
-4
lines changed

src/Type/Php/ThrowableReturnTypeExtension.php

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,23 @@
22

33
namespace PHPStan\Type\Php;
44

5+
use PDOException;
56
use PhpParser\Node\Expr\MethodCall;
67
use PHPStan\Analyser\Scope;
78
use PHPStan\Reflection\MethodReflection;
89
use PHPStan\Type\BenevolentUnionType;
910
use PHPStan\Type\DynamicMethodReturnTypeExtension;
11+
use PHPStan\Type\ErrorType;
1012
use PHPStan\Type\IntegerType;
13+
use PHPStan\Type\ObjectType;
1114
use PHPStan\Type\StringType;
1215
use PHPStan\Type\Type;
16+
use PHPStan\Type\TypeCombinator;
17+
use PHPStan\Type\TypeUtils;
1318
use Throwable;
19+
use function count;
20+
use function in_array;
21+
use function strtolower;
1422

1523
final class ThrowableReturnTypeExtension implements DynamicMethodReturnTypeExtension
1624
{
@@ -27,7 +35,32 @@ public function isMethodSupported(MethodReflection $methodReflection): bool
2735

2836
public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type
2937
{
30-
return new BenevolentUnionType([new IntegerType(), new StringType()]);
38+
$type = $scope->getType($methodCall->var);
39+
$types = [];
40+
$pdoException = new ObjectType(PDOException::class);
41+
foreach (TypeUtils::getDirectClassNames($type) as $class) {
42+
$classType = new ObjectType($class);
43+
if ($pdoException->isSuperTypeOf($classType)->yes()) {
44+
$types[] = new StringType();
45+
continue;
46+
}
47+
48+
if (in_array(strtolower($class), [
49+
'throwable',
50+
'exception',
51+
], true)) {
52+
$types[] = new BenevolentUnionType([new IntegerType(), new StringType()]);
53+
continue;
54+
}
55+
56+
$types[] = new IntegerType();
57+
}
58+
59+
if (count($types) === 0) {
60+
return new ErrorType();
61+
}
62+
63+
return TypeCombinator::union(...$types);
3164
}
3265

3366
}

tests/PHPStan/Analyser/data/bug-6001.php

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,36 @@
44

55
use function PHPStan\Testing\assertType;
66

7-
assertType('(int|string)', (new \Exception())->getCode());
7+
class Foo
8+
{
89

9-
assertType('(int|string)', (new \RuntimeException())->getCode());
10+
public function doFoo(\Throwable $t): void
11+
{
12+
assertType('(int|string)', (new \Exception())->getCode());
13+
assertType('(int|string)', $t->getCode());
14+
assertType('int', (new \RuntimeException())->getCode());
15+
assertType('string', (new \PDOException())->getCode());
16+
assertType('int', (new MyException())->getCode());
17+
assertType('string', (new SubPDOException())->getCode());
18+
}
1019

11-
assertType('(int|string)', (new \PDOException())->getCode());
20+
/**
21+
* @param \PDOException|MyException $exception
22+
* @return void
23+
*/
24+
public function doBar($exception): void
25+
{
26+
assertType('int|string', $exception->getCode());
27+
}
28+
29+
}
30+
31+
class MyException extends \Exception
32+
{
33+
34+
}
35+
36+
class SubPDOException extends \PDOException
37+
{
38+
39+
}

0 commit comments

Comments
 (0)