From 149a1e7514f341299769e24ce4079fdaaebecc0e Mon Sep 17 00:00:00 2001 From: Richard van Velzen Date: Wed, 8 May 2024 11:00:08 +0200 Subject: [PATCH] Specify types on match() condition correctly --- src/Analyser/TypeSpecifier.php | 13 ++++++--- .../CallToFunctionParametersRuleTest.php | 9 +++++++ .../Rules/Functions/data/bug-10974.php | 27 +++++++++++++++++++ .../Properties/AccessPropertiesRuleTest.php | 12 +++++++++ .../Rules/Properties/data/bug-9694.php | 20 ++++++++++++++ 5 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 tests/PHPStan/Rules/Functions/data/bug-10974.php create mode 100644 tests/PHPStan/Rules/Properties/data/bug-9694.php diff --git a/src/Analyser/TypeSpecifier.php b/src/Analyser/TypeSpecifier.php index 3c90d190b1..2fa2a7017d 100644 --- a/src/Analyser/TypeSpecifier.php +++ b/src/Analyser/TypeSpecifier.php @@ -1991,11 +1991,16 @@ public function resolveIdentical(Expr\BinaryOp\Identical $expr, Scope $scope, Ty $exprNode = $expressions[0]; $constantType = $expressions[1]; - $specifiedType = $this->specifyTypesForConstantBinaryExpression($exprNode, $constantType, $context, $scope, $rootExpr); + $unwrappedExprNode = $exprNode; + if ($exprNode instanceof AlwaysRememberedExpr) { + $unwrappedExprNode = $exprNode->getExpr(); + } + + $specifiedType = $this->specifyTypesForConstantBinaryExpression($unwrappedExprNode, $constantType, $context, $scope, $rootExpr); if ($specifiedType !== null) { - if ($exprNode instanceof AlwaysRememberedExpr) { - $specifiedType->unionWith( - $this->create($exprNode->getExpr(), $constantType, $context, false, $scope, $rootExpr), + if ($exprNode !== $unwrappedExprNode) { + $specifiedType = $specifiedType->unionWith( + $this->create($exprNode, $constantType, $context, false, $scope, $rootExpr), ); } return $specifiedType; diff --git a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php index 1a8770dd05..5d1e0c3e39 100644 --- a/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php +++ b/tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php @@ -1669,4 +1669,13 @@ public function testBug10297(): void $this->analyse([__DIR__ . '/data/bug-10297.php'], []); } + public function testBug10974(): void + { + if (PHP_VERSION_ID < 80000) { + $this->markTestSkipped('Test requires PHP 8.0.'); + } + + $this->analyse([__DIR__ . '/data/bug-10974.php'], []); + } + } diff --git a/tests/PHPStan/Rules/Functions/data/bug-10974.php b/tests/PHPStan/Rules/Functions/data/bug-10974.php new file mode 100644 index 0000000000..5e93af420a --- /dev/null +++ b/tests/PHPStan/Rules/Functions/data/bug-10974.php @@ -0,0 +1,27 @@ += 8.0 + +namespace Bug10974; + +function non(): void {} +function single(string $str): void {} +/** @param non-empty-array $strs */ +function multiple(array $strs): void {} + +/** @param array $arr */ +function test(array $arr): void +{ + match (count($arr)) + { + 0 => non(), + 1 => single(reset($arr)), + default => multiple($arr) + }; + + if (empty($arr)) { + non(); + } elseif (count($arr) === 1) { + single(reset($arr)); + } else { + multiple($arr); + } +} diff --git a/tests/PHPStan/Rules/Properties/AccessPropertiesRuleTest.php b/tests/PHPStan/Rules/Properties/AccessPropertiesRuleTest.php index 4673409880..7697f826e3 100644 --- a/tests/PHPStan/Rules/Properties/AccessPropertiesRuleTest.php +++ b/tests/PHPStan/Rules/Properties/AccessPropertiesRuleTest.php @@ -937,4 +937,16 @@ public function testBug8629(): void $this->analyse([__DIR__ . '/data/bug-8629.php'], []); } + public function testBug9694(): void + { + if (PHP_VERSION_ID < 80000) { + $this->markTestSkipped('Test requires PHP 8.0.'); + } + + $this->checkThisOnly = false; + $this->checkUnionTypes = true; + $this->checkDynamicProperties = true; + $this->analyse([__DIR__ . '/data/bug-9694.php'], []); + } + } diff --git a/tests/PHPStan/Rules/Properties/data/bug-9694.php b/tests/PHPStan/Rules/Properties/data/bug-9694.php new file mode 100644 index 0000000000..96cd448073 --- /dev/null +++ b/tests/PHPStan/Rules/Properties/data/bug-9694.php @@ -0,0 +1,20 @@ += 8.0 + +class TotpEnrollment +{ + public bool $confirmed; +} + +class User +{ + public ?TotpEnrollment $totpEnrollment; +} + +function () { + $user = new User(); + + return match ($user->totpEnrollment === null) { + true => false, + false => $user->totpEnrollment->confirmed, + }; +};