Skip to content

Fix #11310: False Positive on Match Arm Comparison with Incremented Bounded Integer#4968

Open
phpstan-bot wants to merge 2 commits into2.1.xfrom
create-pull-request/patch-ssi6g55
Open

Fix #11310: False Positive on Match Arm Comparison with Incremented Bounded Integer#4968
phpstan-bot wants to merge 2 commits into2.1.xfrom
create-pull-request/patch-ssi6g55

Conversation

@phpstan-bot
Copy link
Collaborator

Summary

When using post-increment ($i++) or post-decrement ($i--) as the condition in a match expression, PHPStan incorrectly evaluated the match arms using the post-incremented type instead of the original value. For example, match ($i++) { 0 => ... } with $i: int<0, max> would report "Match arm comparison between int<1, max> and 0 is always false", even though $i++ evaluates to the original value of $i before incrementing.

The same issue also affected pre-increment (++$i), which was double-incremented — the type was incremented once during expression processing, then enterMatch() would re-evaluate it in the updated scope, effectively incrementing it a second time.

Changes

  • Modified MutatingScope::enterMatch() in src/Analyser/MutatingScope.php to accept optional pre-computed condition type and native type parameters
  • Modified NodeScopeResolver in src/Analyser/NodeScopeResolver.php to capture both the condition type and native type before processing the condition expression, and pass them to enterMatch()
  • Added regression test in tests/PHPStan/Rules/Comparison/data/bug-11310.php covering post-increment, post-decrement, and pre-increment in match conditions
  • Added test method testBug11310() in tests/PHPStan/Rules/Comparison/MatchExpressionRuleTest.php

Root cause

In NodeScopeResolver::processExprNode() for Expr\Match_, the condition type was captured correctly at line 4178 ($condType = $scope->getType($expr->cond)), but then the condition expression was processed (line 4179), which updates the scope with the incremented variable type. When $scope->enterMatch($expr) was called (line 4185), enterMatch() would re-evaluate $this->getType($cond) using the already-updated scope, getting the wrong type.

The fix passes the pre-computed types directly to enterMatch() so it doesn't need to re-derive them from the (now stale) scope.

Test

Added tests/PHPStan/Rules/Comparison/data/bug-11310.php with three test cases:

  • $i++ (post-increment) in match — should not report false positive on 0 => 'zero'
  • $i-- (post-decrement) in match — should not report false positive on 0 => 'zero'
  • ++$i (pre-increment) in match — should correctly report that 0 is always false (since ++$i on int<0, max> gives int<1, max>), but should NOT report 1 as always false

Fixes phpstan/phpstan#11310

phpstan-bot and others added 2 commits February 17, 2026 01:02
- MutatingScope::enterMatch() re-evaluated the condition type after the
  scope had already been updated by processing the increment expression,
  causing $i++ to be seen as int<1, max> instead of int<0, max>
- Pass the pre-computed condition type and native type from
  NodeScopeResolver to enterMatch() so it uses the correct types
- New regression test in tests/PHPStan/Rules/Comparison/data/bug-11310.php

Closes phpstan/phpstan#11310
Automated fix attempt 1 for CI failures.
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.

1 participant