Skip to content

Fix false positive match.alwaysTrue with consecutive match(true) expressions#5296

Closed
gnutix wants to merge 1 commit intophpstan:2.1.xfrom
gnutix:fix/match-always-true-scope-pollution
Closed

Fix false positive match.alwaysTrue with consecutive match(true) expressions#5296
gnutix wants to merge 1 commit intophpstan:2.1.xfrom
gnutix:fix/match-always-true-scope-pollution

Conversation

@gnutix
Copy link
Copy Markdown
Contributor

@gnutix gnutix commented Mar 25, 2026

Fixes phpstan/phpstan#14368

Summary

  • After an exhaustive match(true) with a throwing default arm, the post-match scope carried conditional expressions encoding relationships between arm-narrowed types (e.g. "if equals(A) is false, then equals(B) is true")
  • When a subsequent match(true) filtered by falsey for the first arm, these conditional expressions activated and incorrectly narrowed the second arm's condition to true, causing a false positive match.alwaysTrue
  • The fix restores the pre-match conditional expressions after the arm body scope merge, preventing match-internal type relationships from leaking into the post-match scope

Root cause

PR #5066 (fix #9601) changed MatchHandler to exclude always-terminating arms (e.g. default => throw ...) from the arm body scope merge. This is correct for type resolution, but the mergeWith call also creates conditionalExpressions that encode relationships between the narrowed types of different arms. These conditional expressions survived into the post-match scope and were activated by subsequent code that narrowed the same expressions.

…cutive match(true) expressions

After an exhaustive match(true) with a throwing default arm, the
post-match scope carried conditional expressions encoding relationships
between arm-narrowed types (e.g. "if equals(A) is false, then equals(B)
is true"). When a subsequent match(true) filtered by falsey for the
first arm, these conditional expressions activated and incorrectly
narrowed the second arm's condition to true.

The fix restores the pre-match conditional expressions after the arm
body scope merge, preventing match-internal type relationships from
leaking into the post-match scope.
@gnutix gnutix changed the title Fix phpstan/phpstan#14368: False positive match.alwaysTrue with consecutive match(true) expressions Fix false positive match.alwaysTrue with consecutive match(true) expressions Mar 25, 2026
@ondrejmirtes
Copy link
Copy Markdown
Member

No fix is needed, this is expected behaviour.

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