From 01e5828ef4dd581732c087c3524073c469173069 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Mon, 17 Jun 2024 17:03:47 +0200 Subject: [PATCH] Solve identical comparison with call-site variance --- .../InitializerExprTypeResolver.php | 9 +++-- src/Type/Enum/EnumCaseObjectType.php | 11 +++++++ ...rictComparisonOfDifferentTypesRuleTest.php | 12 +++++++ .../Rules/Comparison/data/bug-10697.php | 12 +++++++ .../Rules/Comparison/data/bug-11161.php | 33 +++++++++++++++++++ 5 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 tests/PHPStan/Rules/Comparison/data/bug-10697.php create mode 100644 tests/PHPStan/Rules/Comparison/data/bug-11161.php diff --git a/src/Reflection/InitializerExprTypeResolver.php b/src/Reflection/InitializerExprTypeResolver.php index 17d05079dd..3a9523ab6c 100644 --- a/src/Reflection/InitializerExprTypeResolver.php +++ b/src/Reflection/InitializerExprTypeResolver.php @@ -1336,6 +1336,10 @@ public function getShiftRightType(Expr $left, Expr $right, callable $getTypeCall public function resolveIdenticalType(Type $leftType, Type $rightType): BooleanType { + if ($leftType instanceof NeverType || $rightType instanceof NeverType) { + return new ConstantBooleanType(false); + } + if ($leftType instanceof ConstantScalarType && $rightType instanceof ConstantScalarType) { return new ConstantBooleanType($leftType->getValue() === $rightType->getValue()); } @@ -1346,8 +1350,9 @@ public function resolveIdenticalType(Type $leftType, Type $rightType): BooleanTy return new ConstantBooleanType($leftTypeFiniteTypes[0]->equals($rightTypeFiniteType[0])); } - $isSuperset = $leftType->isSuperTypeOf($rightType); - if ($isSuperset->no()) { + $isLeftSupertype = $leftType->isSuperTypeOf($rightType); + $isRightSupertype = $rightType->isSuperTypeOf($leftType); + if ($isLeftSupertype->no() && $isRightSupertype->no()) { return new ConstantBooleanType(false); } diff --git a/src/Type/Enum/EnumCaseObjectType.php b/src/Type/Enum/EnumCaseObjectType.php index c367d70717..42d8f4afc2 100644 --- a/src/Type/Enum/EnumCaseObjectType.php +++ b/src/Type/Enum/EnumCaseObjectType.php @@ -17,6 +17,7 @@ use PHPStan\Type\Constant\ConstantStringType; use PHPStan\Type\GeneralizePrecision; use PHPStan\Type\ObjectType; +use PHPStan\Type\SubtractableType; use PHPStan\Type\Type; use PHPStan\Type\VerbosityLevel; use function sprintf; @@ -79,6 +80,16 @@ public function isSuperTypeOf(Type $type): TrinaryLogic return $type->isSubTypeOf($this); } + if ( + $type instanceof SubtractableType + && $type->getSubtractedType() !== null + ) { + $isSuperType = $type->getSubtractedType()->isSuperTypeOf($this); + if ($isSuperType->yes()) { + return TrinaryLogic::createNo(); + } + } + $parent = new parent($this->getClassName(), $this->getSubtractedType(), $this->getClassReflection()); return $parent->isSuperTypeOf($type)->and(TrinaryLogic::createMaybe()); diff --git a/tests/PHPStan/Rules/Comparison/StrictComparisonOfDifferentTypesRuleTest.php b/tests/PHPStan/Rules/Comparison/StrictComparisonOfDifferentTypesRuleTest.php index d338686fef..25f6c07533 100644 --- a/tests/PHPStan/Rules/Comparison/StrictComparisonOfDifferentTypesRuleTest.php +++ b/tests/PHPStan/Rules/Comparison/StrictComparisonOfDifferentTypesRuleTest.php @@ -1040,4 +1040,16 @@ public function testBug9804(): void $this->analyse([__DIR__ . '/data/bug-9804.php'], []); } + public function testBug11161(): void + { + $this->checkAlwaysTrueStrictComparison = true; + $this->analyse([__DIR__ . '/data/bug-11161.php'], []); + } + + public function testBug10697(): void + { + $this->checkAlwaysTrueStrictComparison = true; + $this->analyse([__DIR__ . '/data/bug-10697.php'], []); + } + } diff --git a/tests/PHPStan/Rules/Comparison/data/bug-10697.php b/tests/PHPStan/Rules/Comparison/data/bug-10697.php new file mode 100644 index 0000000000..2bc2e574e9 --- /dev/null +++ b/tests/PHPStan/Rules/Comparison/data/bug-10697.php @@ -0,0 +1,12 @@ + $foo1 + * @param Collection $foo2 + */ + public static function compare(Collection $foo1, Collection $foo2): bool + { + return $foo1 === $foo2; + } +} + +/** + * @param Collection $collection + */ +function test(Collection $collection): bool +{ + return Comparator::compare($collection, $collection); +}