Skip to content

Commit

Permalink
Retain list type when assigning to offset 1 of non-empty-list
Browse files Browse the repository at this point in the history
  • Loading branch information
herndlm authored Nov 27, 2024
1 parent 970117e commit 0b925a9
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 1 deletion.
9 changes: 8 additions & 1 deletion src/Type/IntersectionType.php
Original file line number Diff line number Diff line change
Expand Up @@ -756,7 +756,14 @@ public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $uni
);
});
}
return $this->intersectTypes(static fn (Type $type): Type => $type->setOffsetValueType($offsetType, $valueType, $unionValues));

$result = $this->intersectTypes(static fn (Type $type): Type => $type->setOffsetValueType($offsetType, $valueType, $unionValues));

if ($offsetType !== null && $this->isList()->yes() && $this->isIterableAtLeastOnce()->yes() && (new ConstantIntegerType(1))->isSuperTypeOf($offsetType)->yes()) {
$result = AccessoryArrayListType::intersectWith($result);
}

return $result;
}

public function setExistingOffsetValueType(Type $offsetType, Type $valueType): Type
Expand Down
25 changes: 25 additions & 0 deletions tests/PHPStan/Analyser/nsrt/list-type.php
Original file line number Diff line number Diff line change
Expand Up @@ -106,4 +106,29 @@ public function testUnset(array $list): void
assertType('array<int<0, 1>|int<3, max>, int>', $list);
}

/** @param list<int> $list */
public function testSetOffsetExplicitlyWithoutGap(array $list): void
{
assertType('list<int>', $list);
$list[0] = 17;
assertType('non-empty-list<int>&hasOffsetValue(0, 17)', $list);
$list[1] = 19;
assertType('non-empty-list<int>&hasOffsetValue(0, 17)&hasOffsetValue(1, 19)', $list);
$list[0] = 21;
assertType('non-empty-list<int>&hasOffsetValue(0, 21)&hasOffsetValue(1, 19)', $list);

$list[2] = 23;
assertType('non-empty-array<int<0, max>, int>&hasOffsetValue(0, 21)&hasOffsetValue(1, 19)&hasOffsetValue(2, 23)', $list);
}

/** @param list<int> $list */
public function testSetOffsetExplicitlyWithGap(array $list): void
{
assertType('list<int>', $list);
$list[0] = 17;
assertType('non-empty-list<int>&hasOffsetValue(0, 17)', $list);
$list[2] = 21;
assertType('non-empty-array<int<0, max>, int>&hasOffsetValue(0, 17)&hasOffsetValue(2, 21)', $list);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -692,4 +692,16 @@ public function testBug11617(): void
]);
}

public function testBug12131(): void
{
$this->checkExplicitMixed = true;
$this->analyse([__DIR__ . '/data/bug-12131.php'], [
[
'Property Bug12131\Test::$array (non-empty-list<int>) does not accept non-empty-array<int<0, max>, int>.',
29,
'non-empty-array<int<0, max>, int> might not be a list.',
],
]);
}

}
31 changes: 31 additions & 0 deletions tests/PHPStan/Rules/Properties/data/bug-12131.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php declare(strict_types = 1); // lint >= 7.4

namespace Bug12131;

class Test
{
/**
* @var non-empty-list<int>
*/
public array $array;

public function __construct()
{
$this->array = array_fill(0, 10, 1);
}

public function setAtZero(): void
{
$this->array[0] = 1;
}

public function setAtOne(): void
{
$this->array[1] = 1;
}

public function setAtTwo(): void
{
$this->array[2] = 1;
}
}

0 comments on commit 0b925a9

Please sign in to comment.