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\ArgumentMapPopulator;
|
||||||
use Psalm\Internal\Analyzer\Statements\Expression\Call\ClassTemplateParamCollector;
|
use Psalm\Internal\Analyzer\Statements\Expression\Call\ClassTemplateParamCollector;
|
||||||
use Psalm\Internal\Analyzer\Statements\Expression\Call\ArgumentsAnalyzer;
|
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\Statements\Expression\ExpressionIdentifier;
|
||||||
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
||||||
use Psalm\Internal\Type\Comparator\UnionTypeComparator;
|
use Psalm\Internal\Type\Comparator\UnionTypeComparator;
|
||||||
@ -258,6 +259,33 @@ class AtomicMethodCallAnalyzer extends CallAnalyzer
|
|||||||
false
|
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;
|
$fake_method_exists = false;
|
||||||
|
|
||||||
if (!$naive_method_exists
|
if (!$naive_method_exists
|
||||||
|
@ -35,13 +35,14 @@ class ClosureFromCallableReturnTypeProvider implements \Psalm\Plugin\Hook\Method
|
|||||||
if (!$source instanceof \Psalm\Internal\Analyzer\StatementsAnalyzer) {
|
if (!$source instanceof \Psalm\Internal\Analyzer\StatementsAnalyzer) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$type_provider = $source->getNodeTypeProvider();
|
$type_provider = $source->getNodeTypeProvider();
|
||||||
$codebase = $source->getCodebase();
|
$codebase = $source->getCodebase();
|
||||||
|
|
||||||
|
if ($method_name_lowercase === 'fromcallable') {
|
||||||
$closure_types = [];
|
$closure_types = [];
|
||||||
|
|
||||||
if ($method_name_lowercase === 'fromcallable'
|
if (isset($call_args[0])
|
||||||
&& isset($call_args[0])
|
|
||||||
&& ($input_type = $type_provider->getType($call_args[0]->value))
|
&& ($input_type = $type_provider->getType($call_args[0]->value))
|
||||||
) {
|
) {
|
||||||
foreach ($input_type->getAtomicTypes() as $atomic_type) {
|
foreach ($input_type->getAtomicTypes() as $atomic_type) {
|
||||||
@ -72,3 +73,4 @@ class ClosureFromCallableReturnTypeProvider implements \Psalm\Plugin\Hook\Method
|
|||||||
return Type::getClosure();
|
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