1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-30 04:39:00 +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 if ($new_type_part instanceof TNamedObject
&& (( && ($new_type_has_interface || $old_type_has_interface)
$new_type_has_interface && !UnionTypeComparator::canExpressionTypesBeIdentical(
&& !UnionTypeComparator::isContainedBy(
$codebase,
$existing_var_type,
$new_type
)
)
|| (
$old_type_has_interface
&& !UnionTypeComparator::isContainedBy(
$codebase, $codebase,
$new_type, $new_type,
$existing_var_type $existing_var_type,
false
) )
))
) { ) {
$acceptable_atomic_types = []; $acceptable_atomic_types = [];

View File

@ -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;
} }
} }

View File

@ -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) {

View File

@ -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',
],
]; ];
} }
} }