Skip to content

Commit

Permalink
Merge pull request #7228 from klimick/contextual-inference-for-closur…
Browse files Browse the repository at this point in the history
…e-param-types
  • Loading branch information
weirdan committed Dec 27, 2021
2 parents 910c34f + 87deb19 commit 33466d8
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 2 deletions.
Expand Up @@ -231,6 +231,23 @@ public static function analyze(
);
}

$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;
}
Expand Down
Expand Up @@ -10,7 +10,6 @@
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;
Expand Down Expand Up @@ -165,13 +164,20 @@ public static function analyze(
}

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
);
}

Expand Down
86 changes: 86 additions & 0 deletions tests/CallableTest.php
Expand Up @@ -147,6 +147,92 @@ function asTupled(ArrayList $list): ArrayList
'$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 { }
/** @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 {
Expand Down

0 comments on commit 33466d8

Please sign in to comment.