Skip to content

Commit e7da4dc

Browse files
Respect rule level in InvalidKeyInArrayDimFetchRule
1 parent 08b44e8 commit e7da4dc

10 files changed

+381
-10
lines changed

src/Rules/Arrays/InvalidKeyInArrayDimFetchRule.php

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
use PHPStan\Rules\RuleErrorBuilder;
1111
use PHPStan\Rules\RuleLevelHelper;
1212
use PHPStan\Type\ErrorType;
13-
use PHPStan\Type\MixedType;
1413
use PHPStan\Type\Type;
1514
use PHPStan\Type\VerbosityLevel;
1615
use function sprintf;
@@ -41,19 +40,29 @@ public function processNode(Node $node, Scope $scope): array
4140
return [];
4241
}
4342

44-
$dimensionType = $scope->getType($node->dim);
45-
if ($dimensionType instanceof MixedType) {
46-
return [];
47-
}
48-
4943
$varType = $this->ruleLevelHelper->findTypeToCheck(
5044
$scope,
5145
$node->var,
5246
'',
53-
static fn (Type $varType): bool => $varType->isArray()->no() || AllowedArrayKeysTypes::getType()->isSuperTypeOf($dimensionType)->yes(),
47+
static fn (Type $varType): bool => $varType->isArray()->no(),
5448
)->getType();
5549

56-
if ($varType instanceof ErrorType || $varType->isArray()->no()) {
50+
if ($varType instanceof ErrorType) {
51+
return [];
52+
}
53+
54+
$isArray = $varType->isArray();
55+
if ($isArray->no() || ($isArray->maybe() && !$this->reportMaybes)) {
56+
return [];
57+
}
58+
59+
$dimensionType = $this->ruleLevelHelper->findTypeToCheck(
60+
$scope,
61+
$node->dim,
62+
'',
63+
static fn (Type $dimType): bool => AllowedArrayKeysTypes::getType()->isSuperTypeOf($dimType)->yes(),
64+
)->getType();
65+
if ($dimensionType instanceof ErrorType) {
5766
return [];
5867
}
5968

@@ -64,7 +73,11 @@ public function processNode(Node $node, Scope $scope): array
6473

6574
return [
6675
RuleErrorBuilder::message(
67-
sprintf('%s array key type %s.', $isSuperType->no() ? 'Invalid' : 'Possibly invalid', $dimensionType->describe(VerbosityLevel::typeOnly())),
76+
sprintf(
77+
'%s array key type %s.',
78+
$isArray->yes() && $isSuperType->no() ? 'Invalid' : 'Possibly invalid',
79+
$dimensionType->describe(VerbosityLevel::typeOnly()),
80+
),
6881
)->identifier('offsetAccess.invalidOffset')->build(),
6982
];
7083
}

src/Rules/RuleLevelHelper.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,11 @@ private function findTypeToCheckImplementation(
190190
bool $isTopLevel = false,
191191
): FoundTypeResult
192192
{
193-
if (!$this->checkNullables && !$type->isNull()->yes()) {
193+
if (
194+
!$this->checkNullables
195+
&& !$type->isNull()->yes()
196+
&& !$unionTypeCriteriaCallback(new NullType())
197+
) {
194198
$type = TypeCombinator::removeNull($type);
195199
}
196200

tests/PHPStan/Levels/LevelsIntegrationTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ public static function dataTopics(): array
4141
['arrayDestructuring'],
4242
['listType'],
4343
['missingTypes'],
44+
['arrayOffsetAccess'],
4445
];
4546
if (PHP_VERSION_ID >= 80300) {
4647
$topics[] = ['constantAccesses83'];
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
[
2+
{
3+
"message": "Possibly invalid array key type mixed.",
4+
"line": 22,
5+
"ignorable": true
6+
},
7+
{
8+
"message": "Possibly invalid array key type mixed.",
9+
"line": 31,
10+
"ignorable": true
11+
},
12+
{
13+
"message": "Cannot access offset 42 on mixed.",
14+
"line": 42,
15+
"ignorable": true
16+
},
17+
{
18+
"message": "Cannot access offset null on mixed.",
19+
"line": 43,
20+
"ignorable": true
21+
},
22+
{
23+
"message": "Cannot access offset DateTimeImmutable on mixed.",
24+
"line": 44,
25+
"ignorable": true
26+
},
27+
{
28+
"message": "Cannot access offset int|null on mixed.",
29+
"line": 45,
30+
"ignorable": true
31+
},
32+
{
33+
"message": "Cannot access offset int|object on mixed.",
34+
"line": 46,
35+
"ignorable": true
36+
},
37+
{
38+
"message": "Cannot access offset object|null on mixed.",
39+
"line": 47,
40+
"ignorable": true
41+
},
42+
{
43+
"message": "Cannot access offset mixed on mixed.",
44+
"line": 48,
45+
"ignorable": true
46+
},
47+
{
48+
"message": "Cannot access offset mixed on mixed.",
49+
"line": 49,
50+
"ignorable": true
51+
}
52+
]
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[
2+
{
3+
"message": "Invalid array key type DateTimeImmutable.",
4+
"line": 17,
5+
"ignorable": true
6+
}
7+
]
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
[
2+
{
3+
"message": "Expression \"$a[42]\" on a separate line does not do anything.",
4+
"line": 15,
5+
"ignorable": true
6+
},
7+
{
8+
"message": "Expression \"$a[null]\" on a separate line does not do anything.",
9+
"line": 16,
10+
"ignorable": true
11+
},
12+
{
13+
"message": "Expression \"$a[$intOrNull]\" on a separate line does not do anything.",
14+
"line": 18,
15+
"ignorable": true
16+
},
17+
{
18+
"message": "Expression \"$a[$objectOrInt]\" on a separate line does not do anything.",
19+
"line": 19,
20+
"ignorable": true
21+
},
22+
{
23+
"message": "Expression \"$a[$objectOrNull]\" on a separate line does not do anything.",
24+
"line": 20,
25+
"ignorable": true
26+
},
27+
{
28+
"message": "Expression \"$a[$explicitlyMixed]\" on a separate line does not do anything.",
29+
"line": 21,
30+
"ignorable": true
31+
},
32+
{
33+
"message": "Expression \"$a[$implicitlyMixed]\" on a separate line does not do anything.",
34+
"line": 22,
35+
"ignorable": true
36+
},
37+
{
38+
"message": "Expression \"$arrayOrObject[42]\" on a separate line does not do anything.",
39+
"line": 24,
40+
"ignorable": true
41+
},
42+
{
43+
"message": "Expression \"$arrayOrObject[null]\" on a separate line does not do anything.",
44+
"line": 25,
45+
"ignorable": true
46+
},
47+
{
48+
"message": "Expression \"$arrayOrObject[$intOrNull]\" on a separate line does not do anything.",
49+
"line": 27,
50+
"ignorable": true
51+
},
52+
{
53+
"message": "Expression \"$arrayOrObject[$objectOrInt]\" on a separate line does not do anything.",
54+
"line": 28,
55+
"ignorable": true
56+
},
57+
{
58+
"message": "Expression \"$arrayOrObject[$objectOrNull]\" on a separate line does not do anything.",
59+
"line": 29,
60+
"ignorable": true
61+
},
62+
{
63+
"message": "Expression \"$arrayOrObject[$explicitlyMixed]\" on a separate line does not do anything.",
64+
"line": 30,
65+
"ignorable": true
66+
},
67+
{
68+
"message": "Expression \"$arrayOrObject[$implicitlyMixed]\" on a separate line does not do anything.",
69+
"line": 31,
70+
"ignorable": true
71+
},
72+
{
73+
"message": "Expression \"$explicitlyMixed[42]\" on a separate line does not do anything.",
74+
"line": 33,
75+
"ignorable": true
76+
},
77+
{
78+
"message": "Expression \"$explicitlyMixed[null]\" on a separate line does not do anything.",
79+
"line": 34,
80+
"ignorable": true
81+
},
82+
{
83+
"message": "Expression \"$explicitlyMixed[$intOrNull]\" on a separate line does not do anything.",
84+
"line": 36,
85+
"ignorable": true
86+
},
87+
{
88+
"message": "Expression \"$explicitlyMixed[$objectOrInt]\" on a separate line does not do anything.",
89+
"line": 37,
90+
"ignorable": true
91+
},
92+
{
93+
"message": "Expression \"$explicitlyMixed[$objectOrNull]\" on a separate line does not do anything.",
94+
"line": 38,
95+
"ignorable": true
96+
},
97+
{
98+
"message": "Expression \"$explicitlyMixed[$explicitlyMixed]\" on a separate line does not do anything.",
99+
"line": 39,
100+
"ignorable": true
101+
},
102+
{
103+
"message": "Expression \"$explicitlyMixed[$implicitlyMixed]\" on a separate line does not do anything.",
104+
"line": 40,
105+
"ignorable": true
106+
},
107+
{
108+
"message": "Expression \"$implicitlyMixed[42]\" on a separate line does not do anything.",
109+
"line": 42,
110+
"ignorable": true
111+
},
112+
{
113+
"message": "Expression \"$implicitlyMixed[null]\" on a separate line does not do anything.",
114+
"line": 43,
115+
"ignorable": true
116+
},
117+
{
118+
"message": "Expression \"$implicitlyMixed[$intOrNull]\" on a separate line does not do anything.",
119+
"line": 45,
120+
"ignorable": true
121+
},
122+
{
123+
"message": "Expression \"$implicitlyMixed[$objectOrInt]\" on a separate line does not do anything.",
124+
"line": 46,
125+
"ignorable": true
126+
},
127+
{
128+
"message": "Expression \"$implicitlyMixed[$objectOrNull]\" on a separate line does not do anything.",
129+
"line": 47,
130+
"ignorable": true
131+
},
132+
{
133+
"message": "Expression \"$implicitlyMixed[$explicitlyMixed]\" on a separate line does not do anything.",
134+
"line": 48,
135+
"ignorable": true
136+
},
137+
{
138+
"message": "Expression \"$implicitlyMixed[$implicitlyMixed]\" on a separate line does not do anything.",
139+
"line": 49,
140+
"ignorable": true
141+
}
142+
]
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
[
2+
{
3+
"message": "Method Levels\\ArrayOffsetAccess\\Foo::test() has parameter $a with no value type specified in iterable type array.",
4+
"line": 13,
5+
"ignorable": true
6+
},
7+
{
8+
"message": "Method Levels\\ArrayOffsetAccess\\Foo::test() has parameter $arrayOrObject with generic interface ArrayAccess but does not specify its types: TKey, TValue",
9+
"line": 13,
10+
"ignorable": true
11+
},
12+
{
13+
"message": "Method Levels\\ArrayOffsetAccess\\Foo::test() has parameter $arrayOrObject with no value type specified in iterable type array.",
14+
"line": 13,
15+
"ignorable": true
16+
},
17+
{
18+
"message": "Method Levels\\ArrayOffsetAccess\\Foo::test() has parameter $implicitlyMixed with no type specified.",
19+
"line": 13,
20+
"ignorable": true
21+
}
22+
]
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
[
2+
{
3+
"message": "Possibly invalid array key type int|object.",
4+
"line": 19,
5+
"ignorable": true
6+
},
7+
{
8+
"message": "Possibly invalid array key type object|null.",
9+
"line": 20,
10+
"ignorable": true
11+
},
12+
{
13+
"message": "Possibly invalid array key type DateTimeImmutable.",
14+
"line": 26,
15+
"ignorable": true
16+
},
17+
{
18+
"message": "Possibly invalid array key type int|object.",
19+
"line": 28,
20+
"ignorable": true
21+
},
22+
{
23+
"message": "Possibly invalid array key type object|null.",
24+
"line": 29,
25+
"ignorable": true
26+
}
27+
]
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
[
2+
{
3+
"message": "Possibly invalid array key type mixed.",
4+
"line": 21,
5+
"ignorable": true
6+
},
7+
{
8+
"message": "Possibly invalid array key type mixed.",
9+
"line": 30,
10+
"ignorable": true
11+
},
12+
{
13+
"message": "Cannot access offset 42 on mixed.",
14+
"line": 33,
15+
"ignorable": true
16+
},
17+
{
18+
"message": "Cannot access offset null on mixed.",
19+
"line": 34,
20+
"ignorable": true
21+
},
22+
{
23+
"message": "Cannot access offset DateTimeImmutable on mixed.",
24+
"line": 35,
25+
"ignorable": true
26+
},
27+
{
28+
"message": "Cannot access offset int|null on mixed.",
29+
"line": 36,
30+
"ignorable": true
31+
},
32+
{
33+
"message": "Cannot access offset int|object on mixed.",
34+
"line": 37,
35+
"ignorable": true
36+
},
37+
{
38+
"message": "Cannot access offset object|null on mixed.",
39+
"line": 38,
40+
"ignorable": true
41+
},
42+
{
43+
"message": "Cannot access offset mixed on mixed.",
44+
"line": 39,
45+
"ignorable": true
46+
},
47+
{
48+
"message": "Cannot access offset mixed on mixed.",
49+
"line": 40,
50+
"ignorable": true
51+
}
52+
]

0 commit comments

Comments
 (0)