mirror of
https://github.com/danog/psalm.git
synced 2024-11-27 12:55:26 +01:00
Merge pull request #7460 from orklah/7453
handle two more cases of firstClassCallable
This commit is contained in:
commit
f5a093d015
@ -27,6 +27,7 @@ use Psalm\StatementsSource;
|
||||
use Psalm\Storage\ClassLikeStorage;
|
||||
use Psalm\Type;
|
||||
use Psalm\Type\Atomic;
|
||||
use Psalm\Type\Atomic\TClosure;
|
||||
use Psalm\Type\Atomic\TEmpty;
|
||||
use Psalm\Type\Atomic\TEmptyMixed;
|
||||
use Psalm\Type\Atomic\TFalse;
|
||||
@ -188,6 +189,41 @@ class AtomicMethodCallAnalyzer extends CallAnalyzer
|
||||
);
|
||||
}
|
||||
|
||||
if ($stmt->isFirstClassCallable()) {
|
||||
$return_type_candidate = null;
|
||||
$method_name_type = $statements_analyzer->node_data->getType($stmt->name);
|
||||
if ($method_name_type && $method_name_type->isSingleStringLiteral()) {
|
||||
$method_identifier = new MethodIdentifier(
|
||||
$fq_class_name,
|
||||
strtolower($method_name_type->getSingleStringLiteral()->value)
|
||||
);
|
||||
//the call to methodExists will register that the method was called from somewhere
|
||||
if ($codebase->methods->methodExists(
|
||||
$method_identifier,
|
||||
$context->calling_method_id,
|
||||
null,
|
||||
$statements_analyzer,
|
||||
$statements_analyzer->getFilePath(),
|
||||
true,
|
||||
$context->insideUse()
|
||||
)) {
|
||||
$method_storage = $codebase->methods->getStorage($method_identifier);
|
||||
|
||||
$return_type_candidate = new Union([new TClosure(
|
||||
'Closure',
|
||||
$method_storage->params,
|
||||
$method_storage->return_type,
|
||||
$method_storage->pure
|
||||
)]);
|
||||
}
|
||||
}
|
||||
|
||||
$statements_analyzer->node_data->setType($stmt, $return_type_candidate ?? Type::getClosure());
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ArgumentsAnalyzer::analyze(
|
||||
$statements_analyzer,
|
||||
$stmt->getArgs(),
|
||||
|
@ -225,6 +225,42 @@ class AtomicStaticCallAnalyzer
|
||||
);
|
||||
}
|
||||
|
||||
if ($stmt->isFirstClassCallable()) {
|
||||
$return_type_candidate = null;
|
||||
if (!$stmt->name instanceof PhpParser\Node\Identifier) {
|
||||
$method_name_type = $statements_analyzer->node_data->getType($stmt->name);
|
||||
if ($method_name_type && $method_name_type->isSingleStringLiteral()) {
|
||||
$method_identifier = new MethodIdentifier(
|
||||
$fq_class_name,
|
||||
strtolower($method_name_type->getSingleStringLiteral()->value)
|
||||
);
|
||||
//the call to methodExists will register that the method was called from somewhere
|
||||
if ($codebase->methods->methodExists(
|
||||
$method_identifier,
|
||||
$context->calling_method_id,
|
||||
null,
|
||||
$statements_analyzer,
|
||||
$statements_analyzer->getFilePath(),
|
||||
true,
|
||||
$context->insideUse()
|
||||
)) {
|
||||
$method_storage = $codebase->methods->getStorage($method_identifier);
|
||||
|
||||
$return_type_candidate = new Union([new TClosure(
|
||||
'Closure',
|
||||
$method_storage->params,
|
||||
$method_storage->return_type,
|
||||
$method_storage->pure
|
||||
)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$statements_analyzer->node_data->setType($stmt, $return_type_candidate ?? Type::getClosure());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (ArgumentsAnalyzer::analyze(
|
||||
$statements_analyzer,
|
||||
$stmt->getArgs(),
|
||||
|
@ -609,6 +609,27 @@ class ClosureTest extends TestCase
|
||||
[],
|
||||
'8.1'
|
||||
],
|
||||
'FirstClassCallable:InstanceMethod:Expr' => [
|
||||
'<?php
|
||||
class Test {
|
||||
public function __construct(private readonly string $string) {
|
||||
}
|
||||
|
||||
public function length(): int {
|
||||
return strlen($this->string);
|
||||
}
|
||||
}
|
||||
$test = new Test("test");
|
||||
$method_name = "length";
|
||||
$closure = $test->$method_name(...);
|
||||
$length = $closure();
|
||||
',
|
||||
'assertions' => [
|
||||
'$length' => 'int',
|
||||
],
|
||||
[],
|
||||
'8.1'
|
||||
],
|
||||
'FirstClassCallable:InstanceMethod:BuiltIn' => [
|
||||
'<?php
|
||||
$queue = new \SplQueue;
|
||||
@ -637,6 +658,23 @@ class ClosureTest extends TestCase
|
||||
[],
|
||||
'8.1'
|
||||
],
|
||||
'FirstClassCallable:StaticMethod:Expr' => [
|
||||
'<?php
|
||||
class Test {
|
||||
public static function length(string $param): int {
|
||||
return strlen($param);
|
||||
}
|
||||
}
|
||||
$method_name = "length";
|
||||
$closure = Test::$method_name(...);
|
||||
$length = $closure("test");
|
||||
',
|
||||
'assertions' => [
|
||||
'$length' => 'int',
|
||||
],
|
||||
[],
|
||||
'8.1'
|
||||
],
|
||||
'FirstClassCallable:InvokableObject' => [
|
||||
'<?php
|
||||
class Test {
|
||||
|
Loading…
Reference in New Issue
Block a user