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:
parent
051169f9f2
commit
08e305cbea
@ -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 = [];
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user