From 1d6ae16504de98af0705a4a34a107fbfd108a896 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Thu, 13 Jan 2022 21:22:47 +0100 Subject: [PATCH 1/8] Add Exception code return type provider --- .../ExceptionCodeReturnTypeProvider.php | 34 +++++++++++ .../ReturnTypeProvider/ExceptionCodeTest.php | 61 +++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 src/Psalm/Internal/Provider/ReturnTypeProvider/ExceptionCodeReturnTypeProvider.php create mode 100644 tests/ReturnTypeProvider/ExceptionCodeTest.php diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ExceptionCodeReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ExceptionCodeReturnTypeProvider.php new file mode 100644 index 00000000000..bf55e08e357 --- /dev/null +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ExceptionCodeReturnTypeProvider.php @@ -0,0 +1,34 @@ +getMethodNameLowercase(); + $fqcn = $event->getCalledFqClasslikeName(); + + if ($method_name_lowercase !== 'getcode') { + return null; + } + + if ($fqcn === 'Exception' || $fqcn === 'Throwable') { + return null; + } + + if (is_a($fqcn, \PDOException::class, true)) { + return Type::parseString('string'); + } + + return Type::parseString('int'); + } +} diff --git a/tests/ReturnTypeProvider/ExceptionCodeTest.php b/tests/ReturnTypeProvider/ExceptionCodeTest.php new file mode 100644 index 00000000000..ca607b7e685 --- /dev/null +++ b/tests/ReturnTypeProvider/ExceptionCodeTest.php @@ -0,0 +1,61 @@ + [ + 'getCode(); + return $code; + } + ', + ['$code' => 'int'], + ]; + yield 'LogicException' => [ + 'getCode(); + return $code; + } + ', + ['$code' => 'int'], + ]; + yield 'PDOException' => [ + 'getCode(); + return $code; + } + ', + ['$code' => 'string'], + ]; + yield 'Exception' => [ + 'getCode(); + return $code; + } + ', + ['$code' => 'int|string'], + ]; + yield 'Throwable' => [ + 'getCode(); + return $code; + } + ', + ['$code' => 'int|string'], + ]; + } +} From 31c5845a3039f382ed29496947d545fc551a2395 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Thu, 13 Jan 2022 21:46:01 +0100 Subject: [PATCH 2/8] Register provider --- src/Psalm/Internal/Provider/FunctionReturnTypeProvider.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Psalm/Internal/Provider/FunctionReturnTypeProvider.php b/src/Psalm/Internal/Provider/FunctionReturnTypeProvider.php index 8b124ea5532..35d9d406524 100644 --- a/src/Psalm/Internal/Provider/FunctionReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/FunctionReturnTypeProvider.php @@ -23,6 +23,7 @@ use Psalm\Internal\Provider\ReturnTypeProvider\ArraySpliceReturnTypeProvider; use Psalm\Internal\Provider\ReturnTypeProvider\ArrayUniqueReturnTypeProvider; use Psalm\Internal\Provider\ReturnTypeProvider\ArrayValuesReturnTypeProvider; +use Psalm\Internal\Provider\ReturnTypeProvider\ExceptionCodeReturnTypeProvider; use Psalm\Internal\Provider\ReturnTypeProvider\ExplodeReturnTypeProvider; use Psalm\Internal\Provider\ReturnTypeProvider\FilterVarReturnTypeProvider; use Psalm\Internal\Provider\ReturnTypeProvider\FirstArgStringReturnTypeProvider; @@ -109,6 +110,7 @@ public function __construct() $this->registerClass(TriggerErrorReturnTypeProvider::class); $this->registerClass(RandReturnTypeProvider::class); $this->registerClass(InArrayReturnTypeProvider::class); + $this->registerClass(ExceptionCodeReturnTypeProvider::class); } /** From b1285bd0584618d1f388d34172459da42d97d78a Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Thu, 13 Jan 2022 21:48:20 +0100 Subject: [PATCH 3/8] Fix cs --- .../ExceptionCodeReturnTypeProvider.php | 10 +++++++--- tests/ReturnTypeProvider/ExceptionCodeTest.php | 9 +++++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ExceptionCodeReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ExceptionCodeReturnTypeProvider.php index bf55e08e357..cbe0ed3f88f 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ExceptionCodeReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ExceptionCodeReturnTypeProvider.php @@ -3,11 +3,15 @@ namespace Psalm\Internal\Provider\ReturnTypeProvider; use Psalm\Plugin\EventHandler\Event\MethodReturnTypeProviderEvent; +use Psalm\Plugin\EventHandler\MethodReturnTypeProviderInterface; use Psalm\Type; +use PDOException; -class ExceptionCodeReturnTypeProvider implements \Psalm\Plugin\EventHandler\MethodReturnTypeProviderInterface +use function is_a; + +class ExceptionCodeReturnTypeProvider implements MethodReturnTypeProviderInterface { - public static function getClassLikeNames() : array + public static function getClassLikeNames(): array { return ['Throwable']; } @@ -25,7 +29,7 @@ public static function getMethodReturnType(MethodReturnTypeProviderEvent $event) return null; } - if (is_a($fqcn, \PDOException::class, true)) { + if (is_a($fqcn, PDOException::class, true)) { return Type::parseString('string'); } diff --git a/tests/ReturnTypeProvider/ExceptionCodeTest.php b/tests/ReturnTypeProvider/ExceptionCodeTest.php index ca607b7e685..8739fec78a9 100644 --- a/tests/ReturnTypeProvider/ExceptionCodeTest.php +++ b/tests/ReturnTypeProvider/ExceptionCodeTest.php @@ -3,7 +3,6 @@ namespace Psalm\Tests\ReturnTypeProvider; use Psalm\Tests\TestCase; -use Psalm\Tests\Traits\InvalidCodeAnalysisTestTrait; use Psalm\Tests\Traits\ValidCodeAnalysisTestTrait; class ExceptionCodeTest extends TestCase @@ -14,7 +13,7 @@ public function providerValidCodeParse(): iterable { yield 'RuntimeException' => [ 'getCode(); return $code; } @@ -23,7 +22,7 @@ function f(\RuntimeException $e) { ]; yield 'LogicException' => [ 'getCode(); return $code; } @@ -32,7 +31,7 @@ function f(\LogicException $e) { ]; yield 'PDOException' => [ 'getCode(); return $code; } @@ -41,6 +40,7 @@ function f(\PDOException $e) { ]; yield 'Exception' => [ 'getCode(); return $code; @@ -50,6 +50,7 @@ function f(\Exception $e) { ]; yield 'Throwable' => [ 'getCode(); return $code; From f2138ace7bda0eb3ab04fec7aa3809b16ce11a50 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Thu, 13 Jan 2022 22:12:39 +0100 Subject: [PATCH 4/8] Avoid calling autoloader --- .../ReturnTypeProvider/ExceptionCodeReturnTypeProvider.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ExceptionCodeReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ExceptionCodeReturnTypeProvider.php index cbe0ed3f88f..391c644eae5 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ExceptionCodeReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ExceptionCodeReturnTypeProvider.php @@ -5,9 +5,6 @@ use Psalm\Plugin\EventHandler\Event\MethodReturnTypeProviderEvent; use Psalm\Plugin\EventHandler\MethodReturnTypeProviderInterface; use Psalm\Type; -use PDOException; - -use function is_a; class ExceptionCodeReturnTypeProvider implements MethodReturnTypeProviderInterface { @@ -29,7 +26,7 @@ public static function getMethodReturnType(MethodReturnTypeProviderEvent $event) return null; } - if (is_a($fqcn, PDOException::class, true)) { + if ($fqcn === 'PDOException') { return Type::parseString('string'); } From b376a8d4d718e0440052441613a83ee1112d982d Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Thu, 13 Jan 2022 22:23:41 +0100 Subject: [PATCH 5/8] Wip --- .../ReturnTypeProvider/ExceptionCodeTest.php | 29 +++++++------------ 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/tests/ReturnTypeProvider/ExceptionCodeTest.php b/tests/ReturnTypeProvider/ExceptionCodeTest.php index 8739fec78a9..ccc8ea10db5 100644 --- a/tests/ReturnTypeProvider/ExceptionCodeTest.php +++ b/tests/ReturnTypeProvider/ExceptionCodeTest.php @@ -14,47 +14,38 @@ public function providerValidCodeParse(): iterable yield 'RuntimeException' => [ 'getCode(); - return $code; + return $e->getCode(); } ', - ['$code' => 'int'], + [], ]; yield 'LogicException' => [ 'getCode(); - return $code; + return $e->getCode(); } ', - ['$code' => 'int'], + [], ]; yield 'PDOException' => [ 'getCode(); - return $code; + return $e->getCode(); } ', - ['$code' => 'string'], + [], ]; yield 'Exception' => [ 'getCode(); - return $code; - } + /** @var \Throwable $e */ + $code = $e->getCode(); ', ['$code' => 'int|string'], ]; yield 'Throwable' => [ 'getCode(); - return $code; - } + /** @var \Exception $e */ + $code = $e->getCode(); ', ['$code' => 'int|string'], ]; From cbfa6a10dadb67c112fa35ba8ee94d212b823507 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Thu, 13 Jan 2022 22:43:57 +0100 Subject: [PATCH 6/8] Fix --- src/Psalm/Internal/Provider/FunctionReturnTypeProvider.php | 1 - src/Psalm/Internal/Provider/MethodReturnTypeProvider.php | 2 ++ .../ReturnTypeProvider/ExceptionCodeReturnTypeProvider.php | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Psalm/Internal/Provider/FunctionReturnTypeProvider.php b/src/Psalm/Internal/Provider/FunctionReturnTypeProvider.php index 35d9d406524..70d8119aaf7 100644 --- a/src/Psalm/Internal/Provider/FunctionReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/FunctionReturnTypeProvider.php @@ -110,7 +110,6 @@ public function __construct() $this->registerClass(TriggerErrorReturnTypeProvider::class); $this->registerClass(RandReturnTypeProvider::class); $this->registerClass(InArrayReturnTypeProvider::class); - $this->registerClass(ExceptionCodeReturnTypeProvider::class); } /** diff --git a/src/Psalm/Internal/Provider/MethodReturnTypeProvider.php b/src/Psalm/Internal/Provider/MethodReturnTypeProvider.php index f29dc77d6cc..7e42a861340 100644 --- a/src/Psalm/Internal/Provider/MethodReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/MethodReturnTypeProvider.php @@ -8,6 +8,7 @@ use Psalm\Context; use Psalm\Internal\Provider\ReturnTypeProvider\ClosureFromCallableReturnTypeProvider; use Psalm\Internal\Provider\ReturnTypeProvider\DomNodeAppendChild; +use Psalm\Internal\Provider\ReturnTypeProvider\ExceptionCodeReturnTypeProvider; use Psalm\Internal\Provider\ReturnTypeProvider\ImagickPixelColorReturnTypeProvider; use Psalm\Internal\Provider\ReturnTypeProvider\PdoStatementReturnTypeProvider; use Psalm\Internal\Provider\ReturnTypeProvider\SimpleXmlElementAsXml; @@ -58,6 +59,7 @@ public function __construct() $this->registerClass(SimpleXmlElementAsXml::class); $this->registerClass(PdoStatementReturnTypeProvider::class); $this->registerClass(ClosureFromCallableReturnTypeProvider::class); + $this->registerClass(ExceptionCodeReturnTypeProvider::class); } /** diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ExceptionCodeReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ExceptionCodeReturnTypeProvider.php index 391c644eae5..e2c5f447522 100644 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ExceptionCodeReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/ReturnTypeProvider/ExceptionCodeReturnTypeProvider.php @@ -16,7 +16,7 @@ public static function getClassLikeNames(): array public static function getMethodReturnType(MethodReturnTypeProviderEvent $event): ?Type\Union { $method_name_lowercase = $event->getMethodNameLowercase(); - $fqcn = $event->getCalledFqClasslikeName(); + $fqcn = $event->getFqClasslikeName(); if ($method_name_lowercase !== 'getcode') { return null; From 9f84da4d6f74ebfd14c2d3fe776a52e57212cc01 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Thu, 13 Jan 2022 23:36:46 +0100 Subject: [PATCH 7/8] Try another strategy --- .../Method/MethodCallReturnTypeFetcher.php | 13 +++++++ .../Provider/FunctionReturnTypeProvider.php | 1 - .../Provider/MethodReturnTypeProvider.php | 2 -- .../ExceptionCodeReturnTypeProvider.php | 35 ------------------- 4 files changed, 13 insertions(+), 38 deletions(-) delete mode 100644 src/Psalm/Internal/Provider/ReturnTypeProvider/ExceptionCodeReturnTypeProvider.php diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.php index cc99d748ec5..4e48a48bd4b 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.php @@ -2,6 +2,8 @@ namespace Psalm\Internal\Analyzer\Statements\Expression\Call\Method; +use Exception; +use PDOException; use PhpParser; use Psalm\CodeLocation; use Psalm\Codebase; @@ -27,6 +29,7 @@ use Psalm\Type\Atomic\TNamedObject; use Psalm\Type\Atomic\TTemplateParam; use Psalm\Type\Union; +use Throwable; use UnexpectedValueException; use function array_filter; @@ -92,6 +95,16 @@ public static function fetch( } } + if ($premixin_method_id->method_name === 'getcode' + && $premixin_method_id->fq_class_name !== Exception::class + && in_array(Throwable::class, $class_storage->class_implements)) { + if ($premixin_method_id->fq_class_name === PDOException::class) { + return Type::getString(); + } else { + return Type::getInt(); + } + } + if ($declaring_method_id && $declaring_method_id !== $method_id) { $declaring_fq_class_name = $declaring_method_id->fq_class_name; $declaring_method_name = $declaring_method_id->method_name; diff --git a/src/Psalm/Internal/Provider/FunctionReturnTypeProvider.php b/src/Psalm/Internal/Provider/FunctionReturnTypeProvider.php index 70d8119aaf7..8b124ea5532 100644 --- a/src/Psalm/Internal/Provider/FunctionReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/FunctionReturnTypeProvider.php @@ -23,7 +23,6 @@ use Psalm\Internal\Provider\ReturnTypeProvider\ArraySpliceReturnTypeProvider; use Psalm\Internal\Provider\ReturnTypeProvider\ArrayUniqueReturnTypeProvider; use Psalm\Internal\Provider\ReturnTypeProvider\ArrayValuesReturnTypeProvider; -use Psalm\Internal\Provider\ReturnTypeProvider\ExceptionCodeReturnTypeProvider; use Psalm\Internal\Provider\ReturnTypeProvider\ExplodeReturnTypeProvider; use Psalm\Internal\Provider\ReturnTypeProvider\FilterVarReturnTypeProvider; use Psalm\Internal\Provider\ReturnTypeProvider\FirstArgStringReturnTypeProvider; diff --git a/src/Psalm/Internal/Provider/MethodReturnTypeProvider.php b/src/Psalm/Internal/Provider/MethodReturnTypeProvider.php index 7e42a861340..f29dc77d6cc 100644 --- a/src/Psalm/Internal/Provider/MethodReturnTypeProvider.php +++ b/src/Psalm/Internal/Provider/MethodReturnTypeProvider.php @@ -8,7 +8,6 @@ use Psalm\Context; use Psalm\Internal\Provider\ReturnTypeProvider\ClosureFromCallableReturnTypeProvider; use Psalm\Internal\Provider\ReturnTypeProvider\DomNodeAppendChild; -use Psalm\Internal\Provider\ReturnTypeProvider\ExceptionCodeReturnTypeProvider; use Psalm\Internal\Provider\ReturnTypeProvider\ImagickPixelColorReturnTypeProvider; use Psalm\Internal\Provider\ReturnTypeProvider\PdoStatementReturnTypeProvider; use Psalm\Internal\Provider\ReturnTypeProvider\SimpleXmlElementAsXml; @@ -59,7 +58,6 @@ public function __construct() $this->registerClass(SimpleXmlElementAsXml::class); $this->registerClass(PdoStatementReturnTypeProvider::class); $this->registerClass(ClosureFromCallableReturnTypeProvider::class); - $this->registerClass(ExceptionCodeReturnTypeProvider::class); } /** diff --git a/src/Psalm/Internal/Provider/ReturnTypeProvider/ExceptionCodeReturnTypeProvider.php b/src/Psalm/Internal/Provider/ReturnTypeProvider/ExceptionCodeReturnTypeProvider.php deleted file mode 100644 index e2c5f447522..00000000000 --- a/src/Psalm/Internal/Provider/ReturnTypeProvider/ExceptionCodeReturnTypeProvider.php +++ /dev/null @@ -1,35 +0,0 @@ -getMethodNameLowercase(); - $fqcn = $event->getFqClasslikeName(); - - if ($method_name_lowercase !== 'getcode') { - return null; - } - - if ($fqcn === 'Exception' || $fqcn === 'Throwable') { - return null; - } - - if ($fqcn === 'PDOException') { - return Type::parseString('string'); - } - - return Type::parseString('int'); - } -} From 9905baeceb909175c7e9654023d60c49e8fde8c0 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Mon, 24 Jan 2022 09:27:57 +0100 Subject: [PATCH 8/8] Add flagfrom_calculation --- .../Expression/Call/Method/MethodCallReturnTypeFetcher.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.php index 4e48a48bd4b..c4779156a9e 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.php @@ -101,7 +101,7 @@ public static function fetch( if ($premixin_method_id->fq_class_name === PDOException::class) { return Type::getString(); } else { - return Type::getInt(); + return Type::getInt(true); // TODO: Remove the flag in Psalm 5 } }