From 9c1f32e2b2c94c3b6e4a38f12fc0a5f534a1eb53 Mon Sep 17 00:00:00 2001 From: VincentLanglet <9052536+VincentLanglet@users.noreply.github.com> Date: Thu, 2 Apr 2026 18:52:28 +0000 Subject: [PATCH] Fix #10422 --- src/Analyser/MutatingScope.php | 23 ++++++++++++++++++- tests/PHPStan/Analyser/nsrt/bug-10422.php | 28 +++++++++++++++++++++++ tests/PHPStan/Analyser/nsrt/bug-5051.php | 10 ++++---- 3 files changed, 55 insertions(+), 6 deletions(-) create mode 100644 tests/PHPStan/Analyser/nsrt/bug-10422.php diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index a43ba35dda..b7fa5fda4e 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -3210,6 +3210,7 @@ public function filterBySpecifiedTypes(SpecifiedTypes $specifiedTypes): self $conditions = []; $prevSpecifiedCount = -1; + $debugFile = getenv('PHPSTAN_DEBUG_FILTER') ?: ''; while (count($specifiedExpressions) !== $prevSpecifiedCount) { $prevSpecifiedCount = count($specifiedExpressions); foreach ($scope->conditionalExpressions as $conditionalExprString => $conditionalExpressions) { @@ -3218,11 +3219,31 @@ public function filterBySpecifiedTypes(SpecifiedTypes $specifiedTypes): self } foreach ($conditionalExpressions as $conditionalExpression) { foreach ($conditionalExpression->getConditionExpressionTypeHolders() as $holderExprString => $conditionalTypeHolder) { - if (!array_key_exists($holderExprString, $specifiedExpressions) || !$specifiedExpressions[$holderExprString]->equals($conditionalTypeHolder)) { + if (!array_key_exists($holderExprString, $specifiedExpressions)) { continue 2; } + $specifiedHolder = $specifiedExpressions[$holderExprString]; + if (!$specifiedHolder->equals($conditionalTypeHolder)) { + if ( + !$conditionalExpression->getTypeHolder()->getCertainty()->yes() + || !$specifiedHolder->getCertainty()->equals($conditionalTypeHolder->getCertainty()) + || !$conditionalTypeHolder->getType() instanceof UnionType + || !$conditionalTypeHolder->getType()->isSuperTypeOf($specifiedHolder->getType())->yes() + ) { + if ($debugFile !== '' && str_contains($scope->getFile(), $debugFile)) { + file_put_contents('/tmp/phpstan-debug.log', sprintf("SKIP: target=%s guard=%s guardType=%s specType=%s targetCert=%s\n", $conditionalExprString, $holderExprString, $conditionalTypeHolder->getType()->describe(\PHPStan\Type\VerbosityLevel::precise()), $specifiedHolder->getType()->describe(\PHPStan\Type\VerbosityLevel::precise()), $conditionalExpression->getTypeHolder()->getCertainty()->describe()), FILE_APPEND); + } + continue 2; + } + if ($debugFile !== '' && str_contains($scope->getFile(), $debugFile)) { + file_put_contents('/tmp/phpstan-debug.log', sprintf("RELAXED MATCH: target=%s guard=%s guardType=%s specType=%s targetCert=%s targetType=%s\n", $conditionalExprString, $holderExprString, $conditionalTypeHolder->getType()->describe(\PHPStan\Type\VerbosityLevel::precise()), $specifiedHolder->getType()->describe(\PHPStan\Type\VerbosityLevel::precise()), $conditionalExpression->getTypeHolder()->getCertainty()->describe(), $conditionalExpression->getTypeHolder()->getType()->describe(\PHPStan\Type\VerbosityLevel::precise())), FILE_APPEND); + } + } } + if ($debugFile !== '' && str_contains($scope->getFile(), $debugFile)) { + file_put_contents('/tmp/phpstan-debug.log', sprintf("MATCHED: target=%s targetType=%s targetCert=%s\n", $conditionalExprString, $conditionalExpression->getTypeHolder()->getType()->describe(\PHPStan\Type\VerbosityLevel::precise()), $conditionalExpression->getTypeHolder()->getCertainty()->describe()), FILE_APPEND); + } $conditions[$conditionalExprString][] = $conditionalExpression; $specifiedExpressions[$conditionalExprString] = $conditionalExpression->getTypeHolder(); } diff --git a/tests/PHPStan/Analyser/nsrt/bug-10422.php b/tests/PHPStan/Analyser/nsrt/bug-10422.php new file mode 100644 index 0000000000..2f43789c57 --- /dev/null +++ b/tests/PHPStan/Analyser/nsrt/bug-10422.php @@ -0,0 +1,28 @@ +something()) { + $error = 'another'; + } + if ($error) { + die('Done'); + } + assertType('Bug10422\TestClass', $test); + $test->test(); +}; diff --git a/tests/PHPStan/Analyser/nsrt/bug-5051.php b/tests/PHPStan/Analyser/nsrt/bug-5051.php index 6c3e80dce1..1bc7bc8c96 100644 --- a/tests/PHPStan/Analyser/nsrt/bug-5051.php +++ b/tests/PHPStan/Analyser/nsrt/bug-5051.php @@ -64,23 +64,23 @@ public function testWithBooleans($data): void } if ($data === 1) { - assertType('bool', $update); - assertType('bool', $foo); + assertType('false', $update); + assertType('false', $foo); } else { assertType('bool', $update); assertType('bool', $foo); } if ($data === 2) { - assertType('bool', $update); - assertType('bool', $foo); + assertType('false', $update); + assertType('false', $foo); } else { assertType('bool', $update); assertType('bool', $foo); } if ($data === 3) { - assertType('bool', $update); + assertType('false', $update); assertType('true', $foo); } else { assertType('bool', $update);