diff --git a/src/Analyser/TypeSpecifier.php b/src/Analyser/TypeSpecifier.php index c55d1b06d4..c094bbd0c4 100644 --- a/src/Analyser/TypeSpecifier.php +++ b/src/Analyser/TypeSpecifier.php @@ -317,6 +317,9 @@ public function specifyTypesInCondition( if ($types !== null) { return $types; } + + return $this->create($expr->left, $exprLeftType, $context, false, $scope)->normalize($scope) + ->intersectWith($this->create($expr->right, $exprRightType, $context, false, $scope)->normalize($scope)); } } elseif ($expr instanceof Node\Expr\BinaryOp\NotIdentical) { @@ -434,6 +437,13 @@ public function specifyTypesInCondition( ) { return $this->specifyTypesInCondition($scope, new Expr\BinaryOp\Identical($expr->left, $expr->right), $context); } + + $leftTypes = $this->create($expr->left, $leftType, $context, false, $scope); + $rightTypes = $this->create($expr->right, $rightType, $context, false, $scope); + + return $context->true() + ? $leftTypes->unionWith($rightTypes) + : $leftTypes->normalize($scope)->intersectWith($rightTypes->normalize($scope)); } elseif ($expr instanceof Node\Expr\BinaryOp\NotEqual) { return $this->specifyTypesInCondition( $scope, diff --git a/tests/PHPStan/Analyser/NodeScopeResolverTest.php b/tests/PHPStan/Analyser/NodeScopeResolverTest.php index 2620bf7552..0cae949669 100644 --- a/tests/PHPStan/Analyser/NodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/NodeScopeResolverTest.php @@ -649,6 +649,7 @@ public function dataFileAsserts(): iterable yield from $this->gatherAssertTypes(__DIR__ . '/data/weird-array_key_exists-issue.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/equal.php'); + yield from $this->gatherAssertTypes(__DIR__ . '/data/identical.php'); if (PHP_VERSION_ID >= 80000) { yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-5698-php8.php'); diff --git a/tests/PHPStan/Analyser/TypeSpecifierTest.php b/tests/PHPStan/Analyser/TypeSpecifierTest.php index 424a1ab1e9..9ea86aedaf 100644 --- a/tests/PHPStan/Analyser/TypeSpecifierTest.php +++ b/tests/PHPStan/Analyser/TypeSpecifierTest.php @@ -22,7 +22,9 @@ use PHPStan\Type\ArrayType; use PHPStan\Type\ClassStringType; use PHPStan\Type\Constant\ConstantBooleanType; +use PHPStan\Type\FloatType; use PHPStan\Type\Generic\GenericClassStringType; +use PHPStan\Type\IntegerType; use PHPStan\Type\MixedType; use PHPStan\Type\NullType; use PHPStan\Type\ObjectType; @@ -68,6 +70,8 @@ protected function setUp(): void $this->scope = $this->scope->assignVariable('classString', new ClassStringType()); $this->scope = $this->scope->assignVariable('genericClassString', new GenericClassStringType(new ObjectType('Bar'))); $this->scope = $this->scope->assignVariable('object', new ObjectWithoutClassType()); + $this->scope = $this->scope->assignVariable('int', new IntegerType()); + $this->scope = $this->scope->assignVariable('float', new FloatType()); } /** @@ -1120,6 +1124,34 @@ public function dataCondition(): array '$foo' => 'mixed~non-empty-string', ], ], + [ + new Expr\BinaryOp\BooleanAnd( + $this->createFunctionCall('is_numeric', 'int'), + new Expr\BinaryOp\Equal( + new Variable('int'), + new Expr\Cast\Int_(new Variable('int')), + ), + ), + [ + '$int' => 'int', + '(int) $int' => 'int', + ], + [], + ], + [ + new Expr\BinaryOp\BooleanAnd( + $this->createFunctionCall('is_numeric', 'float'), + new Expr\BinaryOp\Equal( + new Variable('float'), + new Expr\Cast\Int_(new Variable('float')), + ), + ), + [ + '$float' => 'float', + '(int) $float' => 'int', + ], + [], + ], ]; } diff --git a/tests/PHPStan/Analyser/data/equal.php b/tests/PHPStan/Analyser/data/equal.php index 27dd59743f..113d2868b2 100644 --- a/tests/PHPStan/Analyser/data/equal.php +++ b/tests/PHPStan/Analyser/data/equal.php @@ -65,4 +65,38 @@ public function doIpsum(array $a): void assertType('array', $a); } + public function stdClass(\stdClass $a, \stdClass $b): void + { + if ($a == $a) { + assertType('stdClass', $a); + } else { + assertType('*NEVER*', $a); + } + + if ($b != $b) { + assertType('*NEVER*', $b); + } else { + assertType('stdClass', $b); + } + + if ($a == $b) { + assertType('stdClass', $a); + assertType('stdClass', $b); + } else { + assertType('stdClass', $a); + assertType('stdClass', $b); + } + + if ($a != $b) { + assertType('stdClass', $a); + assertType('stdClass', $b); + } else { + assertType('stdClass', $a); + assertType('stdClass', $b); + } + + assertType('stdClass', $a); + assertType('stdClass', $b); + } + } diff --git a/tests/PHPStan/Analyser/data/identical.php b/tests/PHPStan/Analyser/data/identical.php new file mode 100644 index 0000000000..26602e2918 --- /dev/null +++ b/tests/PHPStan/Analyser/data/identical.php @@ -0,0 +1,44 @@ +