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:
parent
d7e435c352
commit
a8ed6ba9c4
@ -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
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -728,6 +728,7 @@ class ReturnTypeAnalyzer
|
|||||||
$fleshed_out_return_type,
|
$fleshed_out_return_type,
|
||||||
$template_result,
|
$template_result,
|
||||||
$codebase,
|
$codebase,
|
||||||
|
null,
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -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 ?: ''
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
);
|
);
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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'
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user