Skip to content

Commit 154bb87

Browse files
Introduce reportCastedArrayKey parameter
1 parent 5b4bf80 commit 154bb87

File tree

8 files changed

+142
-6
lines changed

8 files changed

+142
-6
lines changed

conf/config.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ parameters:
6565
reportStaticMethodSignatures: false
6666
reportWrongPhpDocTypeInVarTag: false
6767
reportAnyTypeWideningInVarTag: false
68+
reportCastedArrayKey: false
6869
reportPossiblyNonexistentGeneralArrayOffset: false
6970
reportPossiblyNonexistentConstantArrayOffset: false
7071
checkMissingOverrideMethodAttribute: false

conf/parametersSchema.neon

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ parametersSchema:
7575
reportStaticMethodSignatures: bool()
7676
reportWrongPhpDocTypeInVarTag: bool()
7777
reportAnyTypeWideningInVarTag: bool()
78+
reportCastedArrayKey: bool()
7879
reportPossiblyNonexistentGeneralArrayOffset: bool()
7980
reportPossiblyNonexistentConstantArrayOffset: bool()
8081
checkMissingOverrideMethodAttribute: bool()

src/Rules/Arrays/AllowedArrayKeysTypes.php

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,15 @@
2121
final class AllowedArrayKeysTypes
2222
{
2323

24-
public static function getType(): Type
24+
public static function getType(bool $strict = false): Type
2525
{
26+
if ($strict) {
27+
return new UnionType([
28+
new IntegerType(),
29+
new StringType(),
30+
]);
31+
}
32+
2633
return new UnionType([
2734
new IntegerType(),
2835
new StringType(),

src/Rules/Arrays/InvalidKeyInArrayDimFetchRule.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ public function __construct(
2626
private RuleLevelHelper $ruleLevelHelper,
2727
#[AutowiredParameter]
2828
private bool $reportMaybes,
29+
#[AutowiredParameter]
30+
private bool $reportCastedArrayKey,
2931
)
3032
{
3133
}
@@ -46,18 +48,19 @@ public function processNode(Node $node, Scope $scope): array
4648
return [];
4749
}
4850

51+
$reportCastedArrayKey = $this->reportCastedArrayKey;
4952
$varType = $this->ruleLevelHelper->findTypeToCheck(
5053
$scope,
5154
$node->var,
5255
'',
53-
static fn (Type $varType): bool => $varType->isArray()->no() || AllowedArrayKeysTypes::getType()->isSuperTypeOf($dimensionType)->yes(),
56+
static fn (Type $varType): bool => $varType->isArray()->no() || AllowedArrayKeysTypes::getType($reportCastedArrayKey)->isSuperTypeOf($dimensionType)->yes(),
5457
)->getType();
5558

5659
if ($varType instanceof ErrorType || $varType->isArray()->no()) {
5760
return [];
5861
}
5962

60-
$isSuperType = AllowedArrayKeysTypes::getType()->isSuperTypeOf($dimensionType);
63+
$isSuperType = AllowedArrayKeysTypes::getType($this->reportCastedArrayKey)->isSuperTypeOf($dimensionType);
6164
if ($isSuperType->yes() || ($isSuperType->maybe() && !$this->reportMaybes)) {
6265
return [];
6366
}

src/Rules/Arrays/InvalidKeyInArrayItemRule.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ final class InvalidKeyInArrayItemRule implements Rule
2222
public function __construct(
2323
#[AutowiredParameter]
2424
private bool $reportMaybes,
25+
#[AutowiredParameter]
26+
private bool $reportCastedArrayKey,
2527
)
2628
{
2729
}
@@ -38,7 +40,7 @@ public function processNode(Node $node, Scope $scope): array
3840
}
3941

4042
$dimensionType = $scope->getType($node->key);
41-
$isSuperType = AllowedArrayKeysTypes::getType()->isSuperTypeOf($dimensionType);
43+
$isSuperType = AllowedArrayKeysTypes::getType($this->reportCastedArrayKey)->isSuperTypeOf($dimensionType);
4244
if ($isSuperType->no()) {
4345
return [
4446
RuleErrorBuilder::message(

tests/PHPStan/Rules/Arrays/InvalidKeyInArrayDimFetchRuleTest.php

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@
1313
class InvalidKeyInArrayDimFetchRuleTest extends RuleTestCase
1414
{
1515

16+
private bool $reportCastedArrayKey = false;
17+
1618
protected function getRule(): Rule
1719
{
1820
$ruleLevelHelper = new RuleLevelHelper(self::createReflectionProvider(), true, false, true, false, false, false, true);
19-
return new InvalidKeyInArrayDimFetchRule($ruleLevelHelper, true);
21+
return new InvalidKeyInArrayDimFetchRule($ruleLevelHelper, true, $this->reportCastedArrayKey);
2022
}
2123

2224
public function testInvalidKey(): void
@@ -61,6 +63,69 @@ public function testInvalidKey(): void
6163
]);
6264
}
6365

66+
public function testInvalidKeyReportingCastedArrayKey(): void
67+
{
68+
$this->reportCastedArrayKey = true;
69+
$this->analyse([__DIR__ . '/data/invalid-key-array-dim-fetch.php'], [
70+
[
71+
'Invalid array key type null.',
72+
6,
73+
],
74+
[
75+
'Invalid array key type DateTimeImmutable.',
76+
7,
77+
],
78+
[
79+
'Invalid array key type array.',
80+
8,
81+
],
82+
[
83+
'Invalid array key type float.',
84+
10,
85+
],
86+
[
87+
'Invalid array key type true.',
88+
12,
89+
],
90+
[
91+
'Invalid array key type false.',
92+
13,
93+
],
94+
[
95+
'Possibly invalid array key type string|null.',
96+
17,
97+
],
98+
[
99+
'Possibly invalid array key type stdClass|string.',
100+
24,
101+
],
102+
[
103+
'Invalid array key type DateTimeImmutable.',
104+
31,
105+
],
106+
[
107+
'Invalid array key type DateTimeImmutable.',
108+
45,
109+
],
110+
[
111+
'Invalid array key type DateTimeImmutable.',
112+
46,
113+
],
114+
[
115+
'Invalid array key type DateTimeImmutable.',
116+
47,
117+
],
118+
[
119+
'Invalid array key type stdClass.',
120+
47,
121+
],
122+
[
123+
'Invalid array key type DateTimeImmutable.',
124+
48,
125+
],
126+
]);
127+
}
128+
64129
#[RequiresPhp('>= 8.1')]
65130
public function testBug6315(): void
66131
{
@@ -92,4 +157,20 @@ public function testBug6315(): void
92157
]);
93158
}
94159

160+
public function testUnsetFalseKey(): void
161+
{
162+
$this->reportCastedArrayKey = true;
163+
164+
$this->analyse([__DIR__ . '/data/unset-false-key.php'], [
165+
[
166+
'Invalid array key type false.',
167+
6,
168+
],
169+
[
170+
'Invalid array key type false.',
171+
13,
172+
],
173+
]);
174+
}
175+
95176
}

tests/PHPStan/Rules/Arrays/InvalidKeyInArrayItemRuleTest.php

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@
1212
class InvalidKeyInArrayItemRuleTest extends RuleTestCase
1313
{
1414

15+
private bool $reportCastedArrayKey = false;
16+
1517
protected function getRule(): Rule
1618
{
17-
return new InvalidKeyInArrayItemRule(true);
19+
return new InvalidKeyInArrayItemRule(true, $this->reportCastedArrayKey);
1820
}
1921

2022
public function testInvalidKey(): void
@@ -35,6 +37,29 @@ public function testInvalidKey(): void
3537
]);
3638
}
3739

40+
public function testInvalidKeyReportingCastedArrayKey(): void
41+
{
42+
$this->reportCastedArrayKey = true;
43+
$this->analyse([__DIR__ . '/data/invalid-key-array-item.php'], [
44+
[
45+
'Invalid array key type null.',
46+
12,
47+
],
48+
[
49+
'Invalid array key type DateTimeImmutable.',
50+
13,
51+
],
52+
[
53+
'Invalid array key type array.',
54+
14,
55+
],
56+
[
57+
'Possibly invalid array key type stdClass|string.',
58+
15,
59+
],
60+
]);
61+
}
62+
3863
public function testInvalidKeyInList(): void
3964
{
4065
$this->analyse([__DIR__ . '/data/invalid-key-list.php'], [
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace UnsetFalseKey;
4+
5+
/** @var array<int, int> $data */
6+
unset($data[false]);
7+
8+
function test_remove_element(): void {
9+
$modified = [1, 4, 6, 8];
10+
11+
// this would happen in the SUT
12+
unset($modified[array_search(4, $modified, true)]);
13+
unset($modified[array_search(5, $modified, true)]); // bug is here - will unset key `0` by accident
14+
15+
assert([1, 6, 8] === $modified); // actually is [6, 8]
16+
}

0 commit comments

Comments
 (0)