Skip to content

Commit

Permalink
Fix unassigning the last offset in a ConstantArray that is a list
Browse files Browse the repository at this point in the history
  • Loading branch information
herndlm committed Sep 20, 2024
1 parent cb8f910 commit a287ab8
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 4 deletions.
4 changes: 3 additions & 1 deletion src/Type/Constant/ConstantArrayType.php
Original file line number Diff line number Diff line change
Expand Up @@ -734,7 +734,9 @@ public function unsetOffset(Type $offsetType): Type
$k++;
}

return new self($newKeyTypes, $newValueTypes, $this->nextAutoIndexes, $newOptionalKeys, TrinaryLogic::createNo());
$wasLastOffset = TrinaryLogic::createFromBoolean($i === count($this->keyTypes) - 1);

return new self($newKeyTypes, $newValueTypes, $this->nextAutoIndexes, $newOptionalKeys, $this->isList->and($wasLastOffset));
}

return $this;
Expand Down
11 changes: 9 additions & 2 deletions src/Type/Constant/ConstantArrayTypeBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -122,14 +122,21 @@ public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $opt
$newAutoIndexes[] = $newAutoIndex;
$this->nextAutoIndexes = array_values(array_unique($newAutoIndexes));

$keyTypesCount = count($this->keyTypes);

if ($optional || $hasOptional) {
$this->optionalKeys[] = count($this->keyTypes) - 1;
$this->optionalKeys[] = $keyTypesCount - 1;
}

if (count($this->keyTypes) > self::ARRAY_COUNT_LIMIT) {
if ($keyTypesCount > self::ARRAY_COUNT_LIMIT) {
$this->degradeToGeneralArray = true;
}

if ($keyTypesCount - 1 !== $max && $this->isList->yes()) {
// The array is not a list any longer if there is gap in the offsets
$this->isList = TrinaryLogic::createNo();
}

return;
}

Expand Down
2 changes: 1 addition & 1 deletion tests/PHPStan/Analyser/nsrt/array-is-list-unset.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ function () {
assertType('true', array_is_list($a));
unset($a[2]);
assertType('array{1, 2}', $a);
assertType('false', array_is_list($a));
assertType('true', array_is_list($a));
$a[] = 4;
assertType('array{0: 1, 1: 2, 3: 4}', $a);
assertType('false', array_is_list($a));
Expand Down
5 changes: 5 additions & 0 deletions tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1054,4 +1054,9 @@ public function testBug10715(): void
$this->analyse([__DIR__ . '/data/bug-10715.php'], []);
}

public function testBug11718(): void
{
$this->analyse([__DIR__ . '/data/bug-11718.php'], []);
}

}
18 changes: 18 additions & 0 deletions tests/PHPStan/Rules/Methods/data/bug-11718.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php declare(strict_types = 1);

namespace Bug11718;

class Foo
{
/**
* @return list<string>
*/
public function test(string $a, string $b): array
{
$list = ['x', 'y', $a, $b];

unset($list[3]); // no error if uncommented!

return $list;
}
}

0 comments on commit a287ab8

Please sign in to comment.