-
Notifications
You must be signed in to change notification settings - Fork 554
Support integer range keys in constant arrays #4952
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
staabm
wants to merge
24
commits into
phpstan:2.1.x
Choose a base branch
from
staabm:array-keys
base: 2.1.x
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+219
−1
Open
Changes from all commits
Commits
Show all changes
24 commits
Select commit
Hold shift + click to select a range
eddf264
Support integer range keys in constant arrays
staabm d298bcc
Add regression test for #9907
phpstan-bot db91928
Add regression test for #7978
phpstan-bot 73374f5
Add regression test for #2294
phpstan-bot 1fcb7d2
Add regression test for #13000
phpstan-bot 8bc6aab
Add regression test for #13759
phpstan-bot 289fed3
Address review comments
phpstan-bot a02c4ef
Add regression test for #14129
phpstan-bot 24bf9af
Fix CI failures [claude-ci-fix]
phpstan-bot 3091c0b
Address review comments
phpstan-bot 8b21f9e
Update ConstantArrayType.php
staabm 28f1507
Update bug-13000.php
staabm b62a87c
simplify
staabm 642ff6c
Discard changes to CLAUDE.md
staabm b1bb785
Delete tests/PHPStan/Analyser/nsrt/bug-13759.php
staabm 394fcfa
Delete tests/PHPStan/Analyser/nsrt/bug-13000.php
staabm 72c4541
Delete tests/PHPStan/Analyser/nsrt/bug-2294.php
staabm b304060
Update ConstantArrayType.php
staabm 90d9ac9
Update ConstantArrayType.php
staabm d07cfbc
use proper LIMIT
staabm 101f110
Update ConstantArrayType.php
staabm cdaf2f1
Update ConstantArrayType.php
staabm 6ae63c8
fix
staabm b854fbf
Don't overwrite $offsetType
staabm File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -47,6 +47,7 @@ | |
| use PHPStan\Type\Traits\UndecidedComparisonTypeTrait; | ||
| use PHPStan\Type\Type; | ||
| use PHPStan\Type\TypeCombinator; | ||
| use PHPStan\Type\TypeUtils; | ||
| use PHPStan\Type\UnionType; | ||
| use PHPStan\Type\VerbosityLevel; | ||
| use function array_keys; | ||
|
|
@@ -699,6 +700,57 @@ public function getOffsetValueType(Type $offsetType): Type | |
|
|
||
| public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $unionValues = true): Type | ||
| { | ||
| if ($offsetType !== null) { | ||
| $scalarKeyTypes = $offsetType->toArrayKey()->getConstantStrings(); | ||
| if (count($scalarKeyTypes) === 0) { | ||
| $integerRanges = TypeUtils::getIntegerRanges($offsetType); | ||
| if (count($integerRanges) > 0) { | ||
| foreach ($integerRanges as $integerRange) { | ||
| $finiteTypes = $integerRange->getFiniteTypes(); | ||
| if (count($finiteTypes) === 0) { | ||
| break; | ||
| } | ||
|
|
||
| foreach ($finiteTypes as $finiteType) { | ||
| $scalarKeyTypes[] = $finiteType; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // turn into tagged union for more precise results | ||
| if ( | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's unclear to me why
What about Also the |
||
| count($scalarKeyTypes) >= 2 | ||
| && count($scalarKeyTypes) < ConstantArrayTypeBuilder::ARRAY_COUNT_LIMIT | ||
| ) { | ||
| $hasNewKey = false; | ||
| foreach ($scalarKeyTypes as $scalarKeyType) { | ||
| $existingKeyFound = false; | ||
| foreach ($this->keyTypes as $existingKeyType) { | ||
| if ($existingKeyType->getValue() === $scalarKeyType->getValue()) { | ||
| $existingKeyFound = true; | ||
| break; | ||
| } | ||
| } | ||
| if (!$existingKeyFound) { | ||
| $hasNewKey = true; | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| if ($hasNewKey) { | ||
| $arrayTypes = []; | ||
| foreach ($scalarKeyTypes as $scalarKeyType) { | ||
| $builder = ConstantArrayTypeBuilder::createFromConstantArray($this); | ||
| $builder->setOffsetValueType($scalarKeyType, $valueType); | ||
| $arrayTypes[] = $builder->getArray(); | ||
| } | ||
|
|
||
| return TypeCombinator::union(...$arrayTypes); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| $builder = ConstantArrayTypeBuilder::createFromConstantArray($this); | ||
| $builder->setOffsetValueType($offsetType, $valueType); | ||
|
|
||
|
|
||
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| <?php declare(strict_types = 1); | ||
|
|
||
| namespace Bug9907; | ||
|
|
||
| use function PHPStan\Testing\assertType; | ||
|
|
||
| class HelloWorld | ||
| { | ||
| /** | ||
| * @param 'foo'|'bar' $key | ||
| */ | ||
| public function sayHello(string $key): void | ||
| { | ||
| $a = []; | ||
| $a['id'] = null; | ||
| $a[$key] = 'string'; | ||
|
|
||
| assertType("array{id: null, bar: 'string'}|array{id: null, foo: 'string'}", $a); | ||
| } | ||
| } |
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
70 changes: 70 additions & 0 deletions
70
tests/PHPStan/Analyser/nsrt/set-constant-union-offset-on-constant-array.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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| <?php | ||
|
|
||
| namespace SetConstantUnionOffsetOnConstantArray; | ||
|
|
||
| use function PHPStan\Testing\assertType; | ||
|
|
||
| class Foo | ||
| { | ||
|
|
||
| /** | ||
| * @param array{foo: int} $a | ||
| */ | ||
| public function doFoo(array $a): void | ||
| { | ||
| $k = rand(0, 1) ? 'a' : 'b'; | ||
| $a[$k] = 256; | ||
| assertType('array{foo: int, a: 256}|array{foo: int, b: 256}', $a); | ||
| } | ||
|
|
||
| /** | ||
| * @param array{foo: int} $a | ||
| * @param int<1,5> $intRange | ||
| */ | ||
| public function doBar(array $a, $intRange): void | ||
| { | ||
| $a[$intRange] = 256; | ||
| assertType('array{foo: int, 1: 256}|array{foo: int, 2: 256}|array{foo: int, 3: 256}|array{foo: int, 4: 256}|array{foo: int, 5: 256}', $a); | ||
| } | ||
|
|
||
| /** | ||
| * @param array{foo: int} $a | ||
| * @param int<0, max> $intRange | ||
| */ | ||
| public function doInfiniteRange(array $a, $intRange): void | ||
| { | ||
| $a[$intRange] = 256; | ||
| assertType('non-empty-array<\'foo\'|int<0, max>, int>', $a); | ||
| } | ||
|
|
||
| /** | ||
| * @param array{foo: int} $a | ||
| * @param int<0, 5>|int<10, 15> $intRange | ||
| */ | ||
| public function doUnionOfRanges(array $a, $intRange): void | ||
| { | ||
| $a[$intRange] = 256; | ||
| assertType('array{foo: int, 0: 256}|array{foo: int, 10: 256}|array{foo: int, 11: 256}|array{foo: int, 12: 256}|array{foo: int, 13: 256}|array{foo: int, 14: 256}|array{foo: int, 15: 256}|array{foo: int, 1: 256}|array{foo: int, 2: 256}|array{foo: int, 3: 256}|array{foo: int, 4: 256}|array{foo: int, 5: 256}', $a); | ||
| } | ||
|
|
||
| /** | ||
| * @param array{foo: int} $a | ||
| * @param int<0, 3>|int<2, 4> $intRange | ||
| */ | ||
| public function doOverlappingRanges(array $a, $intRange): void | ||
| { | ||
| $a[$intRange] = 256; | ||
| assertType('array{foo: int, 0: 256}|array{foo: int, 1: 256}|array{foo: int, 2: 256}|array{foo: int, 3: 256}|array{foo: int, 4: 256}', $a); | ||
| } | ||
|
|
||
| /** | ||
| * @param array{0: 'a', 1: 'b'} $a | ||
| * @param int<0,1> $intRange | ||
| */ | ||
| public function doExistingKeys(array $a, $intRange): void | ||
| { | ||
| $a[$intRange] = 'c'; | ||
| assertType("array{'a'|'c', 'b'|'c'}", $a); | ||
| } | ||
|
|
||
| } |
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| <?php declare(strict_types = 1); | ||
|
|
||
| namespace Bug7978; | ||
|
|
||
| class Test { | ||
|
|
||
| const FIELD_SETS = [ | ||
| 'basic' => ['username', 'password'], | ||
| 'headers' => ['app_id', 'app_key'], | ||
| ]; | ||
|
|
||
| /** | ||
| * @param array<string, string> $credentials | ||
| */ | ||
| public function acceptCredentials(array $credentials): void | ||
| { | ||
| } | ||
|
|
||
| public function doSomething(): void | ||
| { | ||
| foreach (self::FIELD_SETS as $type => $fields) { | ||
| $credentials = []; | ||
| foreach ($fields as $field) { | ||
| $credentials[$field] = 'fake'; | ||
| } | ||
| $this->acceptCredentials($credentials); | ||
| } | ||
| } | ||
| } |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| <?php declare(strict_types = 1); | ||
|
|
||
| namespace Bug9907Rule; | ||
|
|
||
| class Demo | ||
| { | ||
| /** | ||
| * @phpstan-param array{street: string, city: string} $address1 | ||
| * @phpstan-param array{street: string, city: string} $address2 | ||
| * | ||
| * @phpstan-return array{ | ||
| * street?: array{change_to: string}, | ||
| * city?: array{change_to: string}, | ||
| * variation_count?: int<1, max> | ||
| * } | ||
| */ | ||
| public function diffAddresses(array $address1, array $address2): array | ||
| { | ||
| $addressDifference = array_diff_assoc($address1, $address2); | ||
| $differenceDetails = []; | ||
|
|
||
| foreach ($addressDifference as $name => $differenceValue) { | ||
| $differenceDetails[$name] = [ | ||
| 'change_to' => $differenceValue, | ||
| ]; | ||
| } | ||
|
|
||
| if (!empty(count($differenceDetails))) { | ||
| $differenceDetails['variation_count'] = count($differenceDetails); | ||
| } | ||
|
|
||
| return $differenceDetails; | ||
| } | ||
| } |
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're calling getIntegerRanges on offsetType but previously you worked with
Shouldn't you work every where with
$offsetType->toArrayKey()?that could have impact for things like
int<0, 2>|'3'maybe ?