mirror of
https://github.com/danog/psalm.git
synced 2024-11-27 04:45:20 +01:00
Fix #2478 - process trait template inheritance sensibly
This commit is contained in:
parent
e551b24843
commit
49a3f89526
@ -665,6 +665,57 @@ class MethodAnalyzer extends FunctionLikeAnalyzer
|
|||||||
);
|
);
|
||||||
|
|
||||||
$guide_class_name = $guide_classlike_storage->name;
|
$guide_class_name = $guide_classlike_storage->name;
|
||||||
|
$implementer_class_name = $implementer_classlike_storage->name;
|
||||||
|
|
||||||
|
$implementer_called_class_storage = $implementer_classlike_storage;
|
||||||
|
|
||||||
|
if ($implementer_called_class_name !== $implementer_class_name) {
|
||||||
|
$implementer_called_class_storage = $codebase->classlike_storage_provider->get(
|
||||||
|
$implementer_called_class_name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($implementer_called_class_storage !== $implementer_classlike_storage
|
||||||
|
&& isset($implementer_called_class_storage->template_type_extends[$implementer_class_name])
|
||||||
|
) {
|
||||||
|
$map = $implementer_called_class_storage->template_type_extends[$implementer_class_name];
|
||||||
|
|
||||||
|
$template_types = [];
|
||||||
|
|
||||||
|
foreach ($map as $key => $type) {
|
||||||
|
if (is_string($key)) {
|
||||||
|
$template_types[$key][$implementer_class_name] = [$type];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$template_result = new \Psalm\Internal\Type\TemplateResult($template_types, []);
|
||||||
|
|
||||||
|
$implementer_method_storage_return_type->replaceTemplateTypesWithArgTypes(
|
||||||
|
$template_result->template_types,
|
||||||
|
$codebase
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($implementer_called_class_storage !== $implementer_classlike_storage
|
||||||
|
&& isset($implementer_called_class_storage->template_type_extends[$guide_class_name])
|
||||||
|
) {
|
||||||
|
$map = $implementer_called_class_storage->template_type_extends[$guide_class_name];
|
||||||
|
|
||||||
|
$template_types = [];
|
||||||
|
|
||||||
|
foreach ($map as $key => $type) {
|
||||||
|
if (is_string($key)) {
|
||||||
|
$template_types[$key][$guide_class_name] = [$type];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$template_result = new \Psalm\Internal\Type\TemplateResult($template_types, []);
|
||||||
|
|
||||||
|
$guide_method_storage_return_type->replaceTemplateTypesWithArgTypes(
|
||||||
|
$template_result->template_types,
|
||||||
|
$codebase
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($implementer_classlike_storage->template_type_extends[$guide_class_name])) {
|
if (isset($implementer_classlike_storage->template_type_extends[$guide_class_name])) {
|
||||||
$map = $implementer_classlike_storage->template_type_extends[$guide_class_name];
|
$map = $implementer_classlike_storage->template_type_extends[$guide_class_name];
|
||||||
@ -673,7 +724,7 @@ class MethodAnalyzer extends FunctionLikeAnalyzer
|
|||||||
|
|
||||||
foreach ($map as $key => $type) {
|
foreach ($map as $key => $type) {
|
||||||
if (is_string($key)) {
|
if (is_string($key)) {
|
||||||
$template_types[$key][$guide_classlike_storage->name] = [$type];
|
$template_types[$key][$guide_class_name] = [$type];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -747,7 +798,7 @@ class MethodAnalyzer extends FunctionLikeAnalyzer
|
|||||||
if ($union_comparison_results->type_coerced) {
|
if ($union_comparison_results->type_coerced) {
|
||||||
if (IssueBuffer::accepts(
|
if (IssueBuffer::accepts(
|
||||||
new LessSpecificImplementedReturnType(
|
new LessSpecificImplementedReturnType(
|
||||||
'The return type \'' . $guide_method_storage_return_type->getId()
|
'The inherited return type \'' . $guide_method_storage_return_type->getId()
|
||||||
. '\' for ' . $cased_guide_method_id . ' is more specific than the implemented '
|
. '\' for ' . $cased_guide_method_id . ' is more specific than the implemented '
|
||||||
. 'return type for ' . $implementer_declaring_method_id . ' \''
|
. 'return type for ' . $implementer_declaring_method_id . ' \''
|
||||||
. $implementer_method_storage_return_type->getId() . '\'',
|
. $implementer_method_storage_return_type->getId() . '\'',
|
||||||
@ -761,7 +812,7 @@ class MethodAnalyzer extends FunctionLikeAnalyzer
|
|||||||
} else {
|
} else {
|
||||||
if (IssueBuffer::accepts(
|
if (IssueBuffer::accepts(
|
||||||
new ImplementedReturnTypeMismatch(
|
new ImplementedReturnTypeMismatch(
|
||||||
'The return type \'' . $guide_method_storage_return_type->getId()
|
'The inherited return type \'' . $guide_method_storage_return_type->getId()
|
||||||
. '\' for ' . $cased_guide_method_id . ' is different to the implemented '
|
. '\' for ' . $cased_guide_method_id . ' is different to the implemented '
|
||||||
. 'return type for ' . $implementer_declaring_method_id . ' \''
|
. 'return type for ' . $implementer_declaring_method_id . ' \''
|
||||||
. $implementer_method_storage_return_type->getId() . '\'',
|
. $implementer_method_storage_return_type->getId() . '\'',
|
||||||
@ -937,6 +988,57 @@ class MethodAnalyzer extends FunctionLikeAnalyzer
|
|||||||
);
|
);
|
||||||
|
|
||||||
$guide_class_name = $guide_classlike_storage->name;
|
$guide_class_name = $guide_classlike_storage->name;
|
||||||
|
$implementer_class_name = $implementer_classlike_storage->name;
|
||||||
|
|
||||||
|
$implementer_called_class_storage = $implementer_classlike_storage;
|
||||||
|
|
||||||
|
if ($implementer_called_class_name !== $implementer_class_name) {
|
||||||
|
$implementer_called_class_storage = $codebase->classlike_storage_provider->get(
|
||||||
|
$implementer_called_class_name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($implementer_called_class_storage !== $implementer_classlike_storage
|
||||||
|
&& isset($implementer_called_class_storage->template_type_extends[$implementer_class_name])
|
||||||
|
) {
|
||||||
|
$map = $implementer_called_class_storage->template_type_extends[$implementer_class_name];
|
||||||
|
|
||||||
|
$template_types = [];
|
||||||
|
|
||||||
|
foreach ($map as $key => $type) {
|
||||||
|
if (is_string($key)) {
|
||||||
|
$template_types[$key][$implementer_class_name] = [$type];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$template_result = new \Psalm\Internal\Type\TemplateResult($template_types, []);
|
||||||
|
|
||||||
|
$implementer_method_storage_param_type->replaceTemplateTypesWithArgTypes(
|
||||||
|
$template_result->template_types,
|
||||||
|
$codebase
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($implementer_called_class_storage !== $implementer_classlike_storage
|
||||||
|
&& isset($implementer_called_class_storage->template_type_extends[$guide_class_name])
|
||||||
|
) {
|
||||||
|
$map = $implementer_called_class_storage->template_type_extends[$guide_class_name];
|
||||||
|
|
||||||
|
$template_types = [];
|
||||||
|
|
||||||
|
foreach ($map as $key => $type) {
|
||||||
|
if (is_string($key)) {
|
||||||
|
$template_types[$key][$guide_class_name] = [$type];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$template_result = new \Psalm\Internal\Type\TemplateResult($template_types, []);
|
||||||
|
|
||||||
|
$guide_method_storage_param_type->replaceTemplateTypesWithArgTypes(
|
||||||
|
$template_result->template_types,
|
||||||
|
$codebase
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($implementer_classlike_storage->template_type_extends[$guide_class_name])) {
|
if (isset($implementer_classlike_storage->template_type_extends[$guide_class_name])) {
|
||||||
$map = $implementer_classlike_storage->template_type_extends[$guide_class_name];
|
$map = $implementer_classlike_storage->template_type_extends[$guide_class_name];
|
||||||
@ -945,7 +1047,7 @@ class MethodAnalyzer extends FunctionLikeAnalyzer
|
|||||||
|
|
||||||
foreach ($map as $key => $type) {
|
foreach ($map as $key => $type) {
|
||||||
if (is_string($key)) {
|
if (is_string($key)) {
|
||||||
$template_types[$key][$guide_classlike_storage->name] = [$type, 0];
|
$template_types[$key][$guide_class_name] = [$type, 0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2616,6 +2616,55 @@ class ClassTemplateExtendsTest extends TestCase
|
|||||||
}
|
}
|
||||||
}'
|
}'
|
||||||
],
|
],
|
||||||
|
'useTraitReturnType' => [
|
||||||
|
'<?php
|
||||||
|
/**
|
||||||
|
* @template TValue
|
||||||
|
* @template TNormalizedValue
|
||||||
|
*/
|
||||||
|
interface Normalizer
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param TValue $v
|
||||||
|
* @return TNormalizedValue
|
||||||
|
*/
|
||||||
|
function normalize($v);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template TTraitValue
|
||||||
|
* @template TTraitNormalizedValue
|
||||||
|
*/
|
||||||
|
trait NormalizerTrait
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param TTraitValue $v
|
||||||
|
* @return TTraitNormalizedValue
|
||||||
|
*/
|
||||||
|
function normalize($v)
|
||||||
|
{
|
||||||
|
return $this->doNormalize($v);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param TTraitValue $v
|
||||||
|
* @return TTraitNormalizedValue
|
||||||
|
*/
|
||||||
|
abstract protected function doNormalize($v);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @implements Normalizer<string, string> */
|
||||||
|
class StringNormalizer implements Normalizer
|
||||||
|
{
|
||||||
|
/** @use NormalizerTrait<string, string> */
|
||||||
|
use NormalizerTrait;
|
||||||
|
|
||||||
|
protected function doNormalize($v): string
|
||||||
|
{
|
||||||
|
return trim($v);
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2661,7 +2710,7 @@ class ClassTemplateExtendsTest extends TestCase
|
|||||||
return new Foo();
|
return new Foo();
|
||||||
}
|
}
|
||||||
}',
|
}',
|
||||||
'error_message' => 'ImplementedReturnTypeMismatch - src' . DIRECTORY_SEPARATOR . 'somefile.php:29:36 - The return type \'A\Bar\' for',
|
'error_message' => 'ImplementedReturnTypeMismatch - src' . DIRECTORY_SEPARATOR . 'somefile.php:29:36 - The inherited return type \'A\Bar\' for',
|
||||||
],
|
],
|
||||||
'extendTemplateAndDoesNotOverrideWithWrongArg' => [
|
'extendTemplateAndDoesNotOverrideWithWrongArg' => [
|
||||||
'<?php
|
'<?php
|
||||||
|
Loading…
Reference in New Issue
Block a user