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..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; @@ -271,7 +270,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/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', ], [ 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 a481085088..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, + ], ]); } @@ -921,6 +925,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]; + } +}