Skip to content

Commit 39c65a9

Browse files
authored
Fix false positives on existing offsets after assign
1 parent 54ca85b commit 39c65a9

File tree

5 files changed

+70
-2
lines changed

5 files changed

+70
-2
lines changed

Diff for: src/Analyser/NodeScopeResolver.php

+1-2
Original file line numberDiff line numberDiff line change
@@ -5480,8 +5480,7 @@ private function processAssignVar(
54805480
}
54815481

54825482
if ($originalVar->dim instanceof Variable || $originalVar->dim instanceof Node\Scalar) {
5483-
$currentVarType = $scope->getType($originalVar);
5484-
if (!$originalValueToWrite->isSuperTypeOf($currentVarType)->yes()) {
5483+
if (!$scope->hasExpressionType($originalVar)->yes()) {
54855484
$scope = $scope->assignExpression(
54865485
$originalVar,
54875486
$originalValueToWrite,

Diff for: tests/PHPStan/Analyser/NodeScopeResolverTest.php

+1
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ private static function findTestFiles(): iterable
208208
yield __DIR__ . '/../Rules/Classes/data/bug-11591-property-tag.php';
209209
yield __DIR__ . '/../Rules/Classes/data/mixin-trait-use.php';
210210

211+
yield __DIR__ . '/../Rules/Arrays/data/bug-11679.php';
211212
yield __DIR__ . '/../Rules/Methods/data/bug-4801.php';
212213
}
213214

Diff for: tests/PHPStan/Rules/Arrays/NonexistentOffsetInArrayDimFetchRuleTest.php

+14
Original file line numberDiff line numberDiff line change
@@ -849,6 +849,20 @@ public function testArrayDimFetchOnArrayKeyFirsOrLastOrCount(): void
849849
]);
850850
}
851851

852+
public function testBug12406(): void
853+
{
854+
$this->reportPossiblyNonexistentGeneralArrayOffset = true;
855+
856+
$this->analyse([__DIR__ . '/data/bug-12406.php'], []);
857+
}
858+
859+
public function testBug11679(): void
860+
{
861+
$this->reportPossiblyNonexistentGeneralArrayOffset = true;
862+
863+
$this->analyse([__DIR__ . '/data/bug-11679.php'], []);
864+
}
865+
852866
public function testBug8649(): void
853867
{
854868
$this->reportPossiblyNonexistentGeneralArrayOffset = true;

Diff for: tests/PHPStan/Rules/Arrays/data/bug-11679.php

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
namespace Bug11679;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class WorkingExample
8+
{
9+
/** @var array{foo?: bool} */
10+
private array $arr = [];
11+
12+
public function sayHello(): bool
13+
{
14+
assertType('array{foo?: bool}', $this->arr);
15+
if (!isset($this->arr['foo'])) {
16+
$this->arr['foo'] = true;
17+
assertType('array{foo: true}', $this->arr);
18+
}
19+
assertType('array{foo: bool}', $this->arr);
20+
return $this->arr['foo']; // PHPStan realizes optional 'foo' is set
21+
}
22+
}
23+
24+
class NonworkingExample
25+
{
26+
/** @var array<int, array{foo?: bool}> */
27+
private array $arr = [];
28+
29+
public function sayHello(int $index): bool
30+
{
31+
assertType('array<int, array{foo?: bool}>', $this->arr);
32+
if (!isset($this->arr[$index]['foo'])) {
33+
$this->arr[$index]['foo'] = true;
34+
assertType('non-empty-array<int, array{foo: true}>', $this->arr);
35+
}
36+
assertType('array<int, array{foo?: bool}>', $this->arr);
37+
return $this->arr[$index]['foo']; // PHPStan does not realize 'foo' is set
38+
}
39+
}

Diff for: tests/PHPStan/Rules/Arrays/data/bug-12406.php

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace Bug12406;
4+
5+
final class HelloWorld
6+
{
7+
/** @var array<string, int> */
8+
protected array $words = [];
9+
10+
public function sayHello(string $word, int $count): void
11+
{
12+
$this->words[$word] ??= 0;
13+
$this->words[$word] += $count;
14+
}
15+
}

0 commit comments

Comments
 (0)