Fix phpstan/phpstan#11776: Union type of not detected in getter#5357
Open
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
Open
Fix phpstan/phpstan#11776: Union type of not detected in getter#5357phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
Conversation
…esolution - When resolveTemplateTypes() replaces a template type with a standin of the same name and scope, preserve the narrower bound from PHPDoc intersections like (int|string)&TOperation - New regression test in tests/PHPStan/Analyser/nsrt/bug-11776.php - Root cause: ResolvedPropertyReflection called resolveTemplateTypes() which replaced TOperation of int|string with TOperation of scalar, losing the intersection narrowing from the PHPDoc type annotation
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 promoted constructor property has a PHPDoc type containing an intersection with a template type (e.g.,
(int|string)&TOperationwhereTOperation of scalar), accessing the property incorrectly reports a type mismatch with the getter's return type, even though both annotations use the same type expression.The fix preserves narrowed template bounds during template type resolution.
Changes
src/Type/Generic/TemplateTypeHelper.php: InresolveTemplateTypes(), when replacing a template type with a standin that is the same template (same name and scope) but with a wider bound, preserve the narrower bound from the original type. This maintains intersection-based narrowing like(int|string)&TOperationresolving toTOperation of int|string.tests/PHPStan/Analyser/nsrt/bug-11776.php: Regression test with a generic class using a promoted constructor property with an intersection-narrowed template type.Root cause
When PHPStan parses
(int|string)&TOperation(whereTOperation of scalar),TypeCombinator::intersect()correctly narrows the template's bound toint|string. However, when accessing the property,ResolvedPropertyReflection::getReadableType()callsTemplateTypeHelper::resolveTemplateTypes()which replaces the narrowedTOperation of int|stringwith the class's active template standinTOperation of scalar, losing the intersection narrowing.The method return type was not affected because
NodeScopeResolver::getPhpDocs()does not pass return types throughresolveTemplateTypes(), preserving the narrowed bound.The fix adds a check: when the standin is a template type with the same name and scope, and the original type has a strictly narrower bound (which is a subtype of the standin's bound), the narrower bound is preserved in the result.
Test
Added
tests/PHPStan/Analyser/nsrt/bug-11776.phpwhich reproduces the original issue: areadonly class ScalarableChoice<TOperation of scalar>with a promoted constructor parameter typed asclass-string<EnumAsFilterInterface<(int|string)&TOperation>>and a getter returning the same type. The test verifies the property type correctly resolves toTOperation of int|stringinstead ofTOperation of bool|float|int|string.Fixes phpstan/phpstan#11776