mirror of
https://github.com/danog/psalm.git
synced 2024-11-26 20:34:47 +01:00
Fix #794 - use template types to restrict input
This commit is contained in:
parent
3a3c10d044
commit
6d4d0cf825
@ -645,6 +645,17 @@ class CallChecker
|
||||
}
|
||||
}
|
||||
|
||||
if ($generic_params) {
|
||||
$existing_generic_params_to_strings = array_map(
|
||||
function(Type\Union $type) {
|
||||
return (string) $type;
|
||||
},
|
||||
$generic_params
|
||||
);
|
||||
} else {
|
||||
$existing_generic_params_to_strings = [];
|
||||
}
|
||||
|
||||
foreach ($args as $argument_offset => $arg) {
|
||||
$function_param = count($function_params) > $argument_offset
|
||||
? $function_params[$argument_offset]
|
||||
@ -794,17 +805,29 @@ class CallChecker
|
||||
}
|
||||
|
||||
$generic_params[$template_type] = $offset_value_type ?: Type::getMixed();
|
||||
} elseif ($template_types) {
|
||||
if ($generic_params === null) {
|
||||
$generic_params = [];
|
||||
} else {
|
||||
if ($existing_generic_params_to_strings) {
|
||||
$empty_generic_params = [];
|
||||
$param_type->replaceTemplateTypesWithStandins(
|
||||
$existing_generic_params_to_strings,
|
||||
$empty_generic_params,
|
||||
$codebase,
|
||||
$arg->value->inferredType
|
||||
);
|
||||
}
|
||||
|
||||
$param_type->replaceTemplateTypesWithStandins(
|
||||
$template_types,
|
||||
$generic_params,
|
||||
$codebase,
|
||||
$arg->value->inferredType
|
||||
);
|
||||
if ($template_types) {
|
||||
if ($generic_params === null) {
|
||||
$generic_params = [];
|
||||
}
|
||||
|
||||
$param_type->replaceTemplateTypesWithStandins(
|
||||
$template_types,
|
||||
$generic_params,
|
||||
$codebase,
|
||||
$arg->value->inferredType
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,4 +101,11 @@ class FunctionLikeParameter
|
||||
. ($this->is_variadic ? '...' : '')
|
||||
. ($this->is_optional ? '=' : '');
|
||||
}
|
||||
|
||||
public function __clone()
|
||||
{
|
||||
if ($this->type) {
|
||||
$this->type = clone $this->type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -685,6 +685,47 @@ class TemplateTest extends TestCase
|
||||
function takesArrayOfStdClass(array $p): void {}',
|
||||
'error_message' => 'MixedTypeCoercion',
|
||||
],
|
||||
'restrictTemplateInput' => [
|
||||
'<?php
|
||||
/** @template T */
|
||||
class Foo
|
||||
{
|
||||
/**
|
||||
* @psalm-var class-string
|
||||
*/
|
||||
private $type;
|
||||
|
||||
/** @var array<T> */
|
||||
private $items;
|
||||
|
||||
/**
|
||||
* @param class-string $type
|
||||
* @template-typeof T $type
|
||||
*/
|
||||
public function __construct(string $type)
|
||||
{
|
||||
if (!in_array($type, [A::class, B::class], true)) {
|
||||
throw new \InvalidArgumentException;
|
||||
}
|
||||
$this->type = $type;
|
||||
$this->items = [];
|
||||
}
|
||||
|
||||
/** @param T $item */
|
||||
public function add($item): void
|
||||
{
|
||||
$this->items[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
class A {}
|
||||
class B {}
|
||||
|
||||
|
||||
$foo = new Foo(A::class);
|
||||
$foo->add(new B);',
|
||||
'error_message' => 'InvalidArgument',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user