Skip to content

Fix phpstan/phpstan#14274: "Variable might not be defined" false positive#5216

Merged
ondrejmirtes merged 3 commits intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-pyp01f2
Mar 13, 2026
Merged

Fix phpstan/phpstan#14274: "Variable might not be defined" false positive#5216
ondrejmirtes merged 3 commits intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-pyp01f2

Conversation

@phpstan-bot
Copy link
Collaborator

Summary

Fixes a false positive "Variable might not be defined" error when maybe-defined variables are guarded by a boolean flag inside complex boolean expressions (|| combined with &&). The issue occurred when multiple if blocks used the same guarded variables, and an unrelated variable assignment in an if body caused the conditional expression holders to be incorrectly dropped.

Changes

  • Modified MutatingScope::invalidateExpression() in src/Analyser/MutatingScope.php to filter ConditionalExpressionHolder instances individually rather than dropping all holders for a variable when any single holder has an invalidated condition guard
  • Added regression test in tests/PHPStan/Rules/Variables/data/bug-14274.php and corresponding test method in DefinedVariableRuleTest.php

Root cause

When invalidateExpression() was called (e.g., when assigning to $messages[]), the code used continue 3 to skip the entire set of ConditionalExpressionHolder instances for a variable if any holder had a condition guard matching the invalidated expression. This meant that if createConditionalExpressions() had added a spurious holder guarded by $messages (as a type guard) alongside the original holder guarded by $has_staged_update, invalidating $messages would also discard the $has_staged_update-guarded holder. This caused subsequent if blocks to lose the information that the variable was definitely defined when $has_staged_update was true.

The fix changes the logic to filter holders individually: only holders whose condition guards match the invalidated expression are removed, while other holders for the same variable are preserved.

Test

The regression test reproduces the exact scenario from the issue: an if/else block that sets a boolean flag and defines variables via list assignment, followed by multiple if blocks using || with &&-guarded access to those variables. Without the fix, the third if block incorrectly reports "Variable might not be defined" for variables that are properly guarded by the boolean flag.

Fixes phpstan/phpstan#14274

…tive

- Fixed invalidateExpression() dropping all ConditionalExpressionHolders for a
  variable when only some holders had an invalidated condition guard
- Previously, `continue 3` skipped the entire variable's holders when any single
  holder's condition guard matched the invalidated expression
- Now filters holders individually, preserving valid holders (e.g. those guarded
  by $has_staged_update) when unrelated holders (e.g. those guarded by $messages)
  are invalidated
- New regression test in tests/PHPStan/Rules/Variables/data/bug-14274.php
Copy link
Contributor

@staabm staabm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needs a regression test for phpstan/phpstan#12373

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@phpstan-bot
Copy link
Collaborator Author

The background make phpstan task completed successfully (exit code 0) — I already retrieved its output earlier showing "No errors". Everything is pushed and ready for review.

@phpstan-bot
Copy link
Collaborator Author

The review from staabm is APPROVED — no further changes are requested.

Looking at the PR diff and recent commits, the regression test for phpstan/phpstan#12373 that was previously requested has already been added:

  • tests/PHPStan/Rules/Variables/data/bug-12373.php — test data file
  • testBug12373() method in DefinedVariableRuleTest.php

The recent commits confirm this:

Everything is already in order — no additional changes needed.

@ondrejmirtes ondrejmirtes merged commit 06417d4 into phpstan:2.1.x Mar 13, 2026
642 of 651 checks passed
@ondrejmirtes ondrejmirtes deleted the create-pull-request/patch-pyp01f2 branch March 13, 2026 09:17
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.

3 participants