1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-29 20:28:59 +01:00

Fix #4010 – don’t create interface intersections unless there’s no overlap

This commit is contained in:
Matt Brown 2021-01-27 12:48:32 -05:00 committed by Daniil Gentili
parent 051169f9f2
commit 08e305cbea
Signed by: danog
GPG Key ID: 8C1BE3B34B230CA7
4 changed files with 49 additions and 27 deletions

View File

@ -390,22 +390,13 @@ class AssertionReconciler extends \Psalm\Type\Reconciler
}
if ($new_type_part instanceof TNamedObject
&& ((
$new_type_has_interface
&& !UnionTypeComparator::isContainedBy(
$codebase,
$existing_var_type,
$new_type
)
)
|| (
$old_type_has_interface
&& !UnionTypeComparator::isContainedBy(
&& ($new_type_has_interface || $old_type_has_interface)
&& !UnionTypeComparator::canExpressionTypesBeIdentical(
$codebase,
$new_type,
$existing_var_type
$existing_var_type,
false
)
))
) {
$acceptable_atomic_types = [];

View File

@ -639,7 +639,8 @@ class AtomicTypeComparator
public static function canBeIdentical(
Codebase $codebase,
Type\Atomic $type1_part,
Type\Atomic $type2_part
Type\Atomic $type2_part,
bool $allow_interface_equality = true
) : bool {
if ((get_class($type1_part) === TList::class
&& $type2_part instanceof Type\Atomic\TNonEmptyList)
@ -673,11 +674,11 @@ class AtomicTypeComparator
$first_comparison_result = new TypeComparisonResult();
$second_comparison_result = new TypeComparisonResult();
$either_contains = (AtomicTypeComparator::isContainedBy(
return (AtomicTypeComparator::isContainedBy(
$codebase,
$type1_part,
$type2_part,
true,
$allow_interface_equality,
false,
$first_comparison_result
)
@ -686,7 +687,7 @@ class AtomicTypeComparator
$codebase,
$type2_part,
$type1_part,
true,
$allow_interface_equality,
false,
$second_comparison_result
)
@ -694,11 +695,5 @@ class AtomicTypeComparator
) || ($first_comparison_result->type_coerced
&& $second_comparison_result->type_coerced
);
if ($either_contains) {
return true;
}
return false;
}
}

View File

@ -352,7 +352,8 @@ class UnionTypeComparator
public static function canExpressionTypesBeIdentical(
Codebase $codebase,
Type\Union $type1,
Type\Union $type2
Type\Union $type2,
bool $allow_interface_equality = true
): bool {
if ($type1->hasMixed() || $type2->hasMixed()) {
return true;
@ -367,7 +368,8 @@ class UnionTypeComparator
$either_contains = AtomicTypeComparator::canBeIdentical(
$codebase,
$type1_part,
$type2_part
$type2_part,
$allow_interface_equality
);
if ($either_contains) {

View File

@ -941,6 +941,40 @@ class InterfaceTest extends TestCase
}',
'error_message' => 'MissingParamType'
],
'reconcileAfterClassInstanceof' => [
'<?php
interface Base {}
class E implements Base {
public function bar() : void {}
}
function foobar(Base $foo) : void {
if ($foo instanceof E) {
$foo->bar();
}
$foo->bar();
}',
'error_message' => 'UndefinedInterfaceMethod - src' . \DIRECTORY_SEPARATOR . 'somefile.php:13:31',
],
'reconcileAfterInterfaceInstanceof' => [
'<?php
interface Base {}
interface E extends Base {
public function bar() : void;
}
function foobar(Base $foo) : void {
if ($foo instanceof E) {
$foo->bar();
}
$foo->bar();
}',
'error_message' => 'UndefinedInterfaceMethod - src' . \DIRECTORY_SEPARATOR . 'somefile.php:13:31',
],
];
}
}