Skip to content

Commit 19ec231

Browse files
phpstan-botclaude
andcommitted
Add comment explaining why Pass 1 does not need No-certainty check
Address review feedback: Pass 1 uses exact matching which cannot produce the asymmetry that causes the bug in Pass 2. The supertype match in Pass 2 is inherently asymmetric - a broader condition type (the "undefined" branch) is more likely to be a supertype of a narrowed type than a narrower condition type (the "defined" branch). Exact matching has no such asymmetry. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 3ff4932 commit 19ec231

1 file changed

Lines changed: 8 additions & 0 deletions

File tree

src/Analyser/MutatingScope.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3237,6 +3237,14 @@ public function filterBySpecifiedTypes(SpecifiedTypes $specifiedTypes): self
32373237
}
32383238

32393239
// Pass 2: Supertype match. Only runs when Pass 1 found no exact match for this expression.
3240+
// Skip No-certainty holders here because supertype matching is asymmetric:
3241+
// a broader condition type (the "undefined" branch) is more likely to match
3242+
// as a supertype of a narrowed specified type than a narrower condition type
3243+
// (the "defined" branch), causing conditionally-defined variables to be
3244+
// incorrectly resolved as never defined.
3245+
// Pass 1 does not need this check because exact matching cannot produce
3246+
// this asymmetry - if the specified type exactly equals the No branch's
3247+
// condition, the scope genuinely corresponds to the "undefined" state.
32403248
foreach ($conditionalExpressions as $conditionalExpression) {
32413249
if ($conditionalExpression->getTypeHolder()->getCertainty()->no()) {
32423250
continue;

0 commit comments

Comments
 (0)