Fix #13526: False positive with array_key_exists and union types#5103
Open
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
Open
Fix #13526: False positive with array_key_exists and union types#5103phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
Conversation
- Fixed optimization in MutatingScope::addTypeToExpression that skipped storing expression types for ArrayDimFetch when the computed type was already correct, causing hasExpressionType() to return false - Added regression test for the false positive - Root cause: the performance optimization early-returned without checking if the expression type was already stored in expressionTypes Closes phpstan/phpstan#13526
Contributor
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
Fixes a false positive where
array_key_exists($item, $map)with a union type array ($map = $a ? $map1 : $map2) still reported "Offset ... might not exist" when accessing$map[$item]inside the truthy branch.Changes
src/Analyser/MutatingScope.phpline 3683: Added&& $this->hasExpressionType($expr)->yes()to the performance optimization condition inaddTypeToExpression()tests/PHPStan/Rules/Arrays/data/bug-13526.phpand test methodtestBug13526inNonexistentOffsetInArrayDimFetchRuleTestRoot cause
When
array_key_exists($item, $map)is processed as a truthy condition, theArrayKeyExistsFunctionTypeSpecifyingExtensioncreates aSpecifiedTypesthat includes a sure type for$map[$item]with the correct value type (1|2).During
filterBySpecifiedTypes, this sure type is applied viaaddTypeToExpression(). However,addTypeToExpression()has a performance optimization that returns early (without callingspecifyExpressionType()) when the intersected type equals the already-computed type and is a constant scalar value. The problem is that forArrayDimFetchexpressions, the type is computed on-the-fly viagetTypeFromArrayDimFetch()without being stored inexpressionTypes. So the optimization returned early, and the expression type was never stored.Later,
NonexistentOffsetInArrayDimFetchRulechecks$scope->hasExpressionType($node)->yes()to determine if the dim fetch was explicitly narrowed (e.g., byarray_key_exists). Since the expression type was never stored, this returnedno, and the rule proceeded to report the false positive.The fix adds
$this->hasExpressionType($expr)->yes()to the optimization condition, ensuring the early return only happens when the expression type is already stored. This preserves the optimization for the common case (variables and already-stored expressions) while fixing the issue for computed-but-not-stored expressions likeArrayDimFetch.Test
Added
tests/PHPStan/Rules/Arrays/data/bug-13526.phpwith a reproduction case: two constant arrays selected via a ternary, followed byarray_key_exists+ access. The test expects no errors, confirming the false positive is fixed.Fixes phpstan/phpstan#13526