mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
Contextual inference for closure param types
This commit is contained in:
parent
910c34f09c
commit
c0ca383020
@ -231,6 +231,23 @@ class ArgumentsAnalyzer
|
||||
);
|
||||
}
|
||||
|
||||
$inferred_arg_type = $statements_analyzer->node_data->getType($arg->value);
|
||||
|
||||
if (null !== $inferred_arg_type && null !== $template_result && null !== $param && null !== $param->type) {
|
||||
$codebase = $statements_analyzer->getCodebase();
|
||||
|
||||
TemplateStandinTypeReplacer::replace(
|
||||
clone $param->type,
|
||||
$template_result,
|
||||
$codebase,
|
||||
$statements_analyzer,
|
||||
$inferred_arg_type,
|
||||
$argument_offset,
|
||||
$context->self,
|
||||
$context->calling_method_id ?: $context->calling_function_id
|
||||
);
|
||||
}
|
||||
|
||||
if ($toggled_class_exists) {
|
||||
$context->inside_class_exists = false;
|
||||
}
|
||||
|
@ -10,7 +10,6 @@ use Psalm\Internal\Algebra;
|
||||
use Psalm\Internal\Algebra\FormulaGenerator;
|
||||
use Psalm\Internal\Analyzer\AlgebraAnalyzer;
|
||||
use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
|
||||
use Psalm\Internal\Analyzer\Statements\Expression\Call\MethodCallAnalyzer;
|
||||
use Psalm\Internal\Analyzer\Statements\Expression\CallAnalyzer;
|
||||
use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
|
||||
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
||||
@ -165,13 +164,20 @@ class FunctionCallAnalyzer extends CallAnalyzer
|
||||
}
|
||||
|
||||
if (!$is_first_class_callable) {
|
||||
$template_result = null;
|
||||
|
||||
if (isset($function_call_info->function_storage->template_types)) {
|
||||
$template_result = new TemplateResult($function_call_info->function_storage->template_types ?: [], []);
|
||||
}
|
||||
|
||||
ArgumentsAnalyzer::analyze(
|
||||
$statements_analyzer,
|
||||
$stmt->getArgs(),
|
||||
$function_call_info->function_params,
|
||||
$function_call_info->function_id,
|
||||
$function_call_info->allow_named_args,
|
||||
$context
|
||||
$context,
|
||||
$template_result
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -147,6 +147,96 @@ class CallableTest extends TestCase
|
||||
'$b' => 'ArrayList<array{int}>',
|
||||
],
|
||||
],
|
||||
'inferArgByPreviousFunctionArg' => [
|
||||
'<?php
|
||||
/**
|
||||
* @template A
|
||||
* @template B
|
||||
*
|
||||
* @param iterable<array-key, A> $_collection
|
||||
* @param callable(A): B $_ab
|
||||
* @return list<B>
|
||||
*/
|
||||
function map(iterable $_collection, callable $_ab) { return []; }
|
||||
|
||||
/** @template T */
|
||||
final class Foo
|
||||
{
|
||||
/** @return Foo<int> */
|
||||
public function toInt() { throw new RuntimeException("???"); }
|
||||
}
|
||||
|
||||
/** @var list<Foo<string>> */
|
||||
$items = [];
|
||||
|
||||
$inferred = map($items, function ($i) {
|
||||
return $i->toInt();
|
||||
});',
|
||||
'assertions' => [
|
||||
'$inferred' => 'list<Foo<int>>',
|
||||
],
|
||||
],
|
||||
'inferTemplateForExplicitlyTypedArgByPreviousFunctionArg' => [
|
||||
'<?php
|
||||
/**
|
||||
* @template A
|
||||
* @template B
|
||||
*
|
||||
* @param iterable<array-key, A> $_collection
|
||||
* @param callable(A): B $_ab
|
||||
* @return list<B>
|
||||
*/
|
||||
function map(iterable $_collection, callable $_ab) { return []; }
|
||||
|
||||
/** @template T */
|
||||
final class Foo
|
||||
{
|
||||
/** @return Foo<int> */
|
||||
public function toInt() { throw new RuntimeException("???"); }
|
||||
}
|
||||
|
||||
/** @var list<Foo<string>> */
|
||||
$items = [];
|
||||
|
||||
$inferred = map($items, function (Foo $i) {
|
||||
return $i->toInt();
|
||||
});',
|
||||
'assertions' => [
|
||||
'$inferred' => 'list<Foo<int>>',
|
||||
],
|
||||
],
|
||||
'doNotInferTemplateForExplicitlyTypedWithPhpdocArgByPreviousFunctionArg' => [
|
||||
'<?php
|
||||
/**
|
||||
* @template A
|
||||
* @template B
|
||||
*
|
||||
* @param iterable<array-key, A> $_collection
|
||||
* @param callable(A): B $_ab
|
||||
* @return list<B>
|
||||
*/
|
||||
function map(iterable $_collection, callable $_ab) { return []; }
|
||||
|
||||
/** @template T */
|
||||
final class Foo
|
||||
{
|
||||
/** @return Foo<int> */
|
||||
public function toInt() { throw new RuntimeException("???"); }
|
||||
}
|
||||
|
||||
/** @var list<Foo<string>> */
|
||||
$items = [];
|
||||
|
||||
$inferred = map($items,
|
||||
/** @param Foo $i */
|
||||
function ($i) {
|
||||
return $i;
|
||||
}
|
||||
);',
|
||||
'assertions' => [
|
||||
'$inferred' => 'list<Foo>',
|
||||
],
|
||||
],
|
||||
'varReturnType' => [
|
||||
'<?php
|
||||
$add_one = function(int $a) : int {
|
||||
|
Loading…
Reference in New Issue
Block a user