From b73b7a3dc0572745838054d42d7270f873c64acc Mon Sep 17 00:00:00 2001 From: David Scandurra <31861387+SplotyCode@users.noreply.github.com> Date: Sat, 8 Nov 2025 16:37:32 +0100 Subject: [PATCH 1/2] Fix non-existent offset when guarding --- src/Analyser/TypeSpecifier.php | 19 +++++++++++++++++ ...nexistentOffsetInArrayDimFetchRuleTest.php | 8 +++++++ ...port-possibly-nonexistent-array-offset.php | 21 +++++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/src/Analyser/TypeSpecifier.php b/src/Analyser/TypeSpecifier.php index 6698a8a800..55e24d1b1a 100644 --- a/src/Analyser/TypeSpecifier.php +++ b/src/Analyser/TypeSpecifier.php @@ -291,6 +291,25 @@ public function specifyTypesInCondition( if ($specifiedTypes !== null) { $result = $result->unionWith($specifiedTypes); } + if ( + $context->true() + && $expr instanceof Node\Expr\BinaryOp\Smaller + && $argType->isList()->yes() + && IntegerRangeType::fromInterval(0, null)->isSuperTypeOf($leftType)->yes() + ) { + $dimFetch = new ArrayDimFetch( + $expr->right->getArgs()[0]->value, + $expr->left, + ); + $result = $result->unionWith( + $this->create( + $dimFetch, + $argType->getIterableValueType(), + TypeSpecifierContext::createTrue(), + $scope, + )->setRootExpr($expr) + ); + } if ( $context->true() && (IntegerRangeType::createAllGreaterThanOrEqualTo(1 - $offset)->isSuperTypeOf($leftType)->yes()) diff --git a/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php b/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php index 879c86f791..1b4658fa50 100644 --- a/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php +++ b/tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php @@ -700,6 +700,10 @@ public static function dataReportPossiblyNonexistentArrayOffset(): iterable "Offset 'foo' might not exist on array.", 9, ], + [ + 'Offset int might not exist on list.', + 77 + ], ]]; yield [true, true, [ [ @@ -710,6 +714,10 @@ public static function dataReportPossiblyNonexistentArrayOffset(): iterable 'Offset string might not exist on array{foo: 1}.', 20, ], + [ + 'Offset int might not exist on list.', + 77 + ], ]]; } diff --git a/tests/PHPStan/Rules/Arrays/data/report-possibly-nonexistent-array-offset.php b/tests/PHPStan/Rules/Arrays/data/report-possibly-nonexistent-array-offset.php index fc54d96f00..967d913cea 100644 --- a/tests/PHPStan/Rules/Arrays/data/report-possibly-nonexistent-array-offset.php +++ b/tests/PHPStan/Rules/Arrays/data/report-possibly-nonexistent-array-offset.php @@ -57,4 +57,25 @@ public function nonEmpty(array $a): void echo $a[0]; } + /** + * @param list $array + * @param positive-int $index + */ + public function guard(array $array, int $index) { + if ($index < count($array)) { + return $array[$index]; + } + return null; + } + + + /** + * @param list $array + */ + public function guardNotSafe(array $array, int $index) { + if ($index < count($array)) { + return $array[$index]; + } + return null; + } } From 679d274fcb278b72faa14ad2b0f00a12fb23509f Mon Sep 17 00:00:00 2001 From: David Scandurra <31861387+SplotyCode@users.noreply.github.com> Date: Sat, 8 Nov 2025 16:57:47 +0100 Subject: [PATCH 2/2] Dont set root expression --- src/Analyser/TypeSpecifier.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Analyser/TypeSpecifier.php b/src/Analyser/TypeSpecifier.php index 55e24d1b1a..5f637a4e75 100644 --- a/src/Analyser/TypeSpecifier.php +++ b/src/Analyser/TypeSpecifier.php @@ -307,7 +307,7 @@ public function specifyTypesInCondition( $argType->getIterableValueType(), TypeSpecifierContext::createTrue(), $scope, - )->setRootExpr($expr) + ) ); }