mirror of
https://github.com/danog/psalm.git
synced 2025-01-22 05:41:20 +01:00
Allow function names to passed in as strings for callable template resolution
This commit is contained in:
parent
ff9d774304
commit
e0d555e203
@ -795,6 +795,7 @@ class ClassAnalyzer extends ClassLikeAnalyzer
|
||||
$template_result,
|
||||
$codebase,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
@ -1823,6 +1824,7 @@ class ClassAnalyzer extends ClassLikeAnalyzer
|
||||
$template_result,
|
||||
$codebase,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
@ -774,6 +774,7 @@ class ReturnTypeAnalyzer
|
||||
$template_result,
|
||||
$codebase,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
@ -503,6 +503,7 @@ class CallAnalyzer
|
||||
$replaced_type,
|
||||
$replace_template_result,
|
||||
$codebase,
|
||||
$statements_analyzer,
|
||||
null,
|
||||
'fn-' . $context->calling_function_id
|
||||
);
|
||||
@ -596,6 +597,7 @@ class CallAnalyzer
|
||||
$generic_param_type,
|
||||
$replace_template_result,
|
||||
$codebase,
|
||||
$statements_analyzer,
|
||||
$statements_analyzer->node_data->getType($arg->value),
|
||||
'fn-' . $context->calling_function_id
|
||||
);
|
||||
@ -1313,6 +1315,7 @@ class CallAnalyzer
|
||||
$function_param->type,
|
||||
$template_result,
|
||||
$codebase,
|
||||
$statements_analyzer,
|
||||
$arg_value_type,
|
||||
$context->self,
|
||||
$context->calling_function_id,
|
||||
@ -1526,6 +1529,7 @@ class CallAnalyzer
|
||||
$param->type,
|
||||
$template_result,
|
||||
$codebase,
|
||||
$statements_analyzer,
|
||||
clone $param->default_type,
|
||||
$context->self,
|
||||
$context->calling_function_id,
|
||||
@ -1723,6 +1727,7 @@ class CallAnalyzer
|
||||
clone $by_ref_type,
|
||||
$template_result,
|
||||
$codebase,
|
||||
$statements_analyzer,
|
||||
$statements_analyzer->node_data->getType($arg->value),
|
||||
'fn-' . $context->calling_function_id
|
||||
);
|
||||
@ -1743,6 +1748,7 @@ class CallAnalyzer
|
||||
clone $by_ref_out_type,
|
||||
$template_result,
|
||||
$codebase,
|
||||
$statements_analyzer,
|
||||
$statements_analyzer->node_data->getType($arg->value),
|
||||
'fn-' . $context->calling_function_id
|
||||
);
|
||||
@ -1832,6 +1838,7 @@ class CallAnalyzer
|
||||
$param_type,
|
||||
$empty_template_result,
|
||||
$codebase,
|
||||
$statements_analyzer,
|
||||
$arg_value_type,
|
||||
$context->self ?: 'fn-' . $context->calling_function_id
|
||||
);
|
||||
@ -1840,6 +1847,7 @@ class CallAnalyzer
|
||||
$arg_type,
|
||||
$empty_template_result,
|
||||
$codebase,
|
||||
$statements_analyzer,
|
||||
$arg_value_type,
|
||||
$context->self ?: 'fn-' . $context->calling_function_id
|
||||
);
|
||||
@ -1884,6 +1892,7 @@ class CallAnalyzer
|
||||
$param_type,
|
||||
$template_result,
|
||||
$codebase,
|
||||
$statements_analyzer,
|
||||
$arg_type_param,
|
||||
$context->self,
|
||||
$context->calling_function_id
|
||||
@ -2453,6 +2462,7 @@ class CallAnalyzer
|
||||
$closure_param_type,
|
||||
$template_result,
|
||||
$codebase,
|
||||
$statements_analyzer,
|
||||
$input_type,
|
||||
$context->self,
|
||||
$context->calling_function_id
|
||||
|
@ -1782,11 +1782,15 @@ class TypeAnalyzer
|
||||
public static function getCallableFromAtomic(
|
||||
Codebase $codebase,
|
||||
Type\Atomic $input_type_part,
|
||||
?TCallable $container_type_part = null
|
||||
?TCallable $container_type_part = null,
|
||||
?StatementsAnalyzer $statements_analyzer = null
|
||||
) : ?TCallable {
|
||||
if ($input_type_part instanceof TLiteralString) {
|
||||
try {
|
||||
$function_storage = $codebase->functions->getStorage(null, $input_type_part->value);
|
||||
$function_storage = $codebase->functions->getStorage(
|
||||
$statements_analyzer,
|
||||
$input_type_part->value
|
||||
);
|
||||
|
||||
return new TCallable(
|
||||
'callable',
|
||||
|
@ -280,6 +280,7 @@ class ArrayMapReturnTypeProvider implements \Psalm\Plugin\Hook\FunctionReturnTyp
|
||||
$closure_param_type,
|
||||
$template_result,
|
||||
$codebase,
|
||||
$statements_source,
|
||||
$array_arg_type->value,
|
||||
$context->self,
|
||||
$context->calling_function_id
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace Psalm\Internal\Type;
|
||||
|
||||
use Psalm\Codebase;
|
||||
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
||||
use Psalm\Internal\Analyzer\TypeAnalyzer;
|
||||
use Psalm\Type\Union;
|
||||
use Psalm\Type\Atomic;
|
||||
@ -21,6 +22,7 @@ class UnionTemplateHandler
|
||||
Union $union_type,
|
||||
TemplateResult $template_result,
|
||||
?Codebase $codebase,
|
||||
?StatementsAnalyzer $statements_analyzer,
|
||||
?Union $input_type,
|
||||
?string $calling_class = null,
|
||||
?string $calling_function = null,
|
||||
@ -42,6 +44,7 @@ class UnionTemplateHandler
|
||||
$key,
|
||||
$template_result,
|
||||
$codebase,
|
||||
$statements_analyzer,
|
||||
$input_type,
|
||||
$calling_class,
|
||||
$calling_function,
|
||||
@ -87,6 +90,7 @@ class UnionTemplateHandler
|
||||
string $key,
|
||||
TemplateResult $template_result,
|
||||
?Codebase $codebase,
|
||||
?StatementsAnalyzer $statements_analyzer,
|
||||
?Union $input_type,
|
||||
?string $calling_class,
|
||||
?string $calling_function,
|
||||
@ -239,13 +243,15 @@ class UnionTemplateHandler
|
||||
$input_type,
|
||||
$atomic_type,
|
||||
$key,
|
||||
$codebase
|
||||
$codebase,
|
||||
$statements_analyzer
|
||||
);
|
||||
}
|
||||
|
||||
$atomic_type = $atomic_type->replaceTemplateTypesWithStandins(
|
||||
$template_result,
|
||||
$codebase,
|
||||
$statements_analyzer,
|
||||
$matching_atomic_type,
|
||||
$calling_class,
|
||||
$calling_function,
|
||||
@ -257,11 +263,12 @@ class UnionTemplateHandler
|
||||
return [$atomic_type];
|
||||
}
|
||||
|
||||
public static function findMatchingAtomicTypeForTemplate(
|
||||
private static function findMatchingAtomicTypeForTemplate(
|
||||
Union $input_type,
|
||||
Atomic $atomic_type,
|
||||
string $key,
|
||||
Codebase $codebase
|
||||
Codebase $codebase,
|
||||
?StatementsAnalyzer $statements_analyzer
|
||||
) : ?Atomic {
|
||||
foreach ($input_type->getAtomicTypes() as $input_key => $atomic_input_type) {
|
||||
if ($bracket_pos = strpos($input_key, '<')) {
|
||||
@ -301,7 +308,9 @@ class UnionTemplateHandler
|
||||
if ($atomic_type instanceof Atomic\TCallable) {
|
||||
$matching_atomic_type = TypeAnalyzer::getCallableFromAtomic(
|
||||
$codebase,
|
||||
$atomic_input_type
|
||||
$atomic_input_type,
|
||||
null,
|
||||
$statements_analyzer
|
||||
);
|
||||
|
||||
if ($matching_atomic_type) {
|
||||
|
@ -12,6 +12,7 @@ use Psalm\Codebase;
|
||||
use Psalm\CodeLocation;
|
||||
use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
|
||||
use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
|
||||
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
||||
use Psalm\Internal\Analyzer\TypeAnalyzer;
|
||||
use Psalm\Internal\Type\TemplateResult;
|
||||
use Psalm\Issue\InvalidTemplateParam;
|
||||
@ -860,6 +861,7 @@ abstract class Atomic
|
||||
public function replaceTemplateTypesWithStandins(
|
||||
TemplateResult $template_result,
|
||||
Codebase $codebase = null,
|
||||
?StatementsAnalyzer $statements_analyer = null,
|
||||
Type\Atomic $input_type = null,
|
||||
?string $calling_class = null,
|
||||
?string $calling_function = null,
|
||||
|
@ -6,6 +6,7 @@ use function count;
|
||||
use function implode;
|
||||
use Psalm\Codebase;
|
||||
use Psalm\CodeLocation;
|
||||
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
||||
use Psalm\Internal\Type\TemplateResult;
|
||||
use Psalm\Internal\Type\UnionTemplateHandler;
|
||||
use Psalm\StatementsSource;
|
||||
@ -197,6 +198,7 @@ trait CallableTrait
|
||||
public function replaceTemplateTypesWithStandins(
|
||||
TemplateResult $template_result,
|
||||
Codebase $codebase = null,
|
||||
?StatementsAnalyzer $statements_analyzer = null,
|
||||
Atomic $input_type = null,
|
||||
?string $calling_class = null,
|
||||
?string $calling_function = null,
|
||||
@ -224,6 +226,7 @@ trait CallableTrait
|
||||
$param->type,
|
||||
$template_result,
|
||||
$codebase,
|
||||
$statements_analyzer,
|
||||
$input_param_type,
|
||||
$calling_class,
|
||||
$calling_function,
|
||||
@ -242,6 +245,7 @@ trait CallableTrait
|
||||
$callable->return_type,
|
||||
$template_result,
|
||||
$codebase,
|
||||
$statements_analyzer,
|
||||
$input_type->return_type,
|
||||
$calling_class,
|
||||
$calling_function,
|
||||
|
@ -5,6 +5,7 @@ use function array_map;
|
||||
use function implode;
|
||||
use Psalm\Codebase;
|
||||
use Psalm\CodeLocation;
|
||||
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
||||
use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
|
||||
use Psalm\Internal\Analyzer\TypeAnalyzer;
|
||||
use Psalm\Internal\Type\TemplateResult;
|
||||
@ -164,7 +165,8 @@ trait GenericTrait
|
||||
|
||||
public function replaceTemplateTypesWithStandins(
|
||||
TemplateResult $template_result,
|
||||
Codebase $codebase = null,
|
||||
?Codebase $codebase = null,
|
||||
?StatementsAnalyzer $statements_analyzer = null,
|
||||
Atomic $input_type = null,
|
||||
?string $calling_class = null,
|
||||
?string $calling_function = null,
|
||||
@ -203,6 +205,7 @@ trait GenericTrait
|
||||
$type_param,
|
||||
$template_result,
|
||||
$codebase,
|
||||
$statements_analyzer,
|
||||
$input_type_param,
|
||||
$calling_class,
|
||||
$calling_function,
|
||||
|
@ -11,6 +11,7 @@ use function sort;
|
||||
use Psalm\Codebase;
|
||||
use Psalm\CodeLocation;
|
||||
use Psalm\StatementsSource;
|
||||
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
||||
use Psalm\Internal\Type\TypeCombination;
|
||||
use Psalm\Internal\Type\TemplateResult;
|
||||
use Psalm\Internal\Type\UnionTemplateHandler;
|
||||
@ -314,6 +315,7 @@ class ObjectLike extends \Psalm\Type\Atomic
|
||||
public function replaceTemplateTypesWithStandins(
|
||||
TemplateResult $template_result,
|
||||
Codebase $codebase = null,
|
||||
StatementsAnalyzer $statements_analyzer = null,
|
||||
Atomic $input_type = null,
|
||||
?string $calling_class = null,
|
||||
?string $calling_function = null,
|
||||
@ -336,6 +338,7 @@ class ObjectLike extends \Psalm\Type\Atomic
|
||||
$property,
|
||||
$template_result,
|
||||
$codebase,
|
||||
$statements_analyzer,
|
||||
$input_type_param,
|
||||
$calling_class,
|
||||
$calling_function,
|
||||
|
@ -5,6 +5,7 @@ use function get_class;
|
||||
use Psalm\Codebase;
|
||||
use Psalm\CodeLocation;
|
||||
use Psalm\StatementsSource;
|
||||
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
||||
use Psalm\Internal\Type\TemplateResult;
|
||||
use Psalm\Internal\Type\UnionTemplateHandler;
|
||||
use Psalm\Type;
|
||||
@ -147,6 +148,7 @@ class TClassStringMap extends \Psalm\Type\Atomic
|
||||
public function replaceTemplateTypesWithStandins(
|
||||
TemplateResult $template_result,
|
||||
Codebase $codebase = null,
|
||||
StatementsAnalyzer $statements_analyzer = null,
|
||||
Atomic $input_type = null,
|
||||
?string $calling_class = null,
|
||||
?string $calling_function = null,
|
||||
@ -184,6 +186,7 @@ class TClassStringMap extends \Psalm\Type\Atomic
|
||||
$type_param,
|
||||
$template_result,
|
||||
$codebase,
|
||||
$statements_analyzer,
|
||||
$input_type_param,
|
||||
$calling_class,
|
||||
$calling_function,
|
||||
|
@ -5,6 +5,7 @@ use function get_class;
|
||||
use Psalm\Codebase;
|
||||
use Psalm\CodeLocation;
|
||||
use Psalm\StatementsSource;
|
||||
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
||||
use Psalm\Internal\Type\TemplateResult;
|
||||
use Psalm\Internal\Type\UnionTemplateHandler;
|
||||
use Psalm\Type;
|
||||
@ -117,6 +118,7 @@ class TList extends \Psalm\Type\Atomic
|
||||
public function replaceTemplateTypesWithStandins(
|
||||
TemplateResult $template_result,
|
||||
Codebase $codebase = null,
|
||||
StatementsAnalyzer $statements_analyzer = null,
|
||||
Atomic $input_type = null,
|
||||
?string $calling_class = null,
|
||||
?string $calling_function = null,
|
||||
@ -154,6 +156,7 @@ class TList extends \Psalm\Type\Atomic
|
||||
$type_param,
|
||||
$template_result,
|
||||
$codebase,
|
||||
$statements_analyzer,
|
||||
$input_type_param,
|
||||
$calling_class,
|
||||
$calling_function,
|
||||
|
@ -11,6 +11,7 @@ use Psalm\StatementsSource;
|
||||
use Psalm\Type;
|
||||
use Psalm\Type\Atomic;
|
||||
use Psalm\Type\Union;
|
||||
use Psalm\Internal\Analyzer\StatementsAnalyzer;
|
||||
use Psalm\Internal\Type\TypeCombination;
|
||||
use Psalm\Internal\Type\TemplateResult;
|
||||
use Psalm\Internal\Type\UnionTemplateHandler;
|
||||
@ -245,6 +246,7 @@ class TObjectWithProperties extends TObject
|
||||
public function replaceTemplateTypesWithStandins(
|
||||
TemplateResult $template_result,
|
||||
Codebase $codebase = null,
|
||||
StatementsAnalyzer $statements_analyzer = null,
|
||||
Atomic $input_type = null,
|
||||
?string $calling_class = null,
|
||||
?string $calling_function = null,
|
||||
@ -267,6 +269,7 @@ class TObjectWithProperties extends TObject
|
||||
$property,
|
||||
$template_result,
|
||||
$codebase,
|
||||
$statements_analyzer,
|
||||
$input_type_param,
|
||||
$calling_class,
|
||||
$calling_function,
|
||||
|
@ -1030,6 +1030,93 @@ class FunctionTemplateTest extends TestCase
|
||||
return new MockObject;
|
||||
}'
|
||||
],
|
||||
'testStringCallableInference' => [
|
||||
'<?php
|
||||
class A {
|
||||
public static function dup(string $a): string {
|
||||
return $a . $a;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param iterable<T> $iter
|
||||
* @return list<T>
|
||||
*/
|
||||
function toArray(iterable $iter): array {
|
||||
$data = [];
|
||||
foreach ($iter as $key => $val) {
|
||||
$data[] = $val;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @template U
|
||||
* @param callable(T): U $predicate
|
||||
* @return callable(iterable<T>): iterable<U>
|
||||
*/
|
||||
function map(callable $predicate): callable {
|
||||
return
|
||||
/** @param iterable<T> $iter */
|
||||
function(iterable $iter) use ($predicate): iterable {
|
||||
foreach ($iter as $key => $value) {
|
||||
yield $key => $predicate($value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** @param list<string> $strings */
|
||||
function _test(array $strings): void {}
|
||||
$a = map([A::class, "dup"])(["a", "b", "c"]);',
|
||||
[
|
||||
'$a' => 'iterable<mixed, string>'
|
||||
]
|
||||
],
|
||||
'testClosureCallableInference' => [
|
||||
'<?php
|
||||
/**
|
||||
* @template T
|
||||
* @param iterable<T> $iter
|
||||
* @return list<T>
|
||||
*/
|
||||
function toArray(iterable $iter): array {
|
||||
$data = [];
|
||||
foreach ($iter as $key => $val) {
|
||||
$data[] = $val;
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @template U
|
||||
* @param callable(T): U $predicate
|
||||
* @return callable(iterable<T>): iterable<U>
|
||||
*/
|
||||
function map(callable $predicate): callable {
|
||||
return
|
||||
/** @param iterable<T> $iter */
|
||||
function(iterable $iter) use ($predicate): iterable {
|
||||
foreach ($iter as $key => $value) {
|
||||
yield $key => $predicate($value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/** @param list<string> $strings */
|
||||
function _test(array $strings): void {}
|
||||
|
||||
$a = map(
|
||||
function (string $a) {
|
||||
return $a . $a;
|
||||
}
|
||||
)(["a", "b", "c"]);',
|
||||
[
|
||||
'$a' => 'iterable<mixed, string>'
|
||||
]
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user