1
0
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:
Matthew Brown 2019-12-18 13:58:37 +00:00
parent e551b24843
commit 49a3f89526
2 changed files with 156 additions and 5 deletions

View File

@ -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];
} }
} }

View File

@ -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