diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php index 262935153..e89cd6faf 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php @@ -38,6 +38,7 @@ use Psalm\Issue\MixedArgumentTypeCoercion; use Psalm\Issue\NamedArgumentNotAllowed; use Psalm\Issue\NoValue; use Psalm\Issue\NullArgument; +use Psalm\Issue\ParentNotFound; use Psalm\Issue\PossiblyFalseArgument; use Psalm\Issue\PossiblyInvalidArgument; use Psalm\Issue\PossiblyNullArgument; @@ -1297,6 +1298,16 @@ final class ArgumentAnalyzer if ($callable_fq_class_name === 'parent') { $container_class = $statements_analyzer->getParentFQCLN(); + if ($container_class === null) { + IssueBuffer::accepts( + new ParentNotFound( + 'Cannot call method on parent' + . ' as this class does not extend another', + $arg_location, + ), + $statements_analyzer->getSuppressedIssues(), + ); + } } if (!$container_class) { diff --git a/tests/CallableTest.php b/tests/CallableTest.php index eff0e838a..9e32eb830 100644 --- a/tests/CallableTest.php +++ b/tests/CallableTest.php @@ -2533,6 +2533,40 @@ class CallableTest extends TestCase 'ignored_issues' => [], 'php_version' => '8.0', ], + 'parentCallableArrayWithoutParent' => [ + 'code' => 'run(["parent", "hello"]); + } + + /** + * @param callable $callable + * @return void + */ + public function run($callable) { + call_user_func($callable); + } + }', + 'error_message' => 'ParentNotFound', + ], + 'parentCallableWithoutParent' => [ + 'code' => 'run("parent::hello"); + } + + /** + * @param callable $callable + * @return void + */ + public function run($callable) { + call_user_func($callable); + } + }', + 'error_message' => 'ParentNotFound', + ], ]; } }