Fix #7170: Can not set optional array element to explicit value in generic#4963
Open
phpstan-bot wants to merge 2 commits into2.1.xfrom
Open
Fix #7170: Can not set optional array element to explicit value in generic#4963phpstan-bot wants to merge 2 commits into2.1.xfrom
phpstan-bot wants to merge 2 commits into2.1.xfrom
Conversation
…stant array property - Override setOffsetValueType() and setExistingOffsetValueType() in TemplateConstantArrayType - When the offset exists in the template's bound and the value type is accepted by the bound's value type, return the template type itself - This preserves the template contract: modifying an in-bound offset on a template value still produces a valid template value - New regression test in tests/PHPStan/Rules/Properties/data/bug-7170.php Closes phpstan/phpstan#7170
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 class has a template type bounded by an array shape with optional keys (e.g.,
@template Tdata of array{extension?: array<mixed>}), setting one of those optional keys to a value that matches the bound's value type incorrectly reported a property assignment error: "does not accept array{extension: array{}}". The same code without generics worked fine.Changes
setOffsetValueType()andsetExistingOffsetValueType()insrc/Type/Generic/TemplateConstantArrayType.phpto preserve the template type when the offset being set exists in the bound and the new value is accepted by the bound's value type for that keyisOffsetWithinBound()helper method that checks whether an offset assignment is compatible with the template's boundtests/PHPStan/Rules/Properties/data/bug-7170.phpand test method intests/PHPStan/Rules/Properties/TypesAssignedToPropertiesRuleTest.phpRoot cause
When
setOffsetValueType()was called on aTemplateConstantArrayType, it delegated to the parentConstantArrayType::setOffsetValueType()which returned a plainConstantArrayType, losing the template type wrapper. This concrete type was then checked against the template property type viaTemplateTypeArgumentStrategy::accepts(), which correctly flagged it asmaybe(since a concrete array type is not guaranteed to match all possible template instantiations). However, the check was too conservative for this case: modifying an offset that exists in the template's bound with a value compatible with that bound preserves the template contract.The fix ensures that when the modification is within the template's bound specification, the template type is preserved, so the subsequent acceptance check correctly passes (
TdataacceptsTdata).Test
The regression test creates a generic class with
@template Tdata of array{extension?: array<mixed>}and sets$this->data['extension'] = []inside anisset()guard. The test expects no errors, matching the behavior of the equivalent non-generic class.Fixes phpstan/phpstan#7170
Closes phpstan/phpstan#10172