Skip to content

Commit fb3eed9

Browse files
phpstan-botclaude
andcommitted
Fix regression: refine guard overlap check in createConditionalExpressions()
The previous guard overlap check was too aggressive - it blocked creating conditional expressions in both loops of createConditionalExpressions(), including cases needed for variable certainty tracking. The fix: 1. Only apply the guard overlap check in the first loop (type narrowing), not the second loop (variable certainty/existence) 2. Additionally require that the guarded expression exists with certainty Yes in the other branch before skipping - this ensures we only skip when both branches define the variable (ambiguous type case), not when a variable is only defined in one branch (certainty tracking case) Adds regression test for the case reported by VincentLanglet where $order should be certainly defined after two sequential if-blocks that together cover all cases of a union type parameter. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 8be11a9 commit fb3eed9

2 files changed

Lines changed: 39 additions & 8 deletions

File tree

src/Analyser/MutatingScope.php

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3604,7 +3604,9 @@ private function createConditionalExpressions(
36043604

36053605
foreach ($variableTypeGuards as $guardExprString => $guardHolder) {
36063606
if (
3607-
array_key_exists($guardExprString, $theirExpressionTypes)
3607+
array_key_exists($exprString, $theirExpressionTypes)
3608+
&& $theirExpressionTypes[$exprString]->getCertainty()->yes()
3609+
&& array_key_exists($guardExprString, $theirExpressionTypes)
36083610
&& $theirExpressionTypes[$guardExprString]->getCertainty()->yes()
36093611
&& !$guardHolder->getType()->isSuperTypeOf($theirExpressionTypes[$guardExprString]->getType())->no()
36103612
) {
@@ -3621,13 +3623,6 @@ private function createConditionalExpressions(
36213623
}
36223624

36233625
foreach ($typeGuards as $guardExprString => $guardHolder) {
3624-
if (
3625-
array_key_exists($guardExprString, $theirExpressionTypes)
3626-
&& $theirExpressionTypes[$guardExprString]->getCertainty()->yes()
3627-
&& !$guardHolder->getType()->isSuperTypeOf($theirExpressionTypes[$guardExprString]->getType())->no()
3628-
) {
3629-
continue;
3630-
}
36313626
$conditionalExpression = new ConditionalExpressionHolder([$guardExprString => $guardHolder], new ExpressionTypeHolder($mergedExprTypeHolder->getExpr(), new ErrorType(), TrinaryLogic::createNo()));
36323627
$conditionalExpressions[$exprString][$conditionalExpression->getKey()] = $conditionalExpression;
36333628
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php // lint >= 8.0
2+
3+
declare(strict_types = 1);
4+
5+
namespace Bug14411Regression;
6+
7+
use function PHPStan\Testing\assertType;
8+
use function PHPStan\Testing\assertVariableCertainty;
9+
use PHPStan\TrinaryLogic;
10+
11+
interface OrderInterface {}
12+
13+
class Event
14+
{
15+
/** @return mixed */
16+
public function getSubject()
17+
{
18+
return new \stdClass();
19+
}
20+
}
21+
22+
function getOrder(Event|OrderInterface $event): OrderInterface
23+
{
24+
if ($event instanceof Event) {
25+
$order = $event->getSubject();
26+
assert($order instanceof OrderInterface);
27+
}
28+
29+
if ($event instanceof OrderInterface) {
30+
$order = $event;
31+
}
32+
33+
assertVariableCertainty(TrinaryLogic::createYes(), $order);
34+
35+
return $order;
36+
}

0 commit comments

Comments
 (0)