Skip to content

Commit b6249f6

Browse files
committed
Fix false positives on non-existing-offset's
1 parent 60b29fa commit b6249f6

File tree

3 files changed

+90
-0
lines changed

3 files changed

+90
-0
lines changed

src/Rules/Arrays/NonexistentOffsetInArrayDimFetchRule.php

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
use PHPStan\Type\Type;
1414
use PHPStan\Type\VerbosityLevel;
1515
use function count;
16+
use function in_array;
1617
use function sprintf;
1718

1819
/**
@@ -96,6 +97,41 @@ public function processNode(Node $node, Scope $scope): array
9697
return [];
9798
}
9899

100+
if (
101+
$node->dim instanceof Node\Expr\FuncCall
102+
&& $node->dim->name instanceof Node\Name
103+
&& in_array($node->dim->name->toLowerString(), ['array_key_first', 'array_key_last'], true)
104+
&& count($node->dim->getArgs()) >= 1
105+
) {
106+
$arrayArg = $node->dim->getArgs()[0]->value;
107+
$arrayType = $scope->getType($arrayArg);
108+
if (
109+
$arrayType->isArray()->yes()
110+
&& $arrayType->isIterableAtLeastOnce()->yes()
111+
) {
112+
return [];
113+
}
114+
}
115+
116+
if (
117+
$node->dim instanceof Node\Expr\BinaryOp\Minus
118+
&& $node->dim->left instanceof Node\Expr\FuncCall
119+
&& $node->dim->left->name instanceof Node\Name
120+
&& in_array($node->dim->left->name->toLowerString(), ['count', 'sizeof'], true)
121+
&& count($node->dim->left->getArgs()) >= 1
122+
&& $node->dim->right instanceof Node\Scalar\Int_
123+
&& $node->dim->right->value === 1
124+
) {
125+
$arrayArg = $node->dim->left->getArgs()[0]->value;
126+
$arrayType = $scope->getType($arrayArg);
127+
if (
128+
$arrayType->isList()->yes()
129+
&& $arrayType->isIterableAtLeastOnce()->yes()
130+
) {
131+
return [];
132+
}
133+
}
134+
99135
return $this->nonexistentOffsetInArrayDimFetchCheck->check(
100136
$scope,
101137
$node->var,

tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -829,6 +829,18 @@ public function testArrayDimFetchAfterArraySearch(): void
829829
]);
830830
}
831831

832+
public function testArrayDimFetchOnArrayKeyFirsOrLastOrCount(): void
833+
{
834+
$this->reportPossiblyNonexistentGeneralArrayOffset = true;
835+
836+
$this->analyse([__DIR__ . '/data/array-dim-fetch-on-array-key-first-last.php'], [
837+
[
838+
'Offset 0|null might not exist on list<string>.',
839+
12,
840+
],
841+
]);
842+
}
843+
832844
public function testBug8649(): void
833845
{
834846
$this->reportPossiblyNonexistentGeneralArrayOffset = true;
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
namespace ArrayDimFetchOnArrayKeyFirstOrLast;
4+
5+
class Hello {
6+
/**
7+
* @param list<string> $hellos
8+
*/
9+
public function first(array $hellos): string
10+
{
11+
if (rand(0,1)) {
12+
return $hellos[array_key_first($hellos)];
13+
}
14+
if ($hellos !== []) {
15+
return $hellos[array_key_first($hellos)];
16+
}
17+
return '';
18+
}
19+
20+
/**
21+
* @param array<string> $hellos
22+
*/
23+
public function last(array $hellos): string
24+
{
25+
if ($hellos !== []) {
26+
return $hellos[array_key_last($hellos)];
27+
}
28+
return '';
29+
}
30+
31+
/**
32+
* @param list<string> $hellos
33+
*/
34+
public function works(array $hellos): string
35+
{
36+
if ($hellos === []) {
37+
return 'nothing';
38+
}
39+
40+
return $hellos[count($hellos) - 1];
41+
}
42+
}

0 commit comments

Comments
 (0)