Skip to content

Fix phpstan/phpstan#11488: Wrongly assumed undefined variable since 1.11.10#5151

Closed
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-owt81me
Closed

Fix phpstan/phpstan#11488: Wrongly assumed undefined variable since 1.11.10#5151
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-owt81me

Conversation

@phpstan-bot
Copy link
Copy Markdown
Collaborator

Summary

When using count() to narrow a union of constant array types (e.g., array{mixed}|array{mixed, string|null, mixed}), the falsey branch (e.g., count($row) !== 1) incorrectly narrowed the type to *NEVER* instead of the expected non-matching array type. This caused false positive "undefined variable" errors when destructuring the narrowed array.

Changes

  • Modified specifyTypesForCountFuncCall in src/Analyser/TypeSpecifier.php to use a direct type assignment (via setAlwaysOverwriteTypes()) for the falsey context when all arrays in the union have definite (fixed) sizes
  • Tracked non-matching array types separately during the loop, so the falsey path can directly assign the complement set rather than relying on TypeCombinator::remove
  • Added regression test in tests/PHPStan/Analyser/nsrt/bug-11488.php

Root cause

ConstantArrayType::isSuperTypeOf uses structural subtyping: array{mixed} is considered a supertype of array{mixed, string|null, mixed} because every key in the smaller array exists in the larger one with compatible value types. This is intentional for general type checking, but it caused TypeCombinator::remove to be overly aggressive when removing matching count types — it would remove the entire union (producing *NEVER*) instead of just the matching member.

The fix avoids TypeCombinator::remove entirely for this case by collecting non-matching array types and directly assigning them as the narrowed type.

Test

Added tests/PHPStan/Analyser/nsrt/bug-11488.php with three test methods covering:

  • count($row) !== 1 narrowing with array{mixed}|array{mixed, string|null, mixed}
  • Same narrowing with array{bool}|array{mixed, string|null, mixed}
  • count($row) === 3 truthy narrowing

Fixes phpstan/phpstan#11488

- Fixed specifyTypesForCountFuncCall to use direct type assignment instead
  of TypeCombinator::remove for falsey context with fixed-size constant arrays
- The root cause was ConstantArrayType::isSuperTypeOf using structural
  subtyping (array{mixed} is supertype of array{mixed, string|null, mixed}),
  which made TypeCombinator::remove overly aggressive
- New regression test in tests/PHPStan/Analyser/nsrt/bug-11488.php

Closes phpstan/phpstan#11488
@VincentLanglet VincentLanglet deleted the create-pull-request/patch-owt81me branch March 12, 2026 22:28
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