mirror of
https://github.com/danog/psalm.git
synced 2024-11-27 04:45:20 +01:00
Fix erroneous Closure::__invoke return type
This commit is contained in:
parent
c6ea274180
commit
b5279cd7d4
@ -10,6 +10,7 @@ use Psalm\Internal\Analyzer\Statements\Expression\CallAnalyzer;
|
||||
use Psalm\Internal\Analyzer\Statements\Expression\Call\ArgumentMapPopulator;
|
||||
use Psalm\Internal\Analyzer\Statements\Expression\Call\ClassTemplateParamCollector;
|
||||
use Psalm\Internal\Analyzer\Statements\Expression\Call\ArgumentsAnalyzer;
|
||||
use Psalm\Internal\Analyzer\Statements\Expression\Call\FunctionCallAnalyzer;
|
||||
use Psalm\Internal\Analyzer\Statements\Expression\ExpressionIdentifier;
|
||||
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
||||
use Psalm\Internal\Type\Comparator\UnionTypeComparator;
|
||||
@ -258,6 +259,33 @@ class AtomicMethodCallAnalyzer extends CallAnalyzer
|
||||
false
|
||||
);
|
||||
|
||||
if ($naive_method_exists && $fq_class_name === 'Closure' && $method_name_lc === '__invoke') {
|
||||
$old_node_data = $statements_analyzer->node_data;
|
||||
$statements_analyzer->node_data = clone $statements_analyzer->node_data;
|
||||
|
||||
$fake_function_call = new PhpParser\Node\Expr\FuncCall(
|
||||
$stmt->var,
|
||||
$stmt->args,
|
||||
$stmt->getAttributes()
|
||||
);
|
||||
$ret_value = FunctionCallAnalyzer::analyze(
|
||||
$statements_analyzer,
|
||||
$fake_function_call,
|
||||
$context
|
||||
);
|
||||
|
||||
$function_return = $statements_analyzer->node_data->getType($fake_function_call);
|
||||
$statements_analyzer->node_data = $old_node_data;
|
||||
|
||||
if (!$result->return_type) {
|
||||
$result->return_type = $function_return;
|
||||
} else {
|
||||
$result->return_type = Type::combineUnionTypes($function_return, $result->return_type);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$fake_method_exists = false;
|
||||
|
||||
if (!$naive_method_exists
|
||||
|
@ -35,13 +35,14 @@ class ClosureFromCallableReturnTypeProvider implements \Psalm\Plugin\Hook\Method
|
||||
if (!$source instanceof \Psalm\Internal\Analyzer\StatementsAnalyzer) {
|
||||
return;
|
||||
}
|
||||
|
||||
$type_provider = $source->getNodeTypeProvider();
|
||||
$codebase = $source->getCodebase();
|
||||
|
||||
if ($method_name_lowercase === 'fromcallable') {
|
||||
$closure_types = [];
|
||||
|
||||
if ($method_name_lowercase === 'fromcallable'
|
||||
&& isset($call_args[0])
|
||||
if (isset($call_args[0])
|
||||
&& ($input_type = $type_provider->getType($call_args[0]->value))
|
||||
) {
|
||||
foreach ($input_type->getAtomicTypes() as $atomic_type) {
|
||||
@ -72,3 +73,4 @@ class ClosureFromCallableReturnTypeProvider implements \Psalm\Plugin\Hook\Method
|
||||
return Type::getClosure();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -492,6 +492,24 @@ class ClosureTest extends TestCase
|
||||
})()
|
||||
);'
|
||||
],
|
||||
'callingInvokeOnClosureIsSameAsCallingDirectly' => [
|
||||
'<?php
|
||||
class A {
|
||||
/** @var Closure(int):int */
|
||||
private Closure $a;
|
||||
|
||||
public function __construct() {
|
||||
$this->a = fn(int $a) : int => $a + 5;
|
||||
}
|
||||
|
||||
public function invoker(int $b) : int {
|
||||
return $this->a->__invoke($b);
|
||||
}
|
||||
}',
|
||||
'assertions' => [],
|
||||
'error_levels' => [],
|
||||
'7.4'
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user