Skip to content
Permalink
Browse files

Prevent incorrect template being passed to function

  • Loading branch information...
muglug committed Jun 24, 2019
1 parent 16768e3 commit 2a1ed6ca6ae89e66d687e1cb5e88c9bb7fdbe1b2
Showing with 66 additions and 35 deletions.
  1. +50 −34 src/Psalm/Internal/Analyzer/TypeAnalyzer.php
  2. +16 −1 tests/Template/TemplateTest.php
@@ -519,46 +519,58 @@ private static function isObjectContainedByObject(
);
}
if ($intersection_container_type_lower === $intersection_input_type_lower) {
continue 2;
}
if ($intersection_input_type_lower === 'generator'
&& in_array($intersection_container_type_lower, ['iterator', 'traversable', 'iterable'], true)
if (!$intersection_container_type instanceof TTemplateParam
|| $intersection_input_type instanceof TTemplateParam
) {
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'
&& $intersection_container_type_lower === 'iterable'
) {
continue 2;
}
if ($intersection_container_type_lower === $intersection_input_type_lower) {
continue 2;
}
$input_type_is_interface = $codebase->interfaceExists($intersection_input_type_lower);
$container_type_is_interface = $codebase->interfaceExists($intersection_container_type_lower);
if ($intersection_input_type_lower === 'generator'
&& in_array($intersection_container_type_lower, ['iterator', 'traversable', 'iterable'], true)
) {
continue 2;
}
if ($allow_interface_equality && $input_type_is_interface && $container_type_is_interface) {
continue 2;
}
if ($intersection_input_type_lower === 'traversable'
&& $intersection_container_type_lower === 'iterable'
) {
continue 2;
}
if ($codebase->classExists($intersection_input_type_lower)
&& $codebase->classOrInterfaceExists($intersection_container_type_lower)
&& $codebase->classExtendsOrImplements(
$intersection_input_type_lower,
$intersection_container_type_lower
)
) {
continue 2;
}
$input_type_is_interface = $codebase->interfaceExists($intersection_input_type_lower);
$container_type_is_interface = $codebase->interfaceExists($intersection_container_type_lower);
if ($input_type_is_interface
&& $codebase->interfaceExtends(
$intersection_input_type_lower,
$intersection_container_type_lower
)
) {
continue 2;
if ($allow_interface_equality && $input_type_is_interface && $container_type_is_interface) {
continue 2;
}
if ($codebase->classExists($intersection_input_type_lower)
&& $codebase->classOrInterfaceExists($intersection_container_type_lower)
&& $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)) {
@@ -690,6 +702,10 @@ public static function isAtomicContainedBy(
}
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(
$codebase,
$input_type_part->as,
@@ -1033,7 +1033,7 @@ function foo(string $class, array $args) : void {
class I {
/**
* @template T as Foo
* @param class-string $class
* @param class-string<T> $class
* @template-typeof T $class
* @return T|null
*/
@@ -3490,6 +3490,21 @@ public function getObject(string $type)
}',
'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'
],
];
}
}

0 comments on commit 2a1ed6c

Please sign in to comment.
You can’t perform that action at this time.