Skip to content

Fix phpstan/phpstan#14206: Incorrect @var PDOStatement<int,string> contains unresolvable type#5128

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

Fix phpstan/phpstan#14206: Incorrect @var PDOStatement<int,string> contains unresolvable type#5128
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-p7k2kkx

Conversation

@phpstan-bot
Copy link
Collaborator

Summary

When using @var PDOStatement<int,string> to override a PDOStatement's iteration types (e.g. when using PDO::FETCH_COLUMN), PHPStan 2.1.39+ incorrectly reports "PHPDoc tag @var contains unresolvable type" (varTag.unresolvableType). This was a regression introduced in 2.1.39.

Changes

  • Modified src/PhpDoc/TypeNodeResolver.php: In resolveGenericTypeNode(), when a non-generic iterable class (like PDOStatement) is parameterized with type arguments and TypeCombinator::intersect collapses the result to NeverType, the code now falls back to creating the IntersectionType directly instead of returning the collapsed NeverType.
  • Added regression test in tests/PHPStan/Rules/PhpDoc/data/bug-14206.php and tests/PHPStan/Rules/PhpDoc/InvalidPhpDocVarTagTypeRuleTest.php.

Root cause

The PDOStatement stub's getIterator() return type was changed from Iterator to Iterator<mixed, array<int|string, mixed>> in commit 97288da. This made PDOStatement->getIterableValueType() return array<int|string, mixed> instead of TemplateMixedType (implicit mixed from the unparameterized Iterator).

When users write @var PDOStatement<int,string>, the TypeNodeResolver interprets this as PDOStatement & iterable<int, string> via TypeCombinator::intersect. With the more precise iterable value type, IterableType(int, string)->isSuperTypeOfMixed(PDOStatement) now returns No (because StringType->isSuperTypeOf(ArrayType) is No), causing the intersection to collapse to NeverType. The UnresolvableTypeHelper then detects this non-explicit NeverType and reports the "unresolvable type" error.

The fix preserves the IntersectionType when TypeCombinator::intersect would otherwise produce NeverType, maintaining the @var override semantics that users rely on.

Test

Added testBug14206 in InvalidPhpDocVarTagTypeRuleTest with a test file that uses @var PDOStatement<int,string> and expects no errors.

Fixes phpstan/phpstan#14206

…reported as unresolvable

- When TypeNodeResolver creates an intersection of a non-generic iterable class with user-provided type arguments (e.g. PDOStatement<int,string>), TypeCombinator::intersect may collapse the result to NeverType if the iterable types are incompatible
- This was triggered by the more precise getIterator() return type in the PDOStatement stub (Iterator<mixed, array<int|string, mixed>> instead of plain Iterator)
- The fix preserves the IntersectionType directly when the intersection would otherwise collapse to NeverType, maintaining @var override semantics
- New regression test in tests/PHPStan/Rules/PhpDoc/data/bug-14206.php
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.

I don't like this fix, I'll explain on the issue my thought.

@VincentLanglet
Copy link
Contributor

Closed in favor of #5132

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