From 3f289731bb0719722813b85f2ecad0302f3fd200 Mon Sep 17 00:00:00 2001 From: Brown Date: Thu, 31 Jan 2019 18:40:40 -0500 Subject: [PATCH] Add ability to check descendants in ignored exceptions --- config.xsd | 3 ++- psalm.xml.dist | 1 + src/Psalm/Config.php | 21 +++++++++++++++---- .../Analyzer/FunctionLikeAnalyzer.php | 16 +++++++++++++- src/Psalm/Internal/Codebase/Populator.php | 20 ++++++++++++++++-- .../Internal/Stubs/CoreGenericFunctions.php | 8 +++++++ 6 files changed, 61 insertions(+), 8 deletions(-) diff --git a/config.xsd b/config.xsd index 4e06bf9d8..5d6036f02 100644 --- a/config.xsd +++ b/config.xsd @@ -95,7 +95,8 @@ - + + diff --git a/psalm.xml.dist b/psalm.xml.dist index b0888f90b..40301ff9b 100644 --- a/psalm.xml.dist +++ b/psalm.xml.dist @@ -21,6 +21,7 @@ + diff --git a/src/Psalm/Config.php b/src/Psalm/Config.php index 173f03789..224071000 100644 --- a/src/Psalm/Config.php +++ b/src/Psalm/Config.php @@ -231,6 +231,11 @@ class Config */ public $ignored_exceptions = []; + /** + * @var array + */ + public $ignored_exceptions_and_descendants = []; + /** * @var array */ @@ -636,10 +641,18 @@ class Config } } - if (isset($config_xml->ignoreExceptions) && isset($config_xml->ignoreExceptions->class)) { - /** @var \SimpleXMLElement $exception_class */ - foreach ($config_xml->ignoreExceptions->class as $exception_class) { - $config->ignored_exceptions[(string) $exception_class['name']] = true; + if (isset($config_xml->ignoreExceptions)) { + if (isset($config_xml->ignoreExceptions->class)) { + /** @var \SimpleXMLElement $exception_class */ + foreach ($config_xml->ignoreExceptions->class as $exception_class) { + $config->ignored_exceptions[(string) $exception_class['name']] = true; + } + } + if (isset($config_xml->ignoreExceptions->classAndDescendants)) { + /** @var \SimpleXMLElement $exception_class */ + foreach ($config_xml->ignoreExceptions->classAndDescendants as $exception_class) { + $config->ignored_exceptions_and_descendants[(string) $exception_class['name']] = true; + } } } diff --git a/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php b/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php index 6ae89624a..829a87850 100644 --- a/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php @@ -720,7 +720,12 @@ abstract class FunctionLikeAnalyzer extends SourceAnalyzer implements Statements if ($context->collect_exceptions) { if ($context->possibly_thrown_exceptions) { - $ignored_exceptions = array_change_key_case($codebase->config->ignored_exceptions); + $ignored_exceptions = array_change_key_case( + $codebase->config->ignored_exceptions + ); + $ignored_exceptions_and_descendants = array_change_key_case( + $codebase->config->ignored_exceptions_and_descendants + ); $undocumented_throws = []; @@ -736,6 +741,15 @@ abstract class FunctionLikeAnalyzer extends SourceAnalyzer implements Statements } } + foreach ($ignored_exceptions_and_descendants as $expected_exception => $_) { + if ($expected_exception === $possibly_thrown_exception + || $codebase->classExtends($possibly_thrown_exception, $expected_exception) + ) { + $is_expected = true; + break; + } + } + if (!$is_expected) { $undocumented_throws[$possibly_thrown_exception] = true; } diff --git a/src/Psalm/Internal/Codebase/Populator.php b/src/Psalm/Internal/Codebase/Populator.php index 1ea74b1e2..f5b34219a 100644 --- a/src/Psalm/Internal/Codebase/Populator.php +++ b/src/Psalm/Internal/Codebase/Populator.php @@ -688,6 +688,13 @@ class Populator if (isset($interface_storage->methods[$method_name])) { $interface_method_storage = $interface_storage->methods[$method_name]; + if (!$method_storage->throws + && $method_storage->inheritdoc + && $interface_method_storage->throws + ) { + $method_storage->throws = $interface_method_storage->throws; + } + if ($interface_method_storage->return_type && $interface_method_storage->signature_return_type && $interface_method_storage->return_type @@ -934,9 +941,18 @@ class Populator list($declaring_class, $declaring_method_name) = explode('::', $declaring_method_id); $declaring_class_storage = $this->classlike_storage_provider->get($declaring_class); + $declaring_method_storage = $declaring_class_storage->methods[strtolower($declaring_method_name)]; + // tell the declaring class it's overridden downstream - $declaring_class_storage->methods[strtolower($declaring_method_name)]->overridden_downstream = true; - $declaring_class_storage->methods[strtolower($declaring_method_name)]->overridden_somewhere = true; + $declaring_method_storage->overridden_downstream = true; + $declaring_method_storage->overridden_somewhere = true; + + if (!$method_storage->throws + && $method_storage->inheritdoc + && $declaring_method_storage->throws + ) { + $method_storage->throws = $declaring_method_storage->throws; + } if (count($storage->overridden_method_ids[$method_name]) === 1 && $method_storage->signature_return_type diff --git a/src/Psalm/Internal/Stubs/CoreGenericFunctions.php b/src/Psalm/Internal/Stubs/CoreGenericFunctions.php index 2e33b9c4e..85814c8da 100644 --- a/src/Psalm/Internal/Stubs/CoreGenericFunctions.php +++ b/src/Psalm/Internal/Stubs/CoreGenericFunctions.php @@ -138,3 +138,11 @@ function array_search($needle, array $haystack, bool $strict = false) {} */ function usort(array &$arr, callable $callback): bool {} +/** + * @psalm-template T + * + * @param array $arr + * @return array + */ +function array_change_key_case(array $arr) {} +