1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-30 04:39:00 +01:00

Fix #2308 - prevent specialisation from bound params

This commit is contained in:
Brown 2019-11-06 11:20:51 -05:00
parent d7e435c352
commit a8ed6ba9c4
9 changed files with 67 additions and 26 deletions

View File

@ -760,6 +760,7 @@ class ClassAnalyzer extends ClassLikeAnalyzer
$fleshed_out_type, $fleshed_out_type,
$template_result, $template_result,
$codebase, $codebase,
null,
null null
); );
} }
@ -1719,6 +1720,7 @@ class ClassAnalyzer extends ClassLikeAnalyzer
$return_type, $return_type,
$template_result, $template_result,
$codebase, $codebase,
null,
null null
); );
} }

View File

@ -728,6 +728,7 @@ class ReturnTypeAnalyzer
$fleshed_out_return_type, $fleshed_out_return_type,
$template_result, $template_result,
$codebase, $codebase,
null,
null null
); );
} }

View File

@ -249,7 +249,7 @@ class CallAnalyzer
protected static function checkMethodArgs( protected static function checkMethodArgs(
$method_id, $method_id,
array $args, array $args,
?TemplateResult $template_result, ?TemplateResult $class_template_result,
Context $context, Context $context,
CodeLocation $code_location, CodeLocation $code_location,
StatementsAnalyzer $statements_analyzer StatementsAnalyzer $statements_analyzer
@ -266,7 +266,7 @@ class CallAnalyzer
$method_params, $method_params,
$method_id, $method_id,
$context, $context,
$template_result $class_template_result
) === false) { ) === false) {
return false; return false;
} }
@ -325,7 +325,7 @@ class CallAnalyzer
$method_params, $method_params,
$method_storage, $method_storage,
$class_storage, $class_storage,
$template_result, $class_template_result,
$code_location, $code_location,
$context $context
) === false) { ) === false) {
@ -475,6 +475,7 @@ class CallAnalyzer
$replaced_type, $replaced_type,
$replace_template_result, $replace_template_result,
$codebase, $codebase,
null,
null null
); );
@ -564,7 +565,8 @@ class CallAnalyzer
$codebase, $codebase,
isset($arg->value->inferredType) isset($arg->value->inferredType)
? $arg->value->inferredType ? $arg->value->inferredType
: null : null,
null
); );
if ($replace_template_result->generic_params) { if ($replace_template_result->generic_params) {
@ -1124,7 +1126,7 @@ class CallAnalyzer
array $function_params, array $function_params,
$function_storage, $function_storage,
$class_storage, $class_storage,
?TemplateResult $existing_template_result, ?TemplateResult $class_template_result,
CodeLocation $code_location, CodeLocation $code_location,
Context $context Context $context
) { ) {
@ -1195,6 +1197,10 @@ class CallAnalyzer
$template_result = null; $template_result = null;
$class_generic_params = $class_template_result
? $class_template_result->generic_params
: [];
if ($function_storage) { if ($function_storage) {
$template_types = self::getTemplateTypesForFunction( $template_types = self::getTemplateTypesForFunction(
$function_storage, $function_storage,
@ -1203,7 +1209,7 @@ class CallAnalyzer
); );
if ($template_types) { if ($template_types) {
$template_result = $existing_template_result; $template_result = $class_template_result;
if (!$template_result) { if (!$template_result) {
$template_result = new TemplateResult($template_types, []); $template_result = new TemplateResult($template_types, []);
@ -1228,23 +1234,20 @@ class CallAnalyzer
$template_result, $template_result,
$codebase, $codebase,
$arg->value->inferredType, $arg->value->inferredType,
$context->self ?: '',
false false
); );
if (!$existing_template_result) { if (!$class_template_result) {
$template_result->generic_params = []; $template_result->generic_params = [];
} }
} }
} }
} }
$existing_generic_params = $existing_template_result foreach ($class_generic_params as $template_name => $type_map) {
? $existing_template_result->generic_params
: [];
foreach ($existing_generic_params as $template_name => $type_map) {
foreach ($type_map as $class => $type) { foreach ($type_map as $class => $type) {
$existing_generic_params[$template_name][$class][0] = clone $type[0]; $class_generic_params[$template_name][$class][0] = clone $type[0];
} }
} }
@ -1305,7 +1308,7 @@ class CallAnalyzer
$argument_offset, $argument_offset,
$arg, $arg,
$context, $context,
$existing_generic_params, $class_generic_params,
$template_result, $template_result,
$function_storage ? $function_storage->pure : false, $function_storage ? $function_storage->pure : false,
$in_call_map $in_call_map
@ -1416,6 +1419,7 @@ class CallAnalyzer
$template_result, $template_result,
$codebase, $codebase,
clone $param->default_type, clone $param->default_type,
null,
true true
); );
} }
@ -1424,7 +1428,7 @@ class CallAnalyzer
} }
/** /**
* @param array<string, array<string, array{Type\Union, 1?:int}>> $existing_generic_params * @param array<string, array<string, array{Type\Union, 1?:int}>> $class_generic_params
* @return false|null * @return false|null
*/ */
private static function checkFunctionLikeArgumentMatches( private static function checkFunctionLikeArgumentMatches(
@ -1437,7 +1441,7 @@ class CallAnalyzer
int $argument_offset, int $argument_offset,
PhpParser\Node\Arg $arg, PhpParser\Node\Arg $arg,
Context $context, Context $context,
array $existing_generic_params, array $class_generic_params,
?TemplateResult $template_result, ?TemplateResult $template_result,
bool $function_is_pure, bool $function_is_pure,
bool $in_call_map bool $in_call_map
@ -1509,7 +1513,7 @@ class CallAnalyzer
$argument_offset, $argument_offset,
$arg, $arg,
$context, $context,
$existing_generic_params, $class_generic_params,
$template_result, $template_result,
$function_is_pure, $function_is_pure,
$in_call_map $in_call_map
@ -1611,7 +1615,8 @@ class CallAnalyzer
$codebase, $codebase,
isset($arg->value->inferredType) isset($arg->value->inferredType)
? $arg->value->inferredType ? $arg->value->inferredType
: null : null,
null
); );
if ($template_result->generic_params) { if ($template_result->generic_params) {
@ -1648,7 +1653,7 @@ class CallAnalyzer
} }
/** /**
* @param array<string, array<string, array{Type\Union, 1?:int}>> $existing_generic_params * @param array<string, array<string, array{Type\Union, 1?:int}>> $class_generic_params
* @param array<string, array<string, array{Type\Union, 1?:int}>> $generic_params * @param array<string, array<string, array{Type\Union, 1?:int}>> $generic_params
* @param array<string, array<string, array{Type\Union}>> $template_types * @param array<string, array<string, array{Type\Union}>> $template_types
* @return false|null * @return false|null
@ -1665,7 +1670,7 @@ class CallAnalyzer
int $argument_offset, int $argument_offset,
PhpParser\Node\Arg $arg, PhpParser\Node\Arg $arg,
Context $context, Context $context,
?array $existing_generic_params, ?array $class_generic_params,
?TemplateResult $template_result, ?TemplateResult $template_result,
bool $function_is_pure, bool $function_is_pure,
bool $in_call_map bool $in_call_map
@ -1680,23 +1685,25 @@ class CallAnalyzer
$param_type = clone $function_param->type; $param_type = clone $function_param->type;
} }
if ($existing_generic_params) { if ($class_generic_params) {
$empty_generic_params = []; $empty_generic_params = [];
$empty_template_result = new TemplateResult($existing_generic_params, $empty_generic_params); $empty_template_result = new TemplateResult($class_generic_params, $empty_generic_params);
$param_type = UnionTemplateHandler::replaceTemplateTypesWithStandins( $param_type = UnionTemplateHandler::replaceTemplateTypesWithStandins(
$param_type, $param_type,
$empty_template_result, $empty_template_result,
$codebase, $codebase,
$arg->value->inferredType $arg->value->inferredType,
$context->self ?: ''
); );
$arg_type = UnionTemplateHandler::replaceTemplateTypesWithStandins( $arg_type = UnionTemplateHandler::replaceTemplateTypesWithStandins(
$arg_type, $arg_type,
$empty_template_result, $empty_template_result,
$codebase, $codebase,
$arg->value->inferredType $arg->value->inferredType,
$context->self ?: ''
); );
} }
@ -1727,7 +1734,8 @@ class CallAnalyzer
$param_type, $param_type,
$template_result, $template_result,
$codebase, $codebase,
$arg_type_param $arg_type_param,
$context->self ?: ''
); );
} }

View File

@ -22,6 +22,7 @@ class UnionTemplateHandler
TemplateResult $template_result, TemplateResult $template_result,
?Codebase $codebase, ?Codebase $codebase,
?Union $input_type, ?Union $input_type,
?string $calling_class = null,
bool $replace = true, bool $replace = true,
bool $add_upper_bound = false, bool $add_upper_bound = false,
int $depth = 0 int $depth = 0
@ -39,6 +40,7 @@ class UnionTemplateHandler
$template_result, $template_result,
$codebase, $codebase,
$input_type, $input_type,
$calling_class,
$replace, $replace,
$add_upper_bound, $add_upper_bound,
$depth, $depth,
@ -77,6 +79,7 @@ class UnionTemplateHandler
TemplateResult $template_result, TemplateResult $template_result,
?Codebase $codebase, ?Codebase $codebase,
?Union $input_type, ?Union $input_type,
?string $calling_class,
bool $replace, bool $replace,
bool $add_upper_bound, bool $add_upper_bound,
int $depth, int $depth,
@ -94,6 +97,7 @@ class UnionTemplateHandler
$atomic_type, $atomic_type,
$key, $key,
$input_type, $input_type,
$calling_class,
$template_result, $template_result,
$codebase, $codebase,
$replace, $replace,
@ -334,6 +338,7 @@ class UnionTemplateHandler
Atomic\TTemplateParam $atomic_type, Atomic\TTemplateParam $atomic_type,
string $key, string $key,
?Union $input_type, ?Union $input_type,
?string $calling_class,
TemplateResult $template_result, TemplateResult $template_result,
?Codebase $codebase, ?Codebase $codebase,
bool $replace, bool $replace,
@ -395,8 +400,11 @@ class UnionTemplateHandler
} }
} }
if ($replacement_atomic_type instanceof Atomic\TTemplateParam) { if ($replacement_atomic_type instanceof Atomic\TTemplateParam
&& $replacement_atomic_type->defining_class !== $calling_class
) {
foreach ($replacement_atomic_type->as->getTypes() as $nested_type_atomic) { foreach ($replacement_atomic_type->as->getTypes() as $nested_type_atomic) {
$replacements_found = true;
$atomic_types[] = clone $nested_type_atomic; $atomic_types[] = clone $nested_type_atomic;
} }
} }

View File

@ -223,6 +223,7 @@ trait CallableTrait
$template_result, $template_result,
$codebase, $codebase,
$input_param_type, $input_param_type,
null,
$replace, $replace,
!$add_upper_bound, !$add_upper_bound,
$depth $depth
@ -239,6 +240,7 @@ trait CallableTrait
$template_result, $template_result,
$codebase, $codebase,
$input_type->return_type, $input_type->return_type,
null,
$replace, $replace,
$add_upper_bound $add_upper_bound
); );

View File

@ -193,6 +193,7 @@ trait GenericTrait
$template_result, $template_result,
$codebase, $codebase,
$input_type_param, $input_type_param,
null,
$replace, $replace,
$add_upper_bound, $add_upper_bound,
$depth + 1 $depth + 1

View File

@ -343,6 +343,7 @@ class ObjectLike extends \Psalm\Type\Atomic
$template_result, $template_result,
$codebase, $codebase,
$input_type_param, $input_type_param,
null,
$replace, $replace,
$add_upper_bound, $add_upper_bound,
$depth $depth

View File

@ -153,6 +153,7 @@ class TList extends \Psalm\Type\Atomic
$template_result, $template_result,
$codebase, $codebase,
$input_type_param, $input_type_param,
null,
$replace, $replace,
$add_upper_bound, $add_upper_bound,
$depth + 1 $depth + 1

View File

@ -2365,6 +2365,23 @@ class ClassTemplateTest extends TestCase
function expectsShape($_): void {}', function expectsShape($_): void {}',
'error_message' => 'MixedArgumentTypeCoercion' 'error_message' => 'MixedArgumentTypeCoercion'
], ],
'preventUseWithMoreSpecificParam' => [
'<?php
/** @template T */
abstract class Collection {
/** @param T $elem */
public function add($elem): void {}
}
/**
* @template T
* @param Collection<T> $col
*/
function usesCollection(Collection $col): void {
$col->add(456);
}',
'error_message' => 'InvalidArgument'
],
]; ];
} }
} }