diff --git a/src/Rules/Arrays/NonexistentOffsetInArrayDimFetchCheck.php b/src/Rules/Arrays/NonexistentOffsetInArrayDimFetchCheck.php index 872211ec24..c46a62264e 100644 --- a/src/Rules/Arrays/NonexistentOffsetInArrayDimFetchCheck.php +++ b/src/Rules/Arrays/NonexistentOffsetInArrayDimFetchCheck.php @@ -14,7 +14,6 @@ use PHPStan\Type\ErrorType; use PHPStan\Type\Type; use PHPStan\Type\TypeUtils; -use PHPStan\Type\UnionType; use PHPStan\Type\VerbosityLevel; use function count; use function sprintf; @@ -76,6 +75,7 @@ public function check( } else { $flattenedTypes = TypeUtils::flattenTypes($type); } + foreach ($flattenedTypes as $innerType) { if ( $this->reportPossiblyNonexistentGeneralArrayOffset @@ -94,15 +94,15 @@ public function check( $report = true; break; } - if ($dimType instanceof UnionType) { - if ($innerType->hasOffsetValueType($dimType)->no()) { - $report = true; - break; - } - continue; + if ($dimType instanceof BenevolentUnionType) { + $flattenedInnerTypes = [$dimType]; + } else { + $flattenedInnerTypes = TypeUtils::flattenTypes($dimType); } - foreach (TypeUtils::flattenTypes($dimType) as $innerDimType) { - if ($innerType->hasOffsetValueType($innerDimType)->no()) { + foreach ($flattenedInnerTypes as $innerDimType) { + if ( + $innerType->hasOffsetValueType($innerDimType)->no() + ) { $report = true; break 2; } diff --git a/tests/PHPStan/Levels/data/stringOffsetAccess-7.json b/tests/PHPStan/Levels/data/stringOffsetAccess-7.json index 5471fbcf70..1b23f1ed6c 100644 --- a/tests/PHPStan/Levels/data/stringOffsetAccess-7.json +++ b/tests/PHPStan/Levels/data/stringOffsetAccess-7.json @@ -1,4 +1,9 @@ [ + { + "message": "Offset int|object might not exist on 'foo'.", + "line": 19, + "ignorable": true + }, { "message": "Offset 'foo' might not exist on array|string.", "line": 27, @@ -9,11 +14,21 @@ "line": 31, "ignorable": true }, + { + "message": "Offset int|object might not exist on array|string.", + "line": 35, + "ignorable": true + }, { "message": "Possibly invalid array key type int|object.", "line": 35, "ignorable": true }, + { + "message": "Offset int|object might not exist on array{baz: 21}|array{foo: 17, bar: 19}.", + "line": 55, + "ignorable": true + }, { "message": "Possibly invalid array key type int|object.", "line": 55, diff --git a/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php b/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php index 81a26af1c6..b5d69736e1 100644 --- a/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php +++ b/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php @@ -119,6 +119,10 @@ public function testRule(): void 'Cannot access offset \'a\' on array{a: 1, b: 1}|(Closure(): void).', 258, ], + [ + 'Offset int|null might not exist on array.', + 309, + ], [ 'Offset null does not exist on array.', 310, @@ -127,6 +131,10 @@ public function testRule(): void 'Offset int does not exist on array.', 312, ], + [ + 'Offset int|null might not exist on array.', + 314, + ], [ 'Offset \'baz\' might not exist on array{bar: 1, baz?: 2}.', 344, @@ -185,6 +193,10 @@ public function testStrings(): void 'Offset 12.34 does not exist on \'foo\'.', 13, ], + [ + 'Offset int|object might not exist on \'foo\'.', + 16, + ], [ 'Offset \'foo\' might not exist on array|string.', 24, @@ -193,6 +205,10 @@ public function testStrings(): void 'Offset 12.34 might not exist on array|string.', 28, ], + [ + 'Offset int|object might not exist on array|string.', + 32, + ], ]); } @@ -926,6 +942,16 @@ public function testBug3747(): void $this->analyse([__DIR__ . '/data/bug-3747.php'], []); } + public function testBug1061(): void + { + $this->analyse([__DIR__ . '/data/bug-1061.php'], [ + [ + "Offset 'one'|'two' might not exist on array{two: 1, three: 2}.", + 14, + ], + ]); + } + public function testBug8372(): void { $this->checkExplicitMixed = true; diff --git a/tests/PHPStan/Rules/Arrays/data/bug-1061.php b/tests/PHPStan/Rules/Arrays/data/bug-1061.php new file mode 100644 index 0000000000..2513b4d498 --- /dev/null +++ b/tests/PHPStan/Rules/Arrays/data/bug-1061.php @@ -0,0 +1,15 @@ + 1, + "three" => 2 + ]; +} + +foreach (A::KEYS as $key) { + echo A::ARR[$key]; +}