Skip to content

Fix phpstan/phpstan#10076: Union of string literal and class-string<T> does not accept that string#5346

Open
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-21zkjxx
Open

Fix phpstan/phpstan#10076: Union of string literal and class-string<T> does not accept that string#5346
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot:create-pull-request/patch-21zkjxx

Conversation

@phpstan-bot
Copy link
Copy Markdown
Collaborator

Summary

When a parameter has type 'object'|'array'|class-string<T>, passing 'object' or 'array' should be accepted because they match the string literal parts of the union. Instead, PHPStan reported false positive errors like "expects class-string, string given" because the literal string members were silently absorbed into class-string<T> during type construction.

Changes

  • Fixed GenericClassStringType::isSuperTypeOf() in src/Type/Generic/GenericClassStringType.php to return Maybe instead of Yes when the generic type is MixedType (which includes TemplateMixedType) and the constant string is not a valid class-string
  • Added regression test in tests/PHPStan/Rules/Functions/data/bug-10076.php and test method in tests/PHPStan/Rules/Functions/CallToFunctionParametersRuleTest.php

Root cause

GenericClassStringType::isSuperTypeOf() had a fast path: when the generic type was instanceof MixedType, it unconditionally returned Yes for any ConstantStringType. Template types with no explicit bound are TemplateMixedType (which extends MixedType), so class-string<T> claimed to be a supertype of strings like 'object' and 'array'. This caused TypeCombinator::union to absorb the constant string members, collapsing 'object'|'array'|class-string<T> into just class-string<T>. After template resolution, the parameter type became class-string<object> instead of 'object'|'array'|class-string<object>, producing false positive argument type errors.

The fix adds a guard: non-class-string constants now get Maybe instead of Yes, preserving them as separate union members.

Test

Added tests/PHPStan/Rules/Functions/data/bug-10076.php which reproduces the exact scenario from the issue: a function with @param 'object'|'array'|class-string<T> $type called with 'object', 'array', and DateTime::class. No errors should be reported.

Fixes phpstan/phpstan#10076

…> loses literal members

- GenericClassStringType::isSuperTypeOf() returned Yes for non-class-string
  constants when the generic type was MixedType (including TemplateMixedType)
- This caused TypeCombinator::union to absorb string literals like 'object'
  and 'array' into class-string<T>, losing them from the union
- Added a check: when generic type is MixedType but the constant string is
  not a class-string, return Maybe instead of Yes
- New regression test in tests/PHPStan/Rules/Functions/data/bug-10076.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.

1 participant