1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-26 20:34:47 +01:00

Fix #4545 - allow intersections in more places

This commit is contained in:
Matt Brown 2020-11-13 09:43:30 -05:00 committed by Daniil Gentili
parent 63bf00513b
commit 5219932408
Signed by: danog
GPG Key ID: 8C1BE3B34B230CA7
4 changed files with 88 additions and 6 deletions

View File

@ -758,6 +758,12 @@ class Methods
return clone $overridden_storage->return_type;
}
if ($candidate_type->getId() === $overridden_storage->return_type->getId()) {
$self_class = $appearing_fq_class_storage->name;
return clone $candidate_type;
}
$old_contained_by_new = UnionTypeComparator::isContainedBy(
$source_analyzer->getCodebase(),
$candidate_type,
@ -770,7 +776,9 @@ class Methods
$candidate_type
);
if (!$old_contained_by_new && !$new_contained_by_old) {
if ((!$old_contained_by_new && !$new_contained_by_old)
|| ($old_contained_by_new && $new_contained_by_old)
) {
$attempted_intersection = Type::intersectUnionTypes(
$overridden_storage->return_type,
$candidate_type,
@ -847,7 +855,9 @@ class Methods
$candidate_type
);
if (!$old_contained_by_new && !$new_contained_by_old) {
if ((!$old_contained_by_new && !$new_contained_by_old)
|| ($old_contained_by_new && $new_contained_by_old)
) {
$attempted_intersection = Type::intersectUnionTypes(
$candidate_type,
$overridden_return_type,

View File

@ -534,7 +534,21 @@ abstract class Type
if ($type_1_atomic instanceof TNamedObject
&& $type_2_atomic instanceof TNamedObject
) {
if (AtomicTypeComparator::isContainedBy(
if (($type_1_atomic->value === $type_2_atomic->value
&& get_class($type_1_atomic) === TNamedObject::class
&& get_class($type_2_atomic) !== TNamedObject::class)
) {
$combined_type->removeType($t1_key);
$combined_type->addType(clone $type_2_atomic);
$intersection_performed = true;
} elseif (($type_1_atomic->value === $type_2_atomic->value
&& get_class($type_2_atomic) === TNamedObject::class
&& get_class($type_1_atomic) !== TNamedObject::class)
) {
$combined_type->removeType($t2_key);
$combined_type->addType(clone $type_1_atomic);
$intersection_performed = true;
} elseif (AtomicTypeComparator::isContainedBy(
$codebase,
$type_2_atomic,
$type_1_atomic

View File

@ -2764,9 +2764,6 @@ class ClassTemplateExtendsTest extends TestCase
*/
protected $c;
/**
* @param T $p
*/
public function filter($p) : C {
return $this->c->filter($p);
}
@ -4131,6 +4128,55 @@ class ClassTemplateExtendsTest extends TestCase
return $f::of($s);
}'
],
'functor' => [
'<?php
/**
* @template T
*/
interface Functor
{
/**
* @template F
* @param callable(T): F $function
* @return Functor<F>
*/
public function map(callable $function): Functor;
}
/**
* @template T
* @implements Functor<T>
*/
class FakeFunctor implements Functor
{
/**
* @var T
*/
private $value;
/**
* @psalm-param T $value
*/
public function __construct($value)
{
$this->value = $value;
}
public function map(callable $function): Functor
{
return new FakeFunctor($function($this->value));
}
}
/** @return Functor<int> */
function foo(string $s) : Functor {
$foo = new FakeFunctor($s);
$function = function (string $a): int {
return strlen($a);
};
return $foo->map($function);
}'
],
];
}

View File

@ -3093,6 +3093,18 @@ class ClassTemplateTest extends TestCase
public $traversable;
}'
],
'simpleTemplate' => [
'<?php
/** @template T */
interface F {}
/** @param F<mixed> $f */
function takesFMixed(F $f) : void {}
function sendsF(F $f) : void {
takesFMixed($f);
}'
],
];
}