diff --git a/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php b/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php index 495429cac9..e275c39fbc 100644 --- a/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php +++ b/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php @@ -766,12 +766,71 @@ public function testMixed(): void 11, ], [ - 'Cannot access offset 5 on mixed.', + 'Cannot access offset 5 on T of mixed.', + 12, + ], + [ + 'Cannot access offset 5 on T of mixed.', + 15, + ], + [ + 'Cannot access offset 5 on T of mixed.', 16, ], [ 'Cannot access offset 5 on mixed.', - 21, + 22, + ], + [ + 'Cannot access offset 5 on mixed.', + 23, + ], + [ + 'Cannot access offset 5 on mixed.', + 26, + ], + [ + 'Cannot access offset 5 on mixed.', + 27, + ], + [ + 'Cannot access offset 5 on mixed.', + 33, + ], + [ + 'Cannot access offset 5 on mixed.', + 34, + ], + [ + 'Cannot access offset 5 on mixed.', + 37, + ], + [ + 'Cannot access offset 5 on mixed.', + 38, + ], + ]); + } + + public function testOffsetAccessLegal(): void + { + $this->checkExplicitMixed = true; + $this->analyse([__DIR__ . '/data/offset-access-legal.php'], [ + [ + 'Cannot access offset 0 on Closure(): void.', + 7, + ], + [ + 'Cannot access offset 0 on stdClass.', + 12, + ], + [ + 'Cannot access offset 0 on stdClass|false.', + 96, + ], + [ + 'Cannot access offset 0 on (Closure(): void)|true.', + 98, ], ]); } diff --git a/tests/PHPStan/Rules/Arrays/data/offset-access-legal.php b/tests/PHPStan/Rules/Arrays/data/offset-access-legal.php new file mode 100644 index 0000000000..f37f13ce48 --- /dev/null +++ b/tests/PHPStan/Rules/Arrays/data/offset-access-legal.php @@ -0,0 +1,99 @@ += 8.0 + +namespace OffsetAccessLegal; + +function closure(): void +{ + (function(){})[0] ?? "error"; +} + +function nonArrayAccessibleObject() +{ + (new \stdClass())[0] ?? "error"; +} + +function arrayAccessibleObject() +{ + (new class implements \ArrayAccess { + public function offsetExists($offset) { + return true; + } + + public function offsetGet($offset) { + return $offset; + } + + public function offsetSet($offset, $value) { + } + + public function offsetUnset($offset) { + } + })[0] ?? "error"; +} + +function array_(): void +{ + [0][0] ?? "error"; +} + +function integer(): void +{ + (0)[0] ?? 'ok'; +} + +function float(): void +{ + (0.0)[0] ?? 'ok'; +} + +function null(): void +{ + (null)[0] ?? 'ok'; +} + +function bool(): void +{ + (true)[0] ?? 'ok'; +} + +function void(): void +{ + ((function (){})())[0] ?? 'ok'; +} + +function resource(): void +{ + (tmpfile())[0] ?? 'ok'; +} + +function offsetAccessibleMaybeAndLegal(): void +{ + $arrayAccessible = rand() ? (new class implements \ArrayAccess { + public function offsetExists($offset) { + return true; + } + + public function offsetGet($offset) { + return $offset; + } + + public function offsetSet($offset, $value) { + } + + public function offsetUnset($offset) { + } + }) : false; + + ($arrayAccessible)[0] ?? "error"; + + (rand() ? "string" : true)[0] ?? "error"; +} + +function offsetAccessibleMaybeAndIllegal(): void +{ + $arrayAccessible = rand() ? new \stdClass() : false; + + ($arrayAccessible)[0] ?? "error"; + + (rand() ? function(){} : true)[0] ?? "error"; +} diff --git a/tests/PHPStan/Rules/Arrays/data/offset-access-mixed.php b/tests/PHPStan/Rules/Arrays/data/offset-access-mixed.php index 9f3300ce65..80dcfc8cea 100644 --- a/tests/PHPStan/Rules/Arrays/data/offset-access-mixed.php +++ b/tests/PHPStan/Rules/Arrays/data/offset-access-mixed.php @@ -9,14 +9,32 @@ function foo(mixed $a): void { var_dump($a[5]); + isset($a[5]); + + if (!is_object($a) && !$a instanceof \Closure) { + var_dump($a[5]); + isset($a[5]); + } } function foo2(mixed $a): void { var_dump($a[5]); + isset($a[5]); + + if (!is_object($a) && !$a instanceof \Closure) { + var_dump($a[5]); + isset($a[5]); + } } function foo3($a): void { var_dump($a[5]); + isset($a[5]); + + if (!is_object($a) && !$a instanceof \Closure) { + var_dump($a[5]); + isset($a[5]); + } }