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

Fix #2974 - interpret interfaces a little better

This commit is contained in:
Matthew Brown 2020-03-15 15:49:13 -04:00
parent b6380ae595
commit 532ce576cc
2 changed files with 73 additions and 9 deletions

View File

@ -462,16 +462,16 @@ class Methods
foreach ($type->getAtomicTypes() as $key => $atomic_type) {
if ($atomic_type instanceof Type\Atomic\TTemplateParam) {
if ($atomic_type->defining_class === $base_fq_class_name) {
if (isset($extends[$base_fq_class_name][$atomic_type->param_name])) {
$extended_param = $extends[$base_fq_class_name][$atomic_type->param_name];
$types_to_add = self::getExtendedTemplatedTypes(
$atomic_type,
$extends
);
$type->removeType($key);
$type = Type::combineUnionTypes(
$type,
$extended_param,
$codebase
);
if ($types_to_add) {
$type->removeType($key);
foreach ($types_to_add as $extra_added_type) {
$type->addType($extra_added_type);
}
}
}
@ -536,6 +536,37 @@ class Methods
return $type;
}
/**
* @param array<string, array<int|string, Type\Union>> $extends
* @return list<Type\Atomic>
*/
private static function getExtendedTemplatedTypes(
Type\Atomic\TTemplateParam $atomic_type,
array $extends
) : array {
$extra_added_types = [];
if (isset($extends[$atomic_type->defining_class][$atomic_type->param_name])) {
$extended_param = clone $extends[$atomic_type->defining_class][$atomic_type->param_name];
foreach ($extended_param->getAtomicTypes() as $extended_atomic_type) {
if ($extended_atomic_type instanceof Type\Atomic\TTemplateParam) {
$extra_added_types = \array_merge(
$extra_added_types,
self::getExtendedTemplatedTypes(
$extended_atomic_type,
$extends
)
);
} else {
$extra_added_types[] = $extended_atomic_type;
}
}
}
return $extra_added_types;
}
/**
* @return bool
*/

View File

@ -3198,6 +3198,39 @@ class ClassTemplateExtendsTest extends TestCase
}
}'
],
'annotationDefinedInInheritedInterface' => [
'<?php
/**
* @template T1
*/
interface X {
/**
* @param T1 $x
* @return T1
*/
public function boo($x);
}
/**
* @template T2
* @extends X<T2>
*/
interface Y extends X {}
/**
* @template T3
* @implements Y<T3>
*/
class A implements Y {
public function boo($x) {
return $x;
}
}
function foo(A $a) : void {
$a->boo("boo");
}'
],
];
}