From 08e305cbead8d822e431a707b00baf2d4e62e990 Mon Sep 17 00:00:00 2001 From: Matt Brown Date: Wed, 27 Jan 2021 12:48:32 -0500 Subject: [PATCH] =?UTF-8?q?Fix=20#4010=20=E2=80=93=20don=E2=80=99t=20creat?= =?UTF-8?q?e=20interface=20intersections=20unless=20there=E2=80=99s=20no?= =?UTF-8?q?=20overlap?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Internal/Type/AssertionReconciler.php | 21 ++++-------- .../Type/Comparator/AtomicTypeComparator.php | 15 +++----- .../Type/Comparator/UnionTypeComparator.php | 6 ++-- tests/InterfaceTest.php | 34 +++++++++++++++++++ 4 files changed, 49 insertions(+), 27 deletions(-) diff --git a/src/Psalm/Internal/Type/AssertionReconciler.php b/src/Psalm/Internal/Type/AssertionReconciler.php index c931f9713..adeed8a7e 100644 --- a/src/Psalm/Internal/Type/AssertionReconciler.php +++ b/src/Psalm/Internal/Type/AssertionReconciler.php @@ -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 - ) + && ($new_type_has_interface || $old_type_has_interface) + && !UnionTypeComparator::canExpressionTypesBeIdentical( + $codebase, + $new_type, + $existing_var_type, + false ) - || ( - $old_type_has_interface - && !UnionTypeComparator::isContainedBy( - $codebase, - $new_type, - $existing_var_type - ) - )) ) { $acceptable_atomic_types = []; diff --git a/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php b/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php index f31bc97e0..f64710493 100644 --- a/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php +++ b/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php @@ -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; } } diff --git a/src/Psalm/Internal/Type/Comparator/UnionTypeComparator.php b/src/Psalm/Internal/Type/Comparator/UnionTypeComparator.php index 1474c1188..2120328ac 100644 --- a/src/Psalm/Internal/Type/Comparator/UnionTypeComparator.php +++ b/src/Psalm/Internal/Type/Comparator/UnionTypeComparator.php @@ -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) { diff --git a/tests/InterfaceTest.php b/tests/InterfaceTest.php index d382ce0cc..c9fd1ac1f 100644 --- a/tests/InterfaceTest.php +++ b/tests/InterfaceTest.php @@ -941,6 +941,40 @@ class InterfaceTest extends TestCase }', 'error_message' => 'MissingParamType' ], + 'reconcileAfterClassInstanceof' => [ + 'bar(); + } + + $foo->bar(); + }', + 'error_message' => 'UndefinedInterfaceMethod - src' . \DIRECTORY_SEPARATOR . 'somefile.php:13:31', + ], + 'reconcileAfterInterfaceInstanceof' => [ + 'bar(); + } + + $foo->bar(); + }', + 'error_message' => 'UndefinedInterfaceMethod - src' . \DIRECTORY_SEPARATOR . 'somefile.php:13:31', + ], ]; } }