From 6a1561803dfeaef25b0915f77141553f92558b6e Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Sat, 30 Aug 2025 18:01:45 +0200 Subject: [PATCH 1/3] Fix accessing offset with object --- src/Type/ArrayType.php | 3 +++ src/Type/Constant/ConstantArrayType.php | 4 ++++ src/Type/ObjectType.php | 2 +- src/Type/Traits/ObjectTypeTrait.php | 2 +- ...nexistentOffsetInArrayDimFetchRuleTest.php | 14 +++++++++++ .../PHPStan/Rules/Arrays/data/bug-object.php | 23 +++++++++++++++++++ 6 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 tests/PHPStan/Rules/Arrays/data/bug-object.php diff --git a/src/Type/ArrayType.php b/src/Type/ArrayType.php index ffd213eb93..320b22ad3b 100644 --- a/src/Type/ArrayType.php +++ b/src/Type/ArrayType.php @@ -268,6 +268,9 @@ public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType public function hasOffsetValueType(Type $offsetType): TrinaryLogic { $offsetType = $offsetType->toArrayKey(); + if ($offsetType instanceof ErrorType) { + return TrinaryLogic::createNo(); + } if ($this->getKeyType()->isSuperTypeOf($offsetType)->no() && ($offsetType->isString()->no() || !$offsetType->isConstantScalarValue()->no()) diff --git a/src/Type/Constant/ConstantArrayType.php b/src/Type/Constant/ConstantArrayType.php index 5643e79655..bba9e8b8a6 100644 --- a/src/Type/Constant/ConstantArrayType.php +++ b/src/Type/Constant/ConstantArrayType.php @@ -588,6 +588,10 @@ public function hasOffsetValueType(Type $offsetType): TrinaryLogic private function recursiveHasOffsetValueType(Type $offsetType): TrinaryLogic { + if ($offsetType instanceof ErrorType) { + return TrinaryLogic::createNo(); + } + if ($offsetType instanceof UnionType) { $results = []; foreach ($offsetType->getTypes() as $innerType) { diff --git a/src/Type/ObjectType.php b/src/Type/ObjectType.php index 78e4473410..43b277774f 100644 --- a/src/Type/ObjectType.php +++ b/src/Type/ObjectType.php @@ -702,7 +702,7 @@ public function toArray(): Type public function toArrayKey(): Type { - return $this->toString(); + return new ErrorType(); } public function toCoercedArgumentType(bool $strictTypes): Type diff --git a/src/Type/Traits/ObjectTypeTrait.php b/src/Type/Traits/ObjectTypeTrait.php index c600f2d74a..fd73472f4f 100644 --- a/src/Type/Traits/ObjectTypeTrait.php +++ b/src/Type/Traits/ObjectTypeTrait.php @@ -271,7 +271,7 @@ public function toArray(): Type public function toArrayKey(): Type { - return new StringType(); + return new ErrorType(); } public function toCoercedArgumentType(bool $strictTypes): Type diff --git a/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php b/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php index a481085088..426b241708 100644 --- a/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php +++ b/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php @@ -921,6 +921,20 @@ public function testBug12593(): void $this->analyse([__DIR__ . '/data/bug-12593.php'], []); } + public function testBugObject(): void + { + $this->analyse([__DIR__ . '/data/bug-object.php'], [ + [ + 'Offset int|object does not exist on array{baz: 21}|array{foo: 17, bar: 19}.', + 12, + ], + [ + 'Offset object does not exist on array.', + 21, + ] + ]); + } + public function testBug3747(): void { $this->analyse([__DIR__ . '/data/bug-3747.php'], []); diff --git a/tests/PHPStan/Rules/Arrays/data/bug-object.php b/tests/PHPStan/Rules/Arrays/data/bug-object.php new file mode 100644 index 0000000000..97c64062a6 --- /dev/null +++ b/tests/PHPStan/Rules/Arrays/data/bug-object.php @@ -0,0 +1,23 @@ + $array + * @param object $key + */ + public function foo2($array, $key) + { + $array[$key]; + } +} From e5c5399a7617be8bc19d2babe860ed1d6fc6d155 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Sat, 30 Aug 2025 18:14:47 +0200 Subject: [PATCH 2/3] Update tests --- src/Type/Traits/ObjectTypeTrait.php | 1 - tests/PHPStan/Levels/data/stringOffsetAccess-3.json | 12 +++++++++++- tests/PHPStan/Levels/data/stringOffsetAccess-7.json | 5 +++++ .../NonexistentOffsetInArrayDimFetchRuleTest.php | 6 +++++- 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/Type/Traits/ObjectTypeTrait.php b/src/Type/Traits/ObjectTypeTrait.php index fd73472f4f..d8a52c200d 100644 --- a/src/Type/Traits/ObjectTypeTrait.php +++ b/src/Type/Traits/ObjectTypeTrait.php @@ -19,7 +19,6 @@ use PHPStan\Type\BooleanType; use PHPStan\Type\ErrorType; use PHPStan\Type\MixedType; -use PHPStan\Type\StringType; use PHPStan\Type\Type; use PHPStan\Type\TypeCombinator; diff --git a/tests/PHPStan/Levels/data/stringOffsetAccess-3.json b/tests/PHPStan/Levels/data/stringOffsetAccess-3.json index be9fa763e2..f7f4afafdd 100644 --- a/tests/PHPStan/Levels/data/stringOffsetAccess-3.json +++ b/tests/PHPStan/Levels/data/stringOffsetAccess-3.json @@ -9,9 +9,19 @@ "line": 16, "ignorable": true }, + { + "message": "Offset int|object does not exist on array{baz: 21}|array{foo: 17, bar: 19}.", + "line": 55, + "ignorable": true + }, { "message": "Invalid array key type stdClass.", "line": 59, "ignorable": true + }, + { + "message": "Offset stdClass does not exist on array{baz: 21}|array{foo: 17, bar: 19}.", + "line": 59, + "ignorable": true } -] \ No newline at end of file +] diff --git a/tests/PHPStan/Levels/data/stringOffsetAccess-7.json b/tests/PHPStan/Levels/data/stringOffsetAccess-7.json index 5471fbcf70..2d59876920 100644 --- a/tests/PHPStan/Levels/data/stringOffsetAccess-7.json +++ b/tests/PHPStan/Levels/data/stringOffsetAccess-7.json @@ -9,6 +9,11 @@ "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, diff --git a/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php b/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php index 426b241708..1c6c21e9e9 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 might not exist on array|string.', 28, ], + [ + 'Offset int|object might not exist on array|string.', + 32, + ], ]); } @@ -931,7 +935,7 @@ public function testBugObject(): void [ 'Offset object does not exist on array.', 21, - ] + ], ]); } From cc5c9e1c44923a0fd5c493f703454c931b916da6 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Sun, 31 Aug 2025 14:01:39 +0200 Subject: [PATCH 3/3] Update test --- tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php b/tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php index 24ab77c1a0..4fdbfb9e8c 100644 --- a/tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php @@ -1801,7 +1801,7 @@ public static function dataProperties(): array '$this->resource', ], [ - 'mixed', + '*ERROR*', '$this->yetAnotherAnotherMixedParameter', ], [