Fix phpstan/phpstan#5357: Intersection of generics are not simplified, calling method on it returns *NEVER*#5343
Open
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
Conversation
- When two GenericObjectTypes of the same class are intersected (e.g. PagerInterface<ProxyQueryInterface> & PagerInterface<Proxy>), merge their type parameters instead of returning never - This fixes the case where a raw generic class (Datagrid) in an intersection with a generic interface (DatagridInterface<Proxy>) caused method return types to resolve to *NEVER* - New regression test in tests/PHPStan/Analyser/nsrt/bug-5357.php - Added baseline entry for instanceof GenericObjectType in TypeCombinator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
When a raw (non-generic) class like
Datagridwas intersected with a generic interface likeDatagridInterface<Proxy>, calling methods on the intersection type returned*NEVER*instead of the expected generic return type.Changes
TypeCombinator::intersect()insrc/Type/TypeCombinator.phpto merge type parameters when intersecting twoGenericObjectTypeinstances of the same class, instead of returningneverphpstan-baseline.neonfor theinstanceof GenericObjectTypeusagetests/PHPStan/Analyser/nsrt/bug-5357.phpRoot cause
When
Datagrid & DatagridInterface<Proxy>was formed (e.g., after anassert($datagrid instanceof Datagrid)), callinggetPager()on the intersection collected return types from both members:Datagrid(raw):PagerInterface<ProxyQueryInterface>(template resolved to its bound)DatagridInterface<Proxy>:PagerInterface<Proxy>(template resolved to concrete type)These were then intersected via
TypeCombinator::intersect(). SincePagerInterface<ProxyQueryInterface>andPagerInterface<Proxy>are the same generic class with different (invariant) type parameters,isSuperTypeOf()returnedno, causing the intersection to incorrectly returnnever.The fix detects when two
GenericObjectTypeinstances of the same class are being intersected and merges their type parameters individually (e.g.,ProxyQueryInterface & Proxy=ProxysinceProxyis a subtype). This producesPagerInterface<Proxy>as expected.Test
Added
tests/PHPStan/Analyser/nsrt/bug-5357.phpwhich reproduces the original issue: aDatagridBuilderreturnsDatagridInterface<Proxy>, thenassert($datagrid instanceof Datagrid)creates the intersection, and$datagrid->getPager()should returnPagerInterface<Proxy>instead of*NEVER*.Fixes phpstan/phpstan#5357