mirror of
https://github.com/danog/psalm.git
synced 2024-11-26 20:34:47 +01:00
Fix #2308 - prevent specialisation from bound params
This commit is contained in:
parent
d7e435c352
commit
a8ed6ba9c4
@ -760,6 +760,7 @@ class ClassAnalyzer extends ClassLikeAnalyzer
|
||||
$fleshed_out_type,
|
||||
$template_result,
|
||||
$codebase,
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
@ -1719,6 +1720,7 @@ class ClassAnalyzer extends ClassLikeAnalyzer
|
||||
$return_type,
|
||||
$template_result,
|
||||
$codebase,
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
@ -728,6 +728,7 @@ class ReturnTypeAnalyzer
|
||||
$fleshed_out_return_type,
|
||||
$template_result,
|
||||
$codebase,
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
@ -249,7 +249,7 @@ class CallAnalyzer
|
||||
protected static function checkMethodArgs(
|
||||
$method_id,
|
||||
array $args,
|
||||
?TemplateResult $template_result,
|
||||
?TemplateResult $class_template_result,
|
||||
Context $context,
|
||||
CodeLocation $code_location,
|
||||
StatementsAnalyzer $statements_analyzer
|
||||
@ -266,7 +266,7 @@ class CallAnalyzer
|
||||
$method_params,
|
||||
$method_id,
|
||||
$context,
|
||||
$template_result
|
||||
$class_template_result
|
||||
) === false) {
|
||||
return false;
|
||||
}
|
||||
@ -325,7 +325,7 @@ class CallAnalyzer
|
||||
$method_params,
|
||||
$method_storage,
|
||||
$class_storage,
|
||||
$template_result,
|
||||
$class_template_result,
|
||||
$code_location,
|
||||
$context
|
||||
) === false) {
|
||||
@ -475,6 +475,7 @@ class CallAnalyzer
|
||||
$replaced_type,
|
||||
$replace_template_result,
|
||||
$codebase,
|
||||
null,
|
||||
null
|
||||
);
|
||||
|
||||
@ -564,7 +565,8 @@ class CallAnalyzer
|
||||
$codebase,
|
||||
isset($arg->value->inferredType)
|
||||
? $arg->value->inferredType
|
||||
: null
|
||||
: null,
|
||||
null
|
||||
);
|
||||
|
||||
if ($replace_template_result->generic_params) {
|
||||
@ -1124,7 +1126,7 @@ class CallAnalyzer
|
||||
array $function_params,
|
||||
$function_storage,
|
||||
$class_storage,
|
||||
?TemplateResult $existing_template_result,
|
||||
?TemplateResult $class_template_result,
|
||||
CodeLocation $code_location,
|
||||
Context $context
|
||||
) {
|
||||
@ -1195,6 +1197,10 @@ class CallAnalyzer
|
||||
|
||||
$template_result = null;
|
||||
|
||||
$class_generic_params = $class_template_result
|
||||
? $class_template_result->generic_params
|
||||
: [];
|
||||
|
||||
if ($function_storage) {
|
||||
$template_types = self::getTemplateTypesForFunction(
|
||||
$function_storage,
|
||||
@ -1203,7 +1209,7 @@ class CallAnalyzer
|
||||
);
|
||||
|
||||
if ($template_types) {
|
||||
$template_result = $existing_template_result;
|
||||
$template_result = $class_template_result;
|
||||
|
||||
if (!$template_result) {
|
||||
$template_result = new TemplateResult($template_types, []);
|
||||
@ -1228,23 +1234,20 @@ class CallAnalyzer
|
||||
$template_result,
|
||||
$codebase,
|
||||
$arg->value->inferredType,
|
||||
$context->self ?: '',
|
||||
false
|
||||
);
|
||||
|
||||
if (!$existing_template_result) {
|
||||
if (!$class_template_result) {
|
||||
$template_result->generic_params = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$existing_generic_params = $existing_template_result
|
||||
? $existing_template_result->generic_params
|
||||
: [];
|
||||
|
||||
foreach ($existing_generic_params as $template_name => $type_map) {
|
||||
foreach ($class_generic_params as $template_name => $type_map) {
|
||||
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,
|
||||
$arg,
|
||||
$context,
|
||||
$existing_generic_params,
|
||||
$class_generic_params,
|
||||
$template_result,
|
||||
$function_storage ? $function_storage->pure : false,
|
||||
$in_call_map
|
||||
@ -1416,6 +1419,7 @@ class CallAnalyzer
|
||||
$template_result,
|
||||
$codebase,
|
||||
clone $param->default_type,
|
||||
null,
|
||||
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
|
||||
*/
|
||||
private static function checkFunctionLikeArgumentMatches(
|
||||
@ -1437,7 +1441,7 @@ class CallAnalyzer
|
||||
int $argument_offset,
|
||||
PhpParser\Node\Arg $arg,
|
||||
Context $context,
|
||||
array $existing_generic_params,
|
||||
array $class_generic_params,
|
||||
?TemplateResult $template_result,
|
||||
bool $function_is_pure,
|
||||
bool $in_call_map
|
||||
@ -1509,7 +1513,7 @@ class CallAnalyzer
|
||||
$argument_offset,
|
||||
$arg,
|
||||
$context,
|
||||
$existing_generic_params,
|
||||
$class_generic_params,
|
||||
$template_result,
|
||||
$function_is_pure,
|
||||
$in_call_map
|
||||
@ -1611,7 +1615,8 @@ class CallAnalyzer
|
||||
$codebase,
|
||||
isset($arg->value->inferredType)
|
||||
? $arg->value->inferredType
|
||||
: null
|
||||
: null,
|
||||
null
|
||||
);
|
||||
|
||||
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}>> $template_types
|
||||
* @return false|null
|
||||
@ -1665,7 +1670,7 @@ class CallAnalyzer
|
||||
int $argument_offset,
|
||||
PhpParser\Node\Arg $arg,
|
||||
Context $context,
|
||||
?array $existing_generic_params,
|
||||
?array $class_generic_params,
|
||||
?TemplateResult $template_result,
|
||||
bool $function_is_pure,
|
||||
bool $in_call_map
|
||||
@ -1680,23 +1685,25 @@ class CallAnalyzer
|
||||
$param_type = clone $function_param->type;
|
||||
}
|
||||
|
||||
if ($existing_generic_params) {
|
||||
if ($class_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,
|
||||
$empty_template_result,
|
||||
$codebase,
|
||||
$arg->value->inferredType
|
||||
$arg->value->inferredType,
|
||||
$context->self ?: ''
|
||||
);
|
||||
|
||||
$arg_type = UnionTemplateHandler::replaceTemplateTypesWithStandins(
|
||||
$arg_type,
|
||||
$empty_template_result,
|
||||
$codebase,
|
||||
$arg->value->inferredType
|
||||
$arg->value->inferredType,
|
||||
$context->self ?: ''
|
||||
);
|
||||
}
|
||||
|
||||
@ -1727,7 +1734,8 @@ class CallAnalyzer
|
||||
$param_type,
|
||||
$template_result,
|
||||
$codebase,
|
||||
$arg_type_param
|
||||
$arg_type_param,
|
||||
$context->self ?: ''
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@ class UnionTemplateHandler
|
||||
TemplateResult $template_result,
|
||||
?Codebase $codebase,
|
||||
?Union $input_type,
|
||||
?string $calling_class = null,
|
||||
bool $replace = true,
|
||||
bool $add_upper_bound = false,
|
||||
int $depth = 0
|
||||
@ -39,6 +40,7 @@ class UnionTemplateHandler
|
||||
$template_result,
|
||||
$codebase,
|
||||
$input_type,
|
||||
$calling_class,
|
||||
$replace,
|
||||
$add_upper_bound,
|
||||
$depth,
|
||||
@ -77,6 +79,7 @@ class UnionTemplateHandler
|
||||
TemplateResult $template_result,
|
||||
?Codebase $codebase,
|
||||
?Union $input_type,
|
||||
?string $calling_class,
|
||||
bool $replace,
|
||||
bool $add_upper_bound,
|
||||
int $depth,
|
||||
@ -94,6 +97,7 @@ class UnionTemplateHandler
|
||||
$atomic_type,
|
||||
$key,
|
||||
$input_type,
|
||||
$calling_class,
|
||||
$template_result,
|
||||
$codebase,
|
||||
$replace,
|
||||
@ -334,6 +338,7 @@ class UnionTemplateHandler
|
||||
Atomic\TTemplateParam $atomic_type,
|
||||
string $key,
|
||||
?Union $input_type,
|
||||
?string $calling_class,
|
||||
TemplateResult $template_result,
|
||||
?Codebase $codebase,
|
||||
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) {
|
||||
$replacements_found = true;
|
||||
$atomic_types[] = clone $nested_type_atomic;
|
||||
}
|
||||
}
|
||||
|
@ -223,6 +223,7 @@ trait CallableTrait
|
||||
$template_result,
|
||||
$codebase,
|
||||
$input_param_type,
|
||||
null,
|
||||
$replace,
|
||||
!$add_upper_bound,
|
||||
$depth
|
||||
@ -239,6 +240,7 @@ trait CallableTrait
|
||||
$template_result,
|
||||
$codebase,
|
||||
$input_type->return_type,
|
||||
null,
|
||||
$replace,
|
||||
$add_upper_bound
|
||||
);
|
||||
|
@ -193,6 +193,7 @@ trait GenericTrait
|
||||
$template_result,
|
||||
$codebase,
|
||||
$input_type_param,
|
||||
null,
|
||||
$replace,
|
||||
$add_upper_bound,
|
||||
$depth + 1
|
||||
|
@ -343,6 +343,7 @@ class ObjectLike extends \Psalm\Type\Atomic
|
||||
$template_result,
|
||||
$codebase,
|
||||
$input_type_param,
|
||||
null,
|
||||
$replace,
|
||||
$add_upper_bound,
|
||||
$depth
|
||||
|
@ -153,6 +153,7 @@ class TList extends \Psalm\Type\Atomic
|
||||
$template_result,
|
||||
$codebase,
|
||||
$input_type_param,
|
||||
null,
|
||||
$replace,
|
||||
$add_upper_bound,
|
||||
$depth + 1
|
||||
|
@ -2365,6 +2365,23 @@ class ClassTemplateTest extends TestCase
|
||||
function expectsShape($_): void {}',
|
||||
'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'
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user