Skip to content

Fix phpstan/phpstan#13247: iterable is equal to array|Traversable#5143

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

Fix phpstan/phpstan#13247: iterable is equal to array|Traversable#5143
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-san8xi7

Conversation

@phpstan-bot
Copy link
Copy Markdown
Collaborator

Summary

iterable<K, V> is semantically equivalent to array<K, V>|Traversable<K, V>, but PHPStan was not accepting iterable<K, V> as an argument where array<K, V>|Traversable<K, V> was expected. This caused false positive argument.type errors and failed template type resolution (argument.templateType errors).

Changes

  • src/Type/UnionType.php: Added iterable-to-union decomposition in two methods:
    • accepts(): Converts IterableType<K,V> to UnionType([ArrayType<K,V>, GenericObjectType(Traversable, [K,V])]) before checking acceptance, so that iterable<K,V> is properly accepted by array<K,V>|Traversable<K,V>
    • inferTemplateTypes(): Same decomposition so template type parameters (K, V) are correctly inferred from an iterable argument when the parameter type is a union of array and Traversable
  • phpstan-baseline.neon: Updated instanceof IterableType ignore count from 1 to 3 for UnionType.php
  • tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php: Added testBug13247 test method
  • tests/PHPStan/Rules/Functions/data/bug-13247.php: Regression test data file

Root cause

UnionType::inferTemplateTypes() iterates through each union member and calls inferTemplateTypes() on them. When the received type was IterableType, neither ArrayType::inferTemplateTypes() (which requires isArray()->yes()) nor GenericObjectType::inferTemplateTypes() could extract template information from it, resulting in empty template maps. Similarly, UnionType::accepts() checked each union member individually, none of which could fully accept the iterable type, and the fallback via IterableType::isAcceptedBy()isSubTypeOf() decomposed to a form that didn't match properly against generic Traversable types.

The fix decomposes IterableType<K,V> into its equivalent array<K,V>|Traversable<K,V> union at the start of both methods, allowing the existing union-handling logic to process it correctly.

Test

Added a regression test that defines a function accepting array<K,V>|Traversable<K,V> and another function that passes iterable<K,V> to it. The test expects no errors, confirming that the iterable type is correctly accepted and templates are properly resolved.

Fixes phpstan/phpstan#13247

- Decompose IterableType to array|Traversable union in UnionType::accepts()
  so that iterable<K,V> is properly accepted where array<K,V>|Traversable<K,V>
  is expected
- Decompose IterableType in UnionType::inferTemplateTypes() so template type
  parameters are correctly resolved across the iterable equivalence
- Updated phpstan-baseline.neon to account for new instanceof IterableType usages
- New regression test in tests/PHPStan/Rules/Functions/data/bug-13247.php
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