mirror of
https://github.com/danog/psalm.git
synced 2024-11-27 04:45:20 +01:00
parent
346d475f55
commit
6e1218065d
@ -108,8 +108,7 @@ class UnionTemplateHandler
|
||||
}
|
||||
|
||||
if ($atomic_type instanceof Atomic\TTemplateParam
|
||||
&& ($param_name_key = strpos($key, '&') ? $key : $atomic_type->param_name)
|
||||
&& isset($template_result->template_types[$param_name_key][$atomic_type->defining_class])
|
||||
&& isset($template_result->template_types[$atomic_type->param_name][$atomic_type->defining_class])
|
||||
) {
|
||||
$a = self::handleTemplateParamStandin(
|
||||
$atomic_type,
|
||||
@ -489,6 +488,38 @@ class UnionTemplateHandler
|
||||
$param_name_key = $key;
|
||||
}
|
||||
|
||||
$extra_types = [];
|
||||
|
||||
if ($atomic_type->extra_types) {
|
||||
foreach ($atomic_type->extra_types as $extra_type) {
|
||||
$extra_type = self::replaceTemplateTypesWithStandins(
|
||||
new \Psalm\Type\Union([$extra_type]),
|
||||
$template_result,
|
||||
$codebase,
|
||||
$statements_analyzer,
|
||||
$input_type,
|
||||
$input_arg_offset,
|
||||
$calling_class,
|
||||
$calling_function,
|
||||
$replace,
|
||||
$add_upper_bound,
|
||||
$depth + 1
|
||||
);
|
||||
|
||||
if ($extra_type->isSingle()) {
|
||||
$extra_type = array_values($extra_type->getAtomicTypes())[0];
|
||||
|
||||
if ($extra_type instanceof Atomic\TNamedObject
|
||||
|| $extra_type instanceof Atomic\TTemplateParam
|
||||
|| $extra_type instanceof Atomic\TIterable
|
||||
|| $extra_type instanceof Atomic\TObjectWithProperties
|
||||
) {
|
||||
$extra_types[] = $extra_type;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($replace) {
|
||||
$atomic_types = [];
|
||||
|
||||
@ -675,6 +706,10 @@ class UnionTemplateHandler
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($atomic_types as $atomic_type) {
|
||||
$atomic_type->extra_types = $extra_types;
|
||||
}
|
||||
|
||||
return $atomic_types;
|
||||
}
|
||||
|
||||
|
@ -4388,6 +4388,86 @@ class ClassTemplateExtendsTest extends TestCase
|
||||
abstract public function foo($param): void;
|
||||
}'
|
||||
],
|
||||
'extendAndImplementedTemplatedProperty' => [
|
||||
'<?php
|
||||
interface Mock {}
|
||||
abstract class A {}
|
||||
class B extends A {}
|
||||
class BMock extends B {}
|
||||
|
||||
/** @template T of A */
|
||||
abstract class ATestCase {
|
||||
/** @var T */
|
||||
protected Mock $foo;
|
||||
|
||||
/** @param T $foo */
|
||||
public function __construct(A $foo) {
|
||||
$this->foo = $foo;
|
||||
}
|
||||
}
|
||||
|
||||
/** @extends ATestCase<B> */
|
||||
class BTestCase extends ATestCase {
|
||||
public function getFoo(): B {
|
||||
return $this->foo;
|
||||
}
|
||||
}
|
||||
|
||||
new BTestCase(new BMock());'
|
||||
],
|
||||
'extendAndImplementedTemplatedIntersectionProperty' => [
|
||||
'<?php
|
||||
interface Mock {
|
||||
function foo():void;
|
||||
}
|
||||
abstract class A {}
|
||||
class B extends A {}
|
||||
|
||||
/** @template T of A */
|
||||
abstract class ATestCase {
|
||||
/** @var T&Mock */
|
||||
protected Mock $obj;
|
||||
|
||||
/** @param T&Mock $obj */
|
||||
public function __construct(Mock $obj) {
|
||||
$this->obj = $obj;
|
||||
}
|
||||
}
|
||||
|
||||
/** @extends ATestCase<B> */
|
||||
class BTestCase extends ATestCase {
|
||||
public function getFoo(): void {
|
||||
$this->obj->foo();
|
||||
}
|
||||
}'
|
||||
],
|
||||
'extendAndImplementedTemplatedIntersectionReceives' => [
|
||||
'<?php
|
||||
interface Mock {
|
||||
function foo():void;
|
||||
}
|
||||
abstract class A {}
|
||||
class B extends A {}
|
||||
class BMock extends B implements Mock {
|
||||
public function foo(): void {}
|
||||
}
|
||||
|
||||
/** @template T of A */
|
||||
abstract class ATestCase {
|
||||
/** @var T&Mock */
|
||||
protected Mock $obj;
|
||||
|
||||
/** @param T&Mock $obj */
|
||||
public function __construct(Mock $obj) {
|
||||
$this->obj = $obj;
|
||||
}
|
||||
}
|
||||
|
||||
/** @extends ATestCase<B> */
|
||||
class BTestCase extends ATestCase {}
|
||||
|
||||
new BTestCase(new BMock());'
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user