From 0d62fbdf98063f83a13f977bbbe1cf928b9f2166 Mon Sep 17 00:00:00 2001 From: Matthew Brown Date: Wed, 11 Mar 2020 10:18:40 -0400 Subject: [PATCH] Detect erroneous abstract static method calls --- config.xsd | 1 + docs/running_psalm/issues.md | 12 ++++++++++++ .../Expression/Call/StaticCallAnalyzer.php | 15 +++++++++++++++ src/Psalm/Issue/AbstractMethodCall.php | 7 +++++++ tests/MethodCallTest.php | 13 +++++++++++++ 5 files changed, 48 insertions(+) create mode 100644 src/Psalm/Issue/AbstractMethodCall.php diff --git a/config.xsd b/config.xsd index 3083e5931..7e410942a 100644 --- a/config.xsd +++ b/config.xsd @@ -157,6 +157,7 @@ + diff --git a/docs/running_psalm/issues.md b/docs/running_psalm/issues.md index 2e39f7173..e5d97040c 100644 --- a/docs/running_psalm/issues.md +++ b/docs/running_psalm/issues.md @@ -9,6 +9,18 @@ abstract class A {} new A(); ``` +### AbstractMethodCall + +Emitted when an attempt is made to call an abstract method + +```php +abstract class Base { + abstract static function bar() : void; +} + +Base::bar(); +``` + ### ArgumentTypeCoercion Emitted when calling a function with an argument which has a less specific type than the function expects diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticCallAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticCallAnalyzer.php index 61cb8ed40..8e517fc55 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticCallAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticCallAnalyzer.php @@ -10,6 +10,7 @@ use Psalm\Internal\Analyzer\StatementsAnalyzer; use Psalm\CodeLocation; use Psalm\Context; use Psalm\Internal\FileManipulation\FileManipulationBuffer; +use Psalm\Issue\AbstractMethodCall; use Psalm\Issue\DeprecatedClass; use Psalm\Issue\ImpureMethodCall; use Psalm\Issue\InvalidStringClass; @@ -952,6 +953,20 @@ class StaticCallAnalyzer extends \Psalm\Internal\Analyzer\Statements\Expression\ $method_storage = $codebase->methods->getUserMethodStorage($method_id); if ($method_storage) { + if ($method_storage->abstract) { + if (IssueBuffer::accepts( + new AbstractMethodCall( + 'Cannot call an abstract method ' . $method_id, + new CodeLocation($statements_analyzer->getSource(), $stmt) + ), + $statements_analyzer->getSuppressedIssues() + )) { + // fall through + } + + return; + } + if ($context->pure && !$method_storage->pure) { if (IssueBuffer::accepts( new ImpureMethodCall( diff --git a/src/Psalm/Issue/AbstractMethodCall.php b/src/Psalm/Issue/AbstractMethodCall.php new file mode 100644 index 000000000..8326d59ef --- /dev/null +++ b/src/Psalm/Issue/AbstractMethodCall.php @@ -0,0 +1,7 @@ + 'InvalidStringClass', ], + 'preventAbstractMethodCall' => [ + ' 'AbstractMethodCall', + ], ]; } }