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) { foreach ($type->getAtomicTypes() as $key => $atomic_type) {
if ($atomic_type instanceof Type\Atomic\TTemplateParam) { if ($atomic_type instanceof Type\Atomic\TTemplateParam) {
if ($atomic_type->defining_class === $base_fq_class_name) { $types_to_add = self::getExtendedTemplatedTypes(
if (isset($extends[$base_fq_class_name][$atomic_type->param_name])) { $atomic_type,
$extended_param = $extends[$base_fq_class_name][$atomic_type->param_name]; $extends
);
$type->removeType($key); if ($types_to_add) {
$type = Type::combineUnionTypes( $type->removeType($key);
$type,
$extended_param, foreach ($types_to_add as $extra_added_type) {
$codebase $type->addType($extra_added_type);
);
} }
} }
} }
@ -536,6 +536,37 @@ class Methods
return $type; 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 * @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");
}'
],
]; ];
} }