mirror of
https://github.com/danog/psalm.git
synced 2024-11-30 04:39:00 +01:00
Prevent incorrect template being passed to function
This commit is contained in:
parent
16768e363c
commit
2a1ed6ca6a
@ -519,46 +519,58 @@ class TypeAnalyzer
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($intersection_container_type_lower === $intersection_input_type_lower) {
|
if (!$intersection_container_type instanceof TTemplateParam
|
||||||
continue 2;
|
|| $intersection_input_type instanceof TTemplateParam
|
||||||
}
|
|
||||||
|
|
||||||
if ($intersection_input_type_lower === 'generator'
|
|
||||||
&& in_array($intersection_container_type_lower, ['iterator', 'traversable', 'iterable'], true)
|
|
||||||
) {
|
) {
|
||||||
continue 2;
|
if ($intersection_container_type instanceof TTemplateParam
|
||||||
}
|
&& $intersection_input_type instanceof TTemplateParam
|
||||||
|
) {
|
||||||
|
if ($intersection_container_type->param_name !== $intersection_input_type->param_name) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($intersection_input_type_lower === 'traversable'
|
if ($intersection_container_type_lower === $intersection_input_type_lower) {
|
||||||
&& $intersection_container_type_lower === 'iterable'
|
continue 2;
|
||||||
) {
|
}
|
||||||
continue 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
$input_type_is_interface = $codebase->interfaceExists($intersection_input_type_lower);
|
if ($intersection_input_type_lower === 'generator'
|
||||||
$container_type_is_interface = $codebase->interfaceExists($intersection_container_type_lower);
|
&& in_array($intersection_container_type_lower, ['iterator', 'traversable', 'iterable'], true)
|
||||||
|
) {
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
|
||||||
if ($allow_interface_equality && $input_type_is_interface && $container_type_is_interface) {
|
if ($intersection_input_type_lower === 'traversable'
|
||||||
continue 2;
|
&& $intersection_container_type_lower === 'iterable'
|
||||||
}
|
) {
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
|
||||||
if ($codebase->classExists($intersection_input_type_lower)
|
$input_type_is_interface = $codebase->interfaceExists($intersection_input_type_lower);
|
||||||
&& $codebase->classOrInterfaceExists($intersection_container_type_lower)
|
$container_type_is_interface = $codebase->interfaceExists($intersection_container_type_lower);
|
||||||
&& $codebase->classExtendsOrImplements(
|
|
||||||
$intersection_input_type_lower,
|
|
||||||
$intersection_container_type_lower
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
continue 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($input_type_is_interface
|
if ($allow_interface_equality && $input_type_is_interface && $container_type_is_interface) {
|
||||||
&& $codebase->interfaceExtends(
|
continue 2;
|
||||||
$intersection_input_type_lower,
|
}
|
||||||
$intersection_container_type_lower
|
|
||||||
)
|
if ($codebase->classExists($intersection_input_type_lower)
|
||||||
) {
|
&& $codebase->classOrInterfaceExists($intersection_container_type_lower)
|
||||||
continue 2;
|
&& $codebase->classExtendsOrImplements(
|
||||||
|
$intersection_input_type_lower,
|
||||||
|
$intersection_container_type_lower
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($input_type_is_interface
|
||||||
|
&& $codebase->interfaceExtends(
|
||||||
|
$intersection_input_type_lower,
|
||||||
|
$intersection_container_type_lower
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
continue 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ExpressionAnalyzer::isMock($intersection_input_type_lower)) {
|
if (ExpressionAnalyzer::isMock($intersection_input_type_lower)) {
|
||||||
@ -690,6 +702,10 @@ class TypeAnalyzer
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($container_type_part instanceof TTemplateParam && $input_type_part instanceof TTemplateParam) {
|
if ($container_type_part instanceof TTemplateParam && $input_type_part instanceof TTemplateParam) {
|
||||||
|
if ($container_type_part->param_name !== $input_type_part->param_name) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return self::isContainedBy(
|
return self::isContainedBy(
|
||||||
$codebase,
|
$codebase,
|
||||||
$input_type_part->as,
|
$input_type_part->as,
|
||||||
|
@ -1033,7 +1033,7 @@ class TemplateTest extends TestCase
|
|||||||
class I {
|
class I {
|
||||||
/**
|
/**
|
||||||
* @template T as Foo
|
* @template T as Foo
|
||||||
* @param class-string $class
|
* @param class-string<T> $class
|
||||||
* @template-typeof T $class
|
* @template-typeof T $class
|
||||||
* @return T|null
|
* @return T|null
|
||||||
*/
|
*/
|
||||||
@ -3490,6 +3490,21 @@ class TemplateTest extends TestCase
|
|||||||
}',
|
}',
|
||||||
'error_message' => 'InvalidReturnStatement'
|
'error_message' => 'InvalidReturnStatement'
|
||||||
],
|
],
|
||||||
|
'preventWrongTemplateBeingPassed' => [
|
||||||
|
'<?php
|
||||||
|
/**
|
||||||
|
* @template T of DateTime
|
||||||
|
* @template T2 of DateTime
|
||||||
|
* @param callable(T): T $parameter
|
||||||
|
* @param T2 $value
|
||||||
|
* @return T
|
||||||
|
*/
|
||||||
|
function foo(callable $parameter, $value)
|
||||||
|
{
|
||||||
|
return $parameter($value);
|
||||||
|
}',
|
||||||
|
'error_message' => 'InvalidArgument'
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user