1
0
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:
Brown 2019-06-24 10:54:03 -04:00
parent 16768e363c
commit 2a1ed6ca6a
2 changed files with 66 additions and 35 deletions

View File

@ -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,

View File

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