From 2fabdf335303858a3d91c2927959eeefb38363db Mon Sep 17 00:00:00 2001 From: Matthew Brown Date: Sun, 22 Apr 2018 00:40:30 -0400 Subject: [PATCH] Fix #314 - add a way to indicate @method list is comprehensive --- src/Psalm/Checker/CommentChecker.php | 4 ++++ .../Expression/Call/MethodCallChecker.php | 12 +++++++++--- src/Psalm/ClassLikeDocblockComment.php | 5 +++++ src/Psalm/Storage/ClassLikeStorage.php | 5 +++++ src/Psalm/Visitor/DependencyFinderVisitor.php | 1 + tests/AnnotationTest.php | 17 +++++++++++++++++ 6 files changed, 41 insertions(+), 3 deletions(-) diff --git a/src/Psalm/Checker/CommentChecker.php b/src/Psalm/Checker/CommentChecker.php index 2d09da227..4d5962e2f 100644 --- a/src/Psalm/Checker/CommentChecker.php +++ b/src/Psalm/Checker/CommentChecker.php @@ -305,6 +305,10 @@ class CommentChecker $info->sealed_properties = true; } + if (isset($comments['specials']['psalm-seal-methods'])) { + $info->sealed_methods = true; + } + if (isset($comments['specials']['psalm-suppress'])) { /** @var string $suppress_entry */ foreach ($comments['specials']['psalm-suppress'] as $suppress_entry) { diff --git a/src/Psalm/Checker/Statements/Expression/Call/MethodCallChecker.php b/src/Psalm/Checker/Statements/Expression/Call/MethodCallChecker.php index 51c77fbf9..cef667d58 100644 --- a/src/Psalm/Checker/Statements/Expression/Call/MethodCallChecker.php +++ b/src/Psalm/Checker/Statements/Expression/Call/MethodCallChecker.php @@ -270,13 +270,13 @@ class MethodCallChecker extends \Psalm\Checker\Statements\Expression\CallChecker $statements_checker->getSource() ) ) { - $has_valid_method_call_type = true; - $existent_method_ids[] = $method_id; - if ($var_id !== '$this') { $class_storage = $project_checker->classlike_storage_provider->get($fq_class_name); if (isset($class_storage->pseudo_methods[$method_name_lc])) { + $has_valid_method_call_type = true; + $existent_method_ids[] = $method_id; + $pseudo_method_storage = $class_storage->pseudo_methods[$method_name_lc]; if (self::checkFunctionArguments( @@ -316,9 +316,15 @@ class MethodCallChecker extends \Psalm\Checker\Statements\Expression\CallChecker continue; } + } elseif ($class_storage->sealed_methods) { + $non_existent_method_ids[] = $method_id; + continue; } } + $has_valid_method_call_type = true; + $existent_method_ids[] = $method_id; + $return_type = Type::getMixed(); continue; } diff --git a/src/Psalm/ClassLikeDocblockComment.php b/src/Psalm/ClassLikeDocblockComment.php index 1e8ddd029..4b4f771fe 100644 --- a/src/Psalm/ClassLikeDocblockComment.php +++ b/src/Psalm/ClassLikeDocblockComment.php @@ -30,6 +30,11 @@ class ClassLikeDocblockComment */ public $sealed_properties = false; + /** + * @var bool + */ + public $sealed_methods = false; + /** * @var array */ diff --git a/src/Psalm/Storage/ClassLikeStorage.php b/src/Psalm/Storage/ClassLikeStorage.php index a580ca590..4ec73440c 100644 --- a/src/Psalm/Storage/ClassLikeStorage.php +++ b/src/Psalm/Storage/ClassLikeStorage.php @@ -52,6 +52,11 @@ class ClassLikeStorage */ public $sealed_properties = false; + /** + * @var bool + */ + public $sealed_methods = false; + /** * @var array */ diff --git a/src/Psalm/Visitor/DependencyFinderVisitor.php b/src/Psalm/Visitor/DependencyFinderVisitor.php index 6f6105ab3..a3ae8f47e 100644 --- a/src/Psalm/Visitor/DependencyFinderVisitor.php +++ b/src/Psalm/Visitor/DependencyFinderVisitor.php @@ -264,6 +264,7 @@ class DependencyFinderVisitor extends PhpParser\NodeVisitorAbstract implements P $storage->deprecated = $docblock_info->deprecated; $storage->sealed_properties = $docblock_info->sealed_properties; + $storage->sealed_methods = $docblock_info->sealed_methods; $storage->suppressed_issues = $docblock_info->suppressed_issues; diff --git a/tests/AnnotationTest.php b/tests/AnnotationTest.php index 6070aebd5..7cbd2e50c 100644 --- a/tests/AnnotationTest.php +++ b/tests/AnnotationTest.php @@ -1680,6 +1680,23 @@ class AnnotationTest extends TestCase class Child extends Parent {}', 'error_message' => 'InvalidDocblock', ], + 'magicMethodAnnotationWithSealed' => [ + 'getString(); + $child->foo();', + 'error_message' => 'UndefinedMethod - src/somefile.php:14 - Method Child::foo does not exist', + ], 'magicMethodAnnotationInvalidArg' => [ '