Skip to content

Fix phpstan/phpstan#13029: False match.unhandled on array with multiple booleans#5107

Merged
VincentLanglet merged 3 commits intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-8comyju
Mar 1, 2026
Merged

Fix phpstan/phpstan#13029: False match.unhandled on array with multiple booleans#5107
VincentLanglet merged 3 commits intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-8comyju

Conversation

@phpstan-bot
Copy link
Collaborator

Summary

Fixes a false positive match.unhandled error when using a match expression with an array subject containing multiple booleans and multi-condition arms (comma-separated conditions).

For example, this valid code was incorrectly reported as having an unhandled array{true, bool}:

$x = match([$bool1, $bool2]) {
    [true, false], [true, true] => 1,
    [false, false] => 0,
    [false, true] => -1,
};

Changes

  • Modified TypeCombinator::remove() in src/Type/TypeCombinator.php to handle removal of types with multiple finite types
  • Added regression test in tests/PHPStan/Rules/Comparison/data/bug-13029.php
  • Added test method testBug13029 in tests/PHPStan/Rules/Comparison/MatchExpressionRuleTest.php

Root cause

When a match arm has multiple conditions (e.g., [true, false], [true, true]), PHPStan builds an in_array() expression for type narrowing. The SpecifiedTypes::unionWith() correctly combines the sureNotTypes from each condition into a union type (e.g., array{true, false} | array{true, true} simplifies to array{true, bool}).

However, TypeCombinator::remove() had a restriction at line 93 that only performed finite-type-based removal when the type to remove had exactly 1 finite type (count($finiteTypesToRemove) === 1). The union array{true, bool} has 2 finite types (array{true, true} and array{true, false}), so the removal was silently skipped, leaving the match condition type un-narrowed.

The fix changes the condition to count($finiteTypesToRemove) > 0 and compares against all finite types to remove, not just the first one.

Test

Added a regression test that reproduces the exact scenario from the issue: a match expression with [$bool1, $bool2] as the subject where the first arm uses comma-separated array conditions [true, false], [true, true]. The test expects no errors (empty expected errors array).

Fixes phpstan/phpstan#13029

- Fixed TypeCombinator::remove() to handle removal of types with multiple finite types
- Previously only removed types with exactly 1 finite type, now handles any count
- This caused match expressions with array conditions and multi-condition arms to not narrow types properly
- New regression test in tests/PHPStan/Rules/Comparison/data/bug-13029.php

Closes phpstan/phpstan#13029
Copy link
Contributor

@VincentLanglet VincentLanglet left a comment

Choose a reason for hiding this comment

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

Seems fine to me ; but this seems to not be enough for the related issue phpstan/phpstan#10128

Comment on lines +14 to +18
$x = match([$bool1, $bool2]) {
[true, false], [true, true] => 1,
[false, false] => 0,
[false, true] => -1,
};
Copy link
Contributor

Choose a reason for hiding this comment

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

since this fix is based on finite types, maybe we can also add a test for int-ranges?

Copy link
Contributor

@staabm staabm Mar 1, 2026

Choose a reason for hiding this comment

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

@staabm staabm changed the title Fix #13029: False match.unhandled on array with multiple booleans Fix phpstan/phpstan#13029: False match.unhandled on array with multiple booleans Mar 1, 2026
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.

love it

@VincentLanglet
Copy link
Contributor

That doesn't solve

$y = match([$int1, $int2]) {
		[0, 0], [1, 1], [2, 2] => 1,
		[0, 1], [1, 2], [2, 0] => 0,
		[0, 2], [1, 0], [2, 1] => -1,
	};

Because of simplification... I hope the bot will find why ^^

@VincentLanglet VincentLanglet merged commit a752f73 into phpstan:2.1.x Mar 1, 2026
359 of 362 checks passed
@staabm staabm deleted the create-pull-request/patch-8comyju branch March 1, 2026 11:05
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