1
0
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:
Matthew Brown 2018-06-09 10:14:18 -04:00
parent 3a3c10d044
commit 6d4d0cf825
3 changed files with 80 additions and 9 deletions

View File

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

View File

@ -101,4 +101,11 @@ class FunctionLikeParameter
. ($this->is_variadic ? '...' : '')
. ($this->is_optional ? '=' : '');
}
public function __clone()
{
if ($this->type) {
$this->type = clone $this->type;
}
}
}

View File

@ -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',
],
];
}
}