Skip to content

Commit 89ee746

Browse files
VincentLangletondrejmirtes
authored andcommitted
Fix
1 parent 4972fd9 commit 89ee746

File tree

6 files changed

+111
-44
lines changed

6 files changed

+111
-44
lines changed

src/Rules/Arrays/NonexistentOffsetInArrayDimFetchCheck.php

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
use PHPStan\Rules\RuleLevelHelper;
1313
use PHPStan\Type\BenevolentUnionType;
1414
use PHPStan\Type\ErrorType;
15+
use PHPStan\Type\NeverType;
1516
use PHPStan\Type\Type;
17+
use PHPStan\Type\TypeCombinator;
1618
use PHPStan\Type\TypeUtils;
1719
use PHPStan\Type\VerbosityLevel;
1820
use function count;
@@ -60,6 +62,14 @@ public function check(
6062
}
6163

6264
if ($type->hasOffsetValueType($dimType)->no()) {
65+
if ($type->isArray()->yes()) {
66+
$validArrayDimType = TypeCombinator::intersect(AllowedArrayKeysTypes::getType(), $dimType);
67+
if ($validArrayDimType instanceof NeverType) {
68+
// Already reported by InvalidKeyInArrayDimFetchRule
69+
return [];
70+
}
71+
}
72+
6373
return [
6474
RuleErrorBuilder::message(sprintf('Offset %s does not exist on %s.', $dimType->describe(count($dimType->getConstantStrings()) > 0 ? VerbosityLevel::precise() : VerbosityLevel::value()), $type->describe(VerbosityLevel::value())))
6575
->identifier('offsetAccess.notFound')
@@ -76,28 +86,32 @@ public function check(
7686
$flattenedTypes = TypeUtils::flattenTypes($type);
7787
}
7888

89+
$validArrayDimType = TypeCombinator::intersect(AllowedArrayKeysTypes::getType(), $dimType);
90+
7991
foreach ($flattenedTypes as $innerType) {
92+
$dimTypeToCheck = $innerType->isArray()->yes() ? $validArrayDimType : $dimType;
93+
8094
if (
8195
$this->reportPossiblyNonexistentGeneralArrayOffset
8296
&& $innerType->isArray()->yes()
8397
&& !$innerType->isConstantArray()->yes()
84-
&& !$innerType->hasOffsetValueType($dimType)->yes()
98+
&& !$innerType->hasOffsetValueType($dimTypeToCheck)->yes()
8599
) {
86100
$report = true;
87101
break;
88102
}
89103
if (
90104
$this->reportPossiblyNonexistentConstantArrayOffset
91105
&& $innerType->isConstantArray()->yes()
92-
&& !$innerType->hasOffsetValueType($dimType)->yes()
106+
&& !$innerType->hasOffsetValueType($dimTypeToCheck)->yes()
93107
) {
94108
$report = true;
95109
break;
96110
}
97-
if ($dimType instanceof BenevolentUnionType) {
98-
$flattenedInnerTypes = [$dimType];
111+
if ($dimTypeToCheck instanceof BenevolentUnionType) {
112+
$flattenedInnerTypes = [$dimTypeToCheck];
99113
} else {
100-
$flattenedInnerTypes = TypeUtils::flattenTypes($dimType);
114+
$flattenedInnerTypes = TypeUtils::flattenTypes($dimTypeToCheck);
101115
}
102116
foreach ($flattenedInnerTypes as $innerDimType) {
103117
if (

tests/PHPStan/Levels/data/arrayOffsetAccess-3.json

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,5 @@
33
"message": "Invalid array key type DateTimeImmutable.",
44
"line": 17,
55
"ignorable": true
6-
},
7-
{
8-
"message": "Offset DateTimeImmutable does not exist on array.",
9-
"line": 17,
10-
"ignorable": true
116
}
127
]

tests/PHPStan/Levels/data/arrayOffsetAccess-7.json

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,24 @@
11
[
2-
{
3-
"message": "Offset int|object might not exist on array.",
4-
"line": 19,
5-
"ignorable": true
6-
},
72
{
83
"message": "Possibly invalid array key type int|object.",
94
"line": 19,
105
"ignorable": true
116
},
12-
{
13-
"message": "Offset object|null might not exist on array.",
14-
"line": 20,
15-
"ignorable": true
16-
},
177
{
188
"message": "Possibly invalid array key type object|null.",
199
"line": 20,
2010
"ignorable": true
2111
},
22-
{
23-
"message": "Offset DateTimeImmutable might not exist on array|ArrayAccess.",
24-
"line": 26,
25-
"ignorable": true
26-
},
2712
{
2813
"message": "Possibly invalid array key type DateTimeImmutable.",
2914
"line": 26,
3015
"ignorable": true
3116
},
32-
{
33-
"message": "Offset int|object might not exist on array|ArrayAccess.",
34-
"line": 28,
35-
"ignorable": true
36-
},
3717
{
3818
"message": "Possibly invalid array key type int|object.",
3919
"line": 28,
4020
"ignorable": true
4121
},
42-
{
43-
"message": "Offset object|null might not exist on array|ArrayAccess.",
44-
"line": 29,
45-
"ignorable": true
46-
},
4722
{
4823
"message": "Possibly invalid array key type object|null.",
4924
"line": 29,

tests/PHPStan/Levels/data/stringOffsetAccess-3.json

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,5 @@
1818
"message": "Invalid array key type stdClass.",
1919
"line": 59,
2020
"ignorable": true
21-
},
22-
{
23-
"message": "Offset stdClass does not exist on array{baz: 21}|array{foo: 17, bar: 19}.",
24-
"line": 59,
25-
"ignorable": true
2621
}
2722
]

tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -949,10 +949,6 @@ public function testBugObject(): void
949949
'Offset int|object does not exist on array{baz: 21}|array{foo: 17, bar: 19}.',
950950
12,
951951
],
952-
[
953-
'Offset object does not exist on array<string, int>.',
954-
21,
955-
],
956952
]);
957953
}
958954

@@ -1049,6 +1045,55 @@ public function testBug13538(): void
10491045
]);
10501046
}
10511047

1048+
public function testPR4385(): void
1049+
{
1050+
$this->reportPossiblyNonexistentGeneralArrayOffset = true;
1051+
$this->reportPossiblyNonexistentConstantArrayOffset = true;
1052+
1053+
$this->analyse([__DIR__ . '/data/pr-4385.php'], [
1054+
[
1055+
'Offset int might not exist on array<int>.',
1056+
24,
1057+
],
1058+
[
1059+
'Offset string might not exist on array<int>.',
1060+
25,
1061+
],
1062+
[
1063+
'Offset array<int>|int might not exist on array<int>.',
1064+
28,
1065+
],
1066+
[
1067+
'Offset array<int>|string might not exist on array<int>.',
1068+
29,
1069+
],
1070+
[
1071+
'Offset 0|array<int> might not exist on array<int>.',
1072+
30,
1073+
],
1074+
[
1075+
'Offset int might not exist on array{string}.',
1076+
33,
1077+
],
1078+
[
1079+
'Offset string might not exist on array{string}.',
1080+
34,
1081+
],
1082+
[
1083+
'Offset array<int>|int might not exist on array{string}.',
1084+
37,
1085+
],
1086+
[
1087+
'Offset array<int>|string might not exist on array{string}.',
1088+
38,
1089+
],
1090+
[
1091+
'Offset array<int>|int might not exist on array<int>|string.',
1092+
41,
1093+
],
1094+
]);
1095+
}
1096+
10521097
public function testBug12805(): void
10531098
{
10541099
$this->reportPossiblyNonexistentGeneralArrayOffset = true;
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
namespace Pr4385;
4+
5+
class Foo
6+
{
7+
/**
8+
* @param array<int> $array
9+
* @param int $int
10+
* @param string $string
11+
* @param object $object
12+
* @param array{0: string} $constantArray
13+
*
14+
* @return void
15+
*/
16+
public function test($array, $int, $string, $object, $constantArray)
17+
{
18+
$arrayOrObject = rand(0, 1) ? $array : $object;
19+
$arrayOrInt = rand(0, 1) ? $array : $int;
20+
$arrayOrString = rand(0, 1) ? $array : $string;
21+
$arrayOrZero = rand(0, 1) ? $array : 0;
22+
23+
$array[$array];
24+
$array[$int];
25+
$array[$string];
26+
$array[$object]; // Reported by InvalidKeyInArrayDimFetchRule
27+
$array[$arrayOrObject]; // Reported by InvalidKeyInArrayDimFetchRule
28+
$array[$arrayOrInt];
29+
$array[$arrayOrString];
30+
$array[$arrayOrZero];
31+
32+
$constantArray[$array];
33+
$constantArray[$int];
34+
$constantArray[$string];
35+
$constantArray[$object]; // Reported by InvalidKeyInArrayDimFetchRule
36+
$constantArray[$arrayOrObject]; // Reported by InvalidKeyInArrayDimFetchRule
37+
$constantArray[$arrayOrInt];
38+
$constantArray[$arrayOrString];
39+
$constantArray[$arrayOrZero]; // Reported by InvalidKeyInArrayDimFetchRule
40+
41+
$arrayOrString[$arrayOrInt];
42+
}
43+
}

0 commit comments

Comments
 (0)