From d696666871025ff629282df100893b404a094fad Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Tue, 2 Sep 2025 11:08:24 +0200 Subject: [PATCH 1/3] Fix hasOffsetValueType for arrays --- src/Type/ArrayType.php | 7 +++---- src/Type/Constant/ConstantArrayType.php | 8 +++----- .../PHPStan/Analyser/LegacyNodeScopeResolverTest.php | 2 +- .../NonexistentOffsetInArrayDimFetchRuleTest.php | 8 -------- .../PHPStan/Rules/Variables/NullCoalesceRuleTest.php | 5 +++++ tests/PHPStan/Rules/Variables/data/bug-doctrine.php | 11 +++++++++++ 6 files changed, 23 insertions(+), 18 deletions(-) create mode 100644 tests/PHPStan/Rules/Variables/data/bug-doctrine.php diff --git a/src/Type/ArrayType.php b/src/Type/ArrayType.php index 320b22ad3b..7453ae3315 100644 --- a/src/Type/ArrayType.php +++ b/src/Type/ArrayType.php @@ -8,6 +8,7 @@ use PHPStan\PhpDocParser\Ast\Type\TypeNode; use PHPStan\Reflection\ClassMemberAccessAnswerer; use PHPStan\Reflection\TrivialParametersAcceptor; +use PHPStan\Rules\Arrays\AllowedArrayKeysTypes; use PHPStan\ShouldNotHappenException; use PHPStan\TrinaryLogic; use PHPStan\Type\Accessory\AccessoryArrayListType; @@ -267,10 +268,8 @@ public function looseCompare(Type $type, PhpVersion $phpVersion): BooleanType public function hasOffsetValueType(Type $offsetType): TrinaryLogic { - $offsetType = $offsetType->toArrayKey(); - if ($offsetType instanceof ErrorType) { - return TrinaryLogic::createNo(); - } + $allowedArrayKeys = AllowedArrayKeysTypes::getType(); + $offsetType = TypeCombinator::intersect($allowedArrayKeys, $offsetType)->toArrayKey(); 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 bba9e8b8a6..9418c81fa7 100644 --- a/src/Type/Constant/ConstantArrayType.php +++ b/src/Type/Constant/ConstantArrayType.php @@ -19,6 +19,7 @@ use PHPStan\Reflection\InitializerExprTypeResolver; use PHPStan\Reflection\PhpVersionStaticAccessor; use PHPStan\Reflection\TrivialParametersAcceptor; +use PHPStan\Rules\Arrays\AllowedArrayKeysTypes; use PHPStan\ShouldNotHappenException; use PHPStan\TrinaryLogic; use PHPStan\Type\AcceptsResult; @@ -581,17 +582,14 @@ public function findTypeAndMethodNames(): array public function hasOffsetValueType(Type $offsetType): TrinaryLogic { - $offsetArrayKeyType = $offsetType->toArrayKey(); + $allowedArrayKeys = AllowedArrayKeysTypes::getType(); + $offsetArrayKeyType = TypeCombinator::intersect($allowedArrayKeys, $offsetType)->toArrayKey(); return $this->recursiveHasOffsetValueType($offsetArrayKeyType); } 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/tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php b/tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php index c1245971e0..fd78999673 100644 --- a/tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/LegacyNodeScopeResolverTest.php @@ -1801,7 +1801,7 @@ public static function dataProperties(): array '$this->resource', ], [ - '*ERROR*', + 'mixed', '$this->yetAnotherAnotherMixedParameter', ], [ diff --git a/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php b/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php index 1c6c21e9e9..2dcdda2446 100644 --- a/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php +++ b/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php @@ -193,10 +193,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, - ], ]); } @@ -932,10 +928,6 @@ public function testBugObject(): void 'Offset int|object does not exist on array{baz: 21}|array{foo: 17, bar: 19}.', 12, ], - [ - 'Offset object does not exist on array.', - 21, - ], ]); } diff --git a/tests/PHPStan/Rules/Variables/NullCoalesceRuleTest.php b/tests/PHPStan/Rules/Variables/NullCoalesceRuleTest.php index 6ffb9013bc..7f9df15081 100644 --- a/tests/PHPStan/Rules/Variables/NullCoalesceRuleTest.php +++ b/tests/PHPStan/Rules/Variables/NullCoalesceRuleTest.php @@ -326,6 +326,11 @@ public function testBug10610(): void $this->analyse([__DIR__ . '/data/bug-10610.php'], []); } + public function testBugDoctrine(): void + { + $this->analyse([__DIR__ . '/data/bug-doctrine.php'], []); + } + #[RequiresPhp('>= 8.4')] public function testBug12553(): void { diff --git a/tests/PHPStan/Rules/Variables/data/bug-doctrine.php b/tests/PHPStan/Rules/Variables/data/bug-doctrine.php new file mode 100644 index 0000000000..0ad574a019 --- /dev/null +++ b/tests/PHPStan/Rules/Variables/data/bug-doctrine.php @@ -0,0 +1,11 @@ + Date: Tue, 2 Sep 2025 11:12:44 +0200 Subject: [PATCH 2/3] Fix --- tests/PHPStan/Levels/data/stringOffsetAccess-3.json | 5 ----- tests/PHPStan/Levels/data/stringOffsetAccess-7.json | 5 ----- 2 files changed, 10 deletions(-) diff --git a/tests/PHPStan/Levels/data/stringOffsetAccess-3.json b/tests/PHPStan/Levels/data/stringOffsetAccess-3.json index f7f4afafdd..2b54f201d4 100644 --- a/tests/PHPStan/Levels/data/stringOffsetAccess-3.json +++ b/tests/PHPStan/Levels/data/stringOffsetAccess-3.json @@ -18,10 +18,5 @@ "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 } ] diff --git a/tests/PHPStan/Levels/data/stringOffsetAccess-7.json b/tests/PHPStan/Levels/data/stringOffsetAccess-7.json index 2d59876920..5471fbcf70 100644 --- a/tests/PHPStan/Levels/data/stringOffsetAccess-7.json +++ b/tests/PHPStan/Levels/data/stringOffsetAccess-7.json @@ -9,11 +9,6 @@ "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, From 3bdbd2536e1a6b8b123b9f1427d2b3f9862cd238 Mon Sep 17 00:00:00 2001 From: Vincent Langlet Date: Tue, 2 Sep 2025 11:30:27 +0200 Subject: [PATCH 3/3] Fix --- tests/PHPStan/Rules/Variables/data/bug-doctrine.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/PHPStan/Rules/Variables/data/bug-doctrine.php b/tests/PHPStan/Rules/Variables/data/bug-doctrine.php index 0ad574a019..9886e052d7 100644 --- a/tests/PHPStan/Rules/Variables/data/bug-doctrine.php +++ b/tests/PHPStan/Rules/Variables/data/bug-doctrine.php @@ -4,7 +4,11 @@ class HelloWorld { - public function sayHello(string|array $a, array $b): void + /** + * @param string|array $a + * @param array $b + */ + public function sayHello($a, $b): void { $b[$a] ?? 2; }