Skip to content

Commit 194ba99

Browse files
authoredMar 28, 2025··
Offset on list definitely exists if there's HasOffsetType with higher number
1 parent ae5562f commit 194ba99

File tree

3 files changed

+71
-0
lines changed

3 files changed

+71
-0
lines changed
 

‎src/Type/IntersectionType.php

+18
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
use PHPStan\Type\Accessory\AccessoryNumericStringType;
2929
use PHPStan\Type\Accessory\AccessoryType;
3030
use PHPStan\Type\Accessory\AccessoryUppercaseStringType;
31+
use PHPStan\Type\Accessory\HasOffsetType;
32+
use PHPStan\Type\Accessory\HasOffsetValueType;
3133
use PHPStan\Type\Accessory\NonEmptyArrayType;
3234
use PHPStan\Type\Constant\ConstantArrayType;
3335
use PHPStan\Type\Constant\ConstantIntegerType;
@@ -45,6 +47,7 @@
4547
use function count;
4648
use function implode;
4749
use function in_array;
50+
use function is_int;
4851
use function ksort;
4952
use function md5;
5053
use function sprintf;
@@ -738,6 +741,21 @@ public function hasOffsetValueType(Type $offsetType): TrinaryLogic
738741
if ((new ConstantIntegerType(0))->isSuperTypeOf($arrayKeyOffsetType)->yes()) {
739742
return TrinaryLogic::createYes();
740743
}
744+
745+
foreach ($this->types as $type) {
746+
if (!$type instanceof HasOffsetValueType && !$type instanceof HasOffsetType) {
747+
continue;
748+
}
749+
750+
foreach ($type->getOffsetType()->getConstantScalarValues() as $constantScalarValue) {
751+
if (!is_int($constantScalarValue)) {
752+
continue;
753+
}
754+
if (IntegerRangeType::fromInterval(0, $constantScalarValue)->isSuperTypeOf($arrayKeyOffsetType)->yes()) {
755+
return TrinaryLogic::createYes();
756+
}
757+
}
758+
}
741759
}
742760

743761
return $this->intersectResults(static fn (Type $type): TrinaryLogic => $type->hasOffsetValueType($offsetType));

‎tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php

+16
Original file line numberDiff line numberDiff line change
@@ -900,6 +900,22 @@ public function testNarrowSuperglobals(): void
900900
$this->analyse([__DIR__ . '/data/narrow-superglobal.php'], []);
901901
}
902902

903+
public function testBug12605(): void
904+
{
905+
$this->reportPossiblyNonexistentGeneralArrayOffset = true;
906+
907+
$this->analyse([__DIR__ . '/data/bug-12605.php'], [
908+
[
909+
'Offset 1 might not exist on list<int>.',
910+
19,
911+
],
912+
[
913+
'Offset 10 might not exist on non-empty-list<int>.',
914+
26,
915+
],
916+
]);
917+
}
918+
903919
public function testBug11602(): void
904920
{
905921
$this->reportPossiblyNonexistentGeneralArrayOffset = true;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
namespace Bug12605;
4+
5+
/**
6+
* @return list<int>
7+
*/
8+
function test(): array
9+
{
10+
return [];
11+
}
12+
13+
function doFoo(): void {
14+
$test = test();
15+
16+
if (isset($test[3])) {
17+
echo $test[1];
18+
}
19+
echo $test[1];
20+
}
21+
22+
function doFooBar(): void {
23+
$test = test();
24+
25+
if (isset($test[4])) {
26+
echo $test[10];
27+
}
28+
}
29+
30+
function doBaz(): void {
31+
$test = test();
32+
33+
if (array_key_exists(5, $test) && is_int($test[5])) {
34+
echo $test[3];
35+
}
36+
}
37+

0 commit comments

Comments
 (0)
Please sign in to comment.