From f05e58777d9630531db640c3e0afd69a10eb6347 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Sat, 26 Jul 2025 15:21:02 +0200 Subject: [PATCH 1/4] Add non regression test --- .../NonexistentOffsetInArrayDimFetchRuleTest.php | 12 ++++++++++++ tests/PHPStan/Rules/Arrays/data/bug-1061.php | 15 +++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 tests/PHPStan/Rules/Arrays/data/bug-1061.php diff --git a/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php b/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php index 81a26af1c6..d25d6f71cd 100644 --- a/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php +++ b/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php @@ -926,6 +926,18 @@ public function testBug3747(): void $this->analyse([__DIR__ . '/data/bug-3747.php'], []); } + public function testBug1061(): void + { + $this->reportPossiblyNonexistentConstantArrayOffset = true; + + $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]; +} From 03e629c32c98047ae17687e2fabc6128688ae177 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Sat, 26 Jul 2025 17:10:51 +0200 Subject: [PATCH 2/4] Rework --- .../NonexistentOffsetInArrayDimFetchCheck.php | 9 ++++++--- ...onexistentOffsetInArrayDimFetchRuleTest.php | 18 ++++++++++++++++-- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/Rules/Arrays/NonexistentOffsetInArrayDimFetchCheck.php b/src/Rules/Arrays/NonexistentOffsetInArrayDimFetchCheck.php index 872211ec24..c567524b4c 100644 --- a/src/Rules/Arrays/NonexistentOffsetInArrayDimFetchCheck.php +++ b/src/Rules/Arrays/NonexistentOffsetInArrayDimFetchCheck.php @@ -76,6 +76,7 @@ public function check( } else { $flattenedTypes = TypeUtils::flattenTypes($type); } + foreach ($flattenedTypes as $innerType) { if ( $this->reportPossiblyNonexistentGeneralArrayOffset @@ -95,9 +96,11 @@ public function check( break; } if ($dimType instanceof UnionType) { - if ($innerType->hasOffsetValueType($dimType)->no()) { - $report = true; - break; + foreach ($dimType->getTypes() as $subDimType) { + if ($innerType->hasOffsetValueType($subDimType)->no()) { + $report = true; + break 2; + } } continue; } diff --git a/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php b/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php index d25d6f71cd..c751368c5a 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, + ], ]); } @@ -928,8 +944,6 @@ public function testBug3747(): void public function testBug1061(): void { - $this->reportPossiblyNonexistentConstantArrayOffset = true; - $this->analyse([__DIR__ . '/data/bug-1061.php'], [ [ "Offset 'one'|'two' might not exist on array{two: 1, three: 2}.", From 51c9e6d27e1b263ad2f9bee9ae9a423b6003c4f1 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Sat, 26 Jul 2025 17:32:29 +0200 Subject: [PATCH 3/4] Fix --- .../NonexistentOffsetInArrayDimFetchCheck.php | 19 ++++++++----------- ...nexistentOffsetInArrayDimFetchRuleTest.php | 10 +--------- 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/src/Rules/Arrays/NonexistentOffsetInArrayDimFetchCheck.php b/src/Rules/Arrays/NonexistentOffsetInArrayDimFetchCheck.php index c567524b4c..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; @@ -95,17 +94,15 @@ public function check( $report = true; break; } - if ($dimType instanceof UnionType) { - foreach ($dimType->getTypes() as $subDimType) { - if ($innerType->hasOffsetValueType($subDimType)->no()) { - $report = true; - break 2; - } - } - 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/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php b/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php index c751368c5a..5a094c254c 100644 --- a/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php +++ b/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php @@ -132,7 +132,7 @@ public function testRule(): void 312, ], [ - ' Offset int|null might not exist on array.', + 'Offset int|null might not exist on array.', 314, ], [ @@ -193,10 +193,6 @@ 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, @@ -205,10 +201,6 @@ public function testStrings(): void 'Offset 12.34 might not exist on array|string.', 28, ], - [ - 'Offset int|object might not exist on array|string.', - 32, - ], ]); } From 2417411959dde03c6189fff60ab91c56d4d7aa10 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Fri, 1 Aug 2025 13:06:53 +0200 Subject: [PATCH 4/4] Fix tests --- .../PHPStan/Levels/data/stringOffsetAccess-7.json | 15 +++++++++++++++ .../NonexistentOffsetInArrayDimFetchRuleTest.php | 8 ++++++++ 2 files changed, 23 insertions(+) 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 5a094c254c..b5d69736e1 100644 --- a/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php +++ b/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php @@ -193,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, @@ -201,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, + ], ]); }