-
Notifications
You must be signed in to change notification settings - Fork 574
Fix phpstan/phpstan#8270: False positive on array item modification #5189
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
Changes from 17 commits
f015cdb
c808bac
45ba75c
ec18c1c
4f3bfc2
de6f6df
b0865e9
e281d8c
9e969b8
5460901
f6520d7
9f6d382
9874e91
f6c6d4d
4976107
91c7a78
980a943
03b1809
b7e4706
d015a48
7647ecf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types = 1); | ||
|
|
||
| namespace Bug13623; | ||
|
staabm marked this conversation as resolved.
|
||
|
|
||
| use function PHPStan\Testing\assertType; | ||
|
|
||
| function (array $results): void { | ||
| $customers = []; | ||
|
|
||
| foreach ($results as $row) { | ||
| $customers[$row['customer_id']] ??= []; | ||
| $customers[$row['customer_id']]['orders'] ??= []; | ||
| $customers[$row['customer_id']]['orders'][$row['order_id']] ??= []; | ||
|
|
||
| $customers[$row['customer_id']]['orders'][$row['order_id']]['balance_forward'] ??= 0; | ||
| $customers[$row['customer_id']]['orders'][$row['order_id']]['new_invoice'] ??= 0; | ||
| $customers[$row['customer_id']]['orders'][$row['order_id']]['payments'] ??= 0; | ||
| $customers[$row['customer_id']]['orders'][$row['order_id']]['balance'] ??= $row['order_total']; | ||
| } | ||
|
|
||
| assertType("array<array{orders: array<array{}|array{balance_forward?: 0, new_invoice?: 0, payments?: 0, balance?: mixed}>}>", $customers); | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types = 1); | ||
|
|
||
| namespace Bug13857; | ||
|
|
||
| use function PHPStan\Testing\assertType; | ||
|
|
||
| /** | ||
| * @param array<int, array{state: string}> $array | ||
| */ | ||
| function test(array $array, int $id): void { | ||
| $array[$id]['state'] = 'foo'; | ||
| // only one element was set to 'foo', not all of them. | ||
| assertType("non-empty-array<int, array{state: string}>", $array); | ||
| } | ||
|
|
||
| /** | ||
| * @param array<int, array{state?: string}> $array | ||
| */ | ||
| function testMaybe(array $array, int $id): void { | ||
| $array[$id]['state'] = 'foo'; | ||
| // only one element was set to 'foo', not all of them. | ||
| assertType("non-empty-array<int, array{state?: string}>", $array); | ||
| } | ||
|
|
||
| /** | ||
| * @param array<int, array{state: string|bool}> $array | ||
| */ | ||
| function testUnionValue(array $array, int $id): void { | ||
| $array[$id]['state'] = 'foo'; | ||
| // only one element was set to 'foo', not all of them. | ||
| assertType("non-empty-array<int, array{state: bool|string}>", $array); | ||
| } | ||
|
|
||
| /** | ||
| * @param array<int, array{state: string}|array{foo: int}> $array | ||
| */ | ||
| function testUnionArray(array $array, int $id): void { | ||
| $array[$id]['state'] = 'foo'; | ||
| // only one element was set to 'foo', not all of them. | ||
| assertType("non-empty-array<int, non-empty-array{foo?: int, state?: string}>", $array); | ||
| } | ||
|
|
||
| /** | ||
| * @param array<int, array{state: string}|array{foo: int}> $array | ||
| */ | ||
| function testUnionArrayDifferentType(array $array, int $id): void { | ||
| $array[$id]['state'] = true; | ||
| assertType("non-empty-array<int, array{state: string}|non-empty-array{foo?: int, state?: true}>", $array); | ||
| } | ||
|
|
||
| /** | ||
| * @param array<int, array{state: 'foo'}> $array | ||
| */ | ||
| function testConstantArray(array $array, int $id): void { | ||
| $array[$id]['state'] = 'bar'; | ||
| assertType("non-empty-array<int, array{state: 'bar'}|array{state: 'foo'}>", $array); | ||
| } | ||
|
|
||
| /** | ||
| * @param array<int, array{state: 'foo'}> $array | ||
| */ | ||
| function testConstantArrayNonScalarAssign(array $array, int $id, bool $b): void { | ||
| $array[$id]['state'] = $b; | ||
| assertType("non-empty-array<int, array{state: 'foo'}|array{state: bool}>", $array); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| <?php | ||
|
|
||
| declare(strict_types = 1); | ||
|
staabm marked this conversation as resolved.
|
||
|
|
||
| namespace Bug8270; | ||
|
|
||
| use function PHPStan\Testing\assertType; | ||
|
|
||
| function doFoo() { | ||
| /** @var non-empty-list<array{test: false, value: int}> $list */ | ||
| $list = []; | ||
| $list[0]['test'] = true; | ||
|
|
||
| foreach ($list as $item) { | ||
| assertType('array{test: bool, value: int}', $item); | ||
| if ($item['test']) { | ||
| assertType('true', $item['test']); | ||
| echo $item['value']; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| function doBar() { | ||
| $list = []; | ||
|
|
||
| for ($i = 0; $i < 10; $i++) { | ||
| $list[] = [ | ||
| 'test' => false, | ||
| 'value' => rand(), | ||
| ]; | ||
| } | ||
|
|
||
| if ($list === []) { | ||
| return; | ||
| } | ||
|
|
||
| $k = array_key_first($list); | ||
| assertType('int<0, max>', $k); | ||
| $list[$k]['test'] = true; | ||
|
|
||
| foreach ($list as $item) { | ||
| assertType('array{test: bool, value: int<0, max>}', $item); | ||
| if ($item['test']) { | ||
| echo $item['value']; | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,7 +18,7 @@ public function doFoo(array $percentageIntervals, array $changes): void | |
| assertType('non-empty-array<array{itemsCount: mixed, interval: mixed}>', $intervalResults); | ||
| assertType('array{itemsCount: mixed, interval: mixed}', $intervalResults[$key]); | ||
| $intervalResults[$key]['itemsCount'] += $itemsCount; | ||
| assertType('non-empty-array<array{itemsCount: (array|float|int), interval: mixed}>', $intervalResults); | ||
|
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. I don't see why we previously expected the
Collaborator
Author
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. All tests pass. Summary: The reviewer's comment on No code changes are needed. I was unable to post a reply comment on the PR due to insufficient token permissions — you may want to reply directly acknowledging that the old |
||
| assertType('non-empty-array<array{itemsCount: mixed, interval: mixed}>', $intervalResults); | ||
| assertType('array{itemsCount: (array|float|int), interval: mixed}', $intervalResults[$key]); | ||
| } else { | ||
| assertType('array<array{itemsCount: mixed, interval: mixed}>', $intervalResults); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.