Skip to content

Commit 97f0039

Browse files
committed
Array shape with optional keys might be a list
1 parent b71aa34 commit 97f0039

File tree

4 files changed

+34
-3
lines changed

4 files changed

+34
-3
lines changed

src/Type/Constant/ConstantArrayTypeBuilder.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,11 @@ public function setOffsetValueType(?Type $offsetType, Type $valueType, bool $opt
163163
$min = min($this->nextAutoIndexes);
164164
$max = max($this->nextAutoIndexes);
165165
if ($offsetType->getValue() > $min) {
166-
$this->isList = TrinaryLogic::createNo();
166+
if ($offsetType->getValue() <= $max) {
167+
$this->isList = $this->isList->and(TrinaryLogic::createMaybe());
168+
} else {
169+
$this->isList = TrinaryLogic::createNo();
170+
}
167171
}
168172
if ($offsetType->getValue() >= $max) {
169173
/** @var int|float $newAutoIndex */

tests/PHPStan/Analyser/NodeScopeResolverTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ public function dataFileAsserts(): iterable
161161
yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Comparison/data/bug-2550.php');
162162
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-2899.php');
163163
yield from $this->gatherAssertTypes(__DIR__ . '/data/preg_split.php');
164+
yield from $this->gatherAssertTypes(__DIR__ . '/data/array-shape-list-optional.php');
164165

165166
if (PHP_VERSION_ID < 80000) {
166167
yield from $this->gatherAssertTypes(__DIR__ . '/data/bcmath-dynamic-return-php7.php');
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
namespace ArrayShapeListOptional;
4+
5+
use function PHPStan\Testing\assertType;
6+
7+
class Foo
8+
{
9+
10+
/**
11+
* @param list{0: string, 1: int, 2?: string, 3?: string} $valid1
12+
* @param list{0: string, 1: int, 2?: string, 4?: string} $invalid1
13+
* @param list{0: string, 1: int, 2?: string, foo?: string} $invalid2
14+
*/
15+
public function doFoo(
16+
$valid1,
17+
$invalid1,
18+
$invalid2
19+
): void
20+
{
21+
assertType('array{0: string, 1: int, 2?: string, 3?: string}&list', $valid1);
22+
assertType('*NEVER*', $invalid1);
23+
assertType('*NEVER*', $invalid2);
24+
}
25+
26+
}

tests/PHPStan/Rules/Methods/ReturnTypeRuleTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -924,12 +924,12 @@ public function testLists(): void
924924
[
925925
"Method ReturnList\Foo::getList1() should return list<string> but returns array{0?: 'foo', 1?: 'bar'}.",
926926
10,
927-
"array{0?: 'foo', 1?: 'bar'} is not a list.",
927+
"array{0?: 'foo', 1?: 'bar'} might not be a list.",
928928
],
929929
[
930930
"Method ReturnList\Foo::getList2() should return list<string> but returns array{0?: 'foo', 1?: 'bar'}.",
931931
19,
932-
"array{0?: 'foo', 1?: 'bar'} is not a list.",
932+
"array{0?: 'foo', 1?: 'bar'} might not be a list.",
933933
],
934934
]);
935935
}

0 commit comments

Comments
 (0)