Skip to content

Commit fe3f932

Browse files
Fix
1 parent 4972fd9 commit fe3f932

File tree

3 files changed

+112
-5
lines changed

3 files changed

+112
-5
lines changed

src/Rules/Arrays/NonexistentOffsetInArrayDimFetchCheck.php

Lines changed: 20 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,15 @@ 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+
73+
6374
return [
6475
RuleErrorBuilder::message(sprintf('Offset %s does not exist on %s.', $dimType->describe(count($dimType->getConstantStrings()) > 0 ? VerbosityLevel::precise() : VerbosityLevel::value()), $type->describe(VerbosityLevel::value())))
6576
->identifier('offsetAccess.notFound')
@@ -76,28 +87,32 @@ public function check(
7687
$flattenedTypes = TypeUtils::flattenTypes($type);
7788
}
7889

90+
$validArrayDimType = TypeCombinator::intersect(AllowedArrayKeysTypes::getType(), $dimType);
91+
7992
foreach ($flattenedTypes as $innerType) {
93+
$dimTypeToCheck = $innerType->isArray()->yes() ? $validArrayDimType : $dimType;
94+
8095
if (
8196
$this->reportPossiblyNonexistentGeneralArrayOffset
8297
&& $innerType->isArray()->yes()
8398
&& !$innerType->isConstantArray()->yes()
84-
&& !$innerType->hasOffsetValueType($dimType)->yes()
99+
&& !$innerType->hasOffsetValueType($dimTypeToCheck)->yes()
85100
) {
86101
$report = true;
87102
break;
88103
}
89104
if (
90105
$this->reportPossiblyNonexistentConstantArrayOffset
91106
&& $innerType->isConstantArray()->yes()
92-
&& !$innerType->hasOffsetValueType($dimType)->yes()
107+
&& !$innerType->hasOffsetValueType($dimTypeToCheck)->yes()
93108
) {
94109
$report = true;
95110
break;
96111
}
97-
if ($dimType instanceof BenevolentUnionType) {
98-
$flattenedInnerTypes = [$dimType];
112+
if ($dimTypeToCheck instanceof BenevolentUnionType) {
113+
$flattenedInnerTypes = [$dimTypeToCheck];
99114
} else {
100-
$flattenedInnerTypes = TypeUtils::flattenTypes($dimType);
115+
$flattenedInnerTypes = TypeUtils::flattenTypes($dimTypeToCheck);
101116
}
102117
foreach ($flattenedInnerTypes as $innerDimType) {
103118
if (

tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,6 +1049,55 @@ public function testBug13538(): void
10491049
]);
10501050
}
10511051

1052+
public function testPR4385(): void
1053+
{
1054+
$this->reportPossiblyNonexistentGeneralArrayOffset = true;
1055+
$this->reportPossiblyNonexistentConstantArrayOffset = true;
1056+
1057+
$this->analyse([__DIR__ . '/data/pr-4385.php'], [
1058+
[
1059+
'Offset int might not exist on array<int>.',
1060+
24,
1061+
],
1062+
[
1063+
'Offset string might not exist on array<int>.',
1064+
25,
1065+
],
1066+
[
1067+
'Offset array<int>|int might not exist on array<int>.',
1068+
28,
1069+
],
1070+
[
1071+
'Offset array<int>|string might not exist on array<int>.',
1072+
29,
1073+
],
1074+
[
1075+
'Offset 0|array<int> might not exist on array<int>.',
1076+
30,
1077+
],
1078+
[
1079+
'Offset int might not exist on array{string}.',
1080+
33,
1081+
],
1082+
[
1083+
'Offset string might not exist on array{string}.',
1084+
34,
1085+
],
1086+
[
1087+
'Offset array<int>|int might not exist on array{string}.',
1088+
37,
1089+
],
1090+
[
1091+
'Offset array<int>|string might not exist on array{string}.',
1092+
38,
1093+
],
1094+
[
1095+
'Offset array<int>|int might not exist on array<int>|string.',
1096+
41,
1097+
],
1098+
]);
1099+
}
1100+
10521101
public function testBug12805(): void
10531102
{
10541103
$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];
27+
$array[$arrayOrObject];
28+
$array[$arrayOrInt];
29+
$array[$arrayOrString];
30+
$array[$arrayOrZero];
31+
32+
$constantArray[$array];
33+
$constantArray[$int];
34+
$constantArray[$string];
35+
$constantArray[$object];
36+
$constantArray[$arrayOrObject];
37+
$constantArray[$arrayOrInt];
38+
$constantArray[$arrayOrString];
39+
$constantArray[$arrayOrZero];
40+
41+
$arrayOrString[$arrayOrInt];
42+
}
43+
}

0 commit comments

Comments
 (0)