mirror of
https://github.com/danog/psalm.git
synced 2024-11-26 12:24:49 +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
|
if ($new_type_part instanceof TNamedObject
|
||||||
&& ((
|
&& ($new_type_has_interface || $old_type_has_interface)
|
||||||
$new_type_has_interface
|
&& !UnionTypeComparator::canExpressionTypesBeIdentical(
|
||||||
&& !UnionTypeComparator::isContainedBy(
|
$codebase,
|
||||||
$codebase,
|
$new_type,
|
||||||
$existing_var_type,
|
$existing_var_type,
|
||||||
$new_type
|
false
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|| (
|
|
||||||
$old_type_has_interface
|
|
||||||
&& !UnionTypeComparator::isContainedBy(
|
|
||||||
$codebase,
|
|
||||||
$new_type,
|
|
||||||
$existing_var_type
|
|
||||||
)
|
|
||||||
))
|
|
||||||
) {
|
) {
|
||||||
$acceptable_atomic_types = [];
|
$acceptable_atomic_types = [];
|
||||||
|
|
||||||
|
@ -639,7 +639,8 @@ class AtomicTypeComparator
|
|||||||
public static function canBeIdentical(
|
public static function canBeIdentical(
|
||||||
Codebase $codebase,
|
Codebase $codebase,
|
||||||
Type\Atomic $type1_part,
|
Type\Atomic $type1_part,
|
||||||
Type\Atomic $type2_part
|
Type\Atomic $type2_part,
|
||||||
|
bool $allow_interface_equality = true
|
||||||
) : bool {
|
) : bool {
|
||||||
if ((get_class($type1_part) === TList::class
|
if ((get_class($type1_part) === TList::class
|
||||||
&& $type2_part instanceof Type\Atomic\TNonEmptyList)
|
&& $type2_part instanceof Type\Atomic\TNonEmptyList)
|
||||||
@ -673,11 +674,11 @@ class AtomicTypeComparator
|
|||||||
$first_comparison_result = new TypeComparisonResult();
|
$first_comparison_result = new TypeComparisonResult();
|
||||||
$second_comparison_result = new TypeComparisonResult();
|
$second_comparison_result = new TypeComparisonResult();
|
||||||
|
|
||||||
$either_contains = (AtomicTypeComparator::isContainedBy(
|
return (AtomicTypeComparator::isContainedBy(
|
||||||
$codebase,
|
$codebase,
|
||||||
$type1_part,
|
$type1_part,
|
||||||
$type2_part,
|
$type2_part,
|
||||||
true,
|
$allow_interface_equality,
|
||||||
false,
|
false,
|
||||||
$first_comparison_result
|
$first_comparison_result
|
||||||
)
|
)
|
||||||
@ -686,7 +687,7 @@ class AtomicTypeComparator
|
|||||||
$codebase,
|
$codebase,
|
||||||
$type2_part,
|
$type2_part,
|
||||||
$type1_part,
|
$type1_part,
|
||||||
true,
|
$allow_interface_equality,
|
||||||
false,
|
false,
|
||||||
$second_comparison_result
|
$second_comparison_result
|
||||||
)
|
)
|
||||||
@ -694,11 +695,5 @@ class AtomicTypeComparator
|
|||||||
) || ($first_comparison_result->type_coerced
|
) || ($first_comparison_result->type_coerced
|
||||||
&& $second_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(
|
public static function canExpressionTypesBeIdentical(
|
||||||
Codebase $codebase,
|
Codebase $codebase,
|
||||||
Type\Union $type1,
|
Type\Union $type1,
|
||||||
Type\Union $type2
|
Type\Union $type2,
|
||||||
|
bool $allow_interface_equality = true
|
||||||
): bool {
|
): bool {
|
||||||
if ($type1->hasMixed() || $type2->hasMixed()) {
|
if ($type1->hasMixed() || $type2->hasMixed()) {
|
||||||
return true;
|
return true;
|
||||||
@ -367,7 +368,8 @@ class UnionTypeComparator
|
|||||||
$either_contains = AtomicTypeComparator::canBeIdentical(
|
$either_contains = AtomicTypeComparator::canBeIdentical(
|
||||||
$codebase,
|
$codebase,
|
||||||
$type1_part,
|
$type1_part,
|
||||||
$type2_part
|
$type2_part,
|
||||||
|
$allow_interface_equality
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($either_contains) {
|
if ($either_contains) {
|
||||||
|
@ -941,6 +941,40 @@ class InterfaceTest extends TestCase
|
|||||||
}',
|
}',
|
||||||
'error_message' => 'MissingParamType'
|
'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