Skip to content

Do not let conditional expression undo explicit type narrowing#5710

Merged
ondrejmirtes merged 1 commit into
phpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-464dt5x
May 19, 2026
Merged

Do not let conditional expression undo explicit type narrowing#5710
ondrejmirtes merged 1 commit into
phpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-464dt5x

Conversation

@phpstan-bot
Copy link
Copy Markdown
Collaborator

Summary

  • Fix regression where mutual-exclusion if (A && B) throw leaked narrowing across later if (B !== null) checks, causing "Cannot call method on X|null" false positive
  • In filterBySpecifiedTypes, skip conditional expression holders with certainty No (remove from scope) when the target expression was already explicitly narrowed by the current type specifications
  • Add regression tests covering properties, local variables, reversed operand order, and three-property mutual exclusion

Root cause

When processing if ($this->b !== null):

  1. $this->b is narrowed to non-null (from the condition)
  2. A CE fires: "if $this->b is non-null, then $this->a is null" (from earlier mutual exclusion)
  3. Another CE fires: "if $this->a is null, remove $this->b from scope" — this undoes the narrowing from step 1

Step 3 contradicts the explicit condition. The fix prevents "remove from scope" CEs from overriding expressions that were directly narrowed by the current type specification.

Test plan

  • Regression test tests/PHPStan/Analyser/nsrt/bug-14645.php fails without fix, passes with fix
  • Tests cover properties, local variables, reversed order, and three-property patterns
  • make tests — all 12121 tests pass
  • make phpstan — no errors
  • make cs-fix — no violations

Closes phpstan/phpstan#14645

When filterBySpecifiedTypes processes a condition like `$this->b !== null`,
it narrows the expression and then resolves conditional expression holders.
A CE chain could derive `$this->a = null` (from mutual exclusion) and then
use that to trigger a "remove $this->b from scope" CE, undoing the explicit
narrowing. This fix skips certainty-No CEs for expressions that were already
narrowed by the original type specifications.

Closes phpstan/phpstan#14645
@ondrejmirtes ondrejmirtes merged commit 678e790 into phpstan:2.1.x May 19, 2026
655 of 659 checks passed
@ondrejmirtes ondrejmirtes deleted the create-pull-request/patch-464dt5x branch May 19, 2026 13:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants