Fix phpstan/phpstan#7976: Array object nested types are not properly identified#5402
Closed
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
Closed
Fix phpstan/phpstan#7976: Array object nested types are not properly identified#5402phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
phpstan-bot wants to merge 1 commit intophpstan:2.1.xfrom
Conversation
…lates - Added generalizeArrayType() to strip HasOffsetValueType accessories from inferred template types for non-covariant generic parameters - The root cause was that array types inferred with hasOffsetValue accessories failed the strict equality check required by invariant template parameters - Rebuilds array types from fundamental properties (key/value types, non-empty, list) to match what users can express in PHPDoc annotations - New regression test in tests/PHPStan/Rules/Methods/data/bug-7976.php
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 returning
new ArrayObject($data)where$datais built up through loop assignments, PHPStan inferredhasOffsetValueaccessories on the inner array types. These accessories made the invariant template type check fail with a confusing error like "should return ArrayObject<int|string, non-empty-array<string, mixed>> but returns ArrayObject<int|string, non-empty-array<string, mixed>>." — where both types display identically but are structurally different internally.Changes
generalizeArrayType()helper insrc/Type/Generic/TemplateTypeHelper.phpthat rebuilds array types from their fundamental properties (key type, value type, non-empty, list), stripping inferred-only accessories likeHasOffsetValueTypegeneralizeInferredTemplateType()for non-covariant template parameterstests/PHPStan/Rules/Methods/data/bug-7976.phpand test method inReturnTypeRuleTest.phpRoot cause
When PHPStan infers template type arguments from constructor calls like
new ArrayObject($data), the inferred TValue retainsHasOffsetValueTypeaccessories from type inference. For invariant template parameters, PHPStan requires exact type equality viaequals(). SinceHasOffsetValueTypecannot be expressed in PHPDoc annotations, the user's declared return type never has these accessories, causing the equality check to always fail — even though the types are fundamentally the same.The fix rebuilds the array type from its interface-level properties (iterable key/value types, non-empty status, list status), effectively stripping accessories that are only added by type inference and cannot be expressed in user-facing type declarations.
Test
Added
testBug7976inReturnTypeRuleTestwith a test data file reproducing the original issue: a method building up array data in a loop with nested foreach and returningnew ArrayObject($data). The test expects no errors (false positive fix).Fixes phpstan/phpstan#7976