Fix phpstan/phpstan#11705: Value of a variable is forgotten in case where it ought to be known with certainty#5191
Open
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
Conversation
…h after narrowing - In MutatingScope::resolveType(), when an ArrayDimFetch has a stored expression type and its dim has been narrowed to a constant scalar value, recompute the offset value type from the current array and dim types - Only replace the stored type when the recomputed type is strictly more specific - New regression test in tests/PHPStan/Analyser/nsrt/bug-11705.php - Root cause: array_key_exists with non-constant key stored a broad type for the ArrayDimFetch expression, which was not updated when the key was later narrowed (e.g., in a switch case)
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
array_key_exists($tag, $theInput)is called with a non-constant key$tag, PHPStan stores the full iterable value type for the expression$theInput[$tag]. If$tagis later narrowed (e.g., in aswitch/case), the stored expression type is not updated, causing PHPStan to report incorrect errors because it uses the broad stored type instead of recomputing based on the narrowed key.Changes
src/Analyser/MutatingScope.phpinresolveType(): when resolving anArrayDimFetchexpression that has a stored expression type, and the dim has been narrowed to a constant scalar value, recompute the offset value type and use it if it is strictly more specific than the stored typetests/PHPStan/Analyser/nsrt/bug-11705.phpRoot cause
ArrayKeyExistsFunctionTypeSpecifyingExtensionstores the fullgetIterableValueType()(e.g.,string|array<int,string>) for$theInput[$tag]when the key is non-constant. This stored type takes precedence inresolveType()over computing the type fresh. When$tagis later narrowed to a constant like'name'inside a switch case, the stored broad type is returned instead of the specificstringthatgetOffsetValueType(ConstantStringType('name'))would produce.The fix adds a check in
resolveType()that, forArrayDimFetchnodes with stored expression types, recomputes the offset value type when the dim is a constant scalar. If the recomputed type is strictly more specific (stored type is a supertype but not vice versa), the more specific type is used.Test
Added
tests/PHPStan/Analyser/nsrt/bug-11705.phpwhich reproduces the original issue: a shaped arrayarray{'name':string,'owners':array<int,string>}accessed with$theInput[$tag]inside acase 'name':block. Asserts that:$tagis correctly narrowed to'name'$theInput[$tag]resolves tostring(notstring|array<int,string>)if ($tag === 'name')checkFixes phpstan/phpstan#11705