From a849f06ff5195c95d0c03ef14dffc65986fe92a2 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Mon, 30 Oct 2023 19:07:38 +0100 Subject: [PATCH] Fix type-specifying of expression involved in nullsafe property fetch --- src/Analyser/TypeSpecifier.php | 8 +++++++ .../Analyser/NodeScopeResolverTest.php | 4 ++++ tests/PHPStan/Analyser/data/bug-10071.php | 22 +++++++++++++++++++ 3 files changed, 34 insertions(+) create mode 100644 tests/PHPStan/Analyser/data/bug-10071.php diff --git a/src/Analyser/TypeSpecifier.php b/src/Analyser/TypeSpecifier.php index f981395f34..27fee51d78 100644 --- a/src/Analyser/TypeSpecifier.php +++ b/src/Analyser/TypeSpecifier.php @@ -861,6 +861,10 @@ private function specifyTypesForConstantBinaryExpression( { if (!$context->null() && $constantType->getValue() === false) { $types = $this->create($exprNode, $constantType, $context, false, $scope, $rootExpr); + if ($exprNode instanceof Expr\NullsafeMethodCall || $exprNode instanceof Expr\NullsafePropertyFetch) { + return $types; + } + return $types->unionWith($this->specifyTypesInCondition( $scope, $exprNode, @@ -871,6 +875,10 @@ private function specifyTypesForConstantBinaryExpression( if (!$context->null() && $constantType->getValue() === true) { $types = $this->create($exprNode, $constantType, $context, false, $scope, $rootExpr); + if ($exprNode instanceof Expr\NullsafeMethodCall || $exprNode instanceof Expr\NullsafePropertyFetch) { + return $types; + } + return $types->unionWith($this->specifyTypesInCondition( $scope, $exprNode, diff --git a/tests/PHPStan/Analyser/NodeScopeResolverTest.php b/tests/PHPStan/Analyser/NodeScopeResolverTest.php index 95e17a2eb6..2da5d1a76d 100644 --- a/tests/PHPStan/Analyser/NodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/NodeScopeResolverTest.php @@ -1184,6 +1184,10 @@ public function dataFileAsserts(): iterable yield from $this->gatherAssertTypes(__DIR__ . '/data/allowed-subtypes-enum.php'); } + if (PHP_VERSION_ID >= 80000) { + yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-10071.php'); + } + yield from $this->gatherAssertTypes(__DIR__ . '/data/allowed-subtypes-datetime.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/allowed-subtypes-throwable.php'); yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Methods/data/bug-8174.php'); diff --git a/tests/PHPStan/Analyser/data/bug-10071.php b/tests/PHPStan/Analyser/data/bug-10071.php new file mode 100644 index 0000000000..ecb298adb3 --- /dev/null +++ b/tests/PHPStan/Analyser/data/bug-10071.php @@ -0,0 +1,22 @@ += 8.0 + +namespace Bug10071; + +use function PHPStan\Testing\assertType; + +class Foo +{ + public ?bool $bar = null; +} + + +function okIfBar(?Foo $foo = null): void +{ + if ($foo?->bar !== false) { + assertType(Foo::class . '|null', $foo); + } else { + assertType(Foo::class, $foo); + } + + assertType(Foo::class . '|null', $foo); +}