1
0
mirror of https://github.com/danog/psalm.git synced 2024-11-26 20:34:47 +01:00

Detect duplicate keys in array shapes

This commit is contained in:
Daniil Gentili 2023-01-25 10:42:05 +01:00
parent e784128902
commit 247d30f74c
2 changed files with 22 additions and 12 deletions

View File

@ -1478,6 +1478,10 @@ class TypeParser
$had_optional = true;
}
if (isset($properties[$property_key])) {
throw new TypeParseTreeException("Duplicate key $property_key detected");
}
$properties[$property_key] = $property_type;
if ($class_string) {
$class_strings[$property_key] = true;
@ -1485,7 +1489,7 @@ class TypeParser
}
if ($had_explicit && $had_implicit) {
throw new TypeParseTreeException('Cannot mix explicit and implicit keys!');
throw new TypeParseTreeException('Cannot mix explicit and implicit keys');
}
if ($type === 'object') {
@ -1500,7 +1504,7 @@ class TypeParser
}
if ($callable && !$properties) {
throw new TypeParseTreeException('A callable array cannot be empty!');
throw new TypeParseTreeException('A callable array cannot be empty');
}
if ($type !== 'array' && $type !== 'list') {
@ -1508,7 +1512,7 @@ class TypeParser
}
if ($type === 'list' && !$is_list) {
throw new TypeParseTreeException('A list shape cannot describe a non-list!');
throw new TypeParseTreeException('A list shape cannot describe a non-list');
}
if (!$properties) {
@ -1520,7 +1524,7 @@ class TypeParser
$class_strings,
$sealed
? null
: [$is_list ? Type::getInt() : Type::getArrayKey(), Type::getMixed()],
: [$is_list ? Type::getListKey() : Type::getArrayKey(), Type::getMixed()],
$is_list,
$from_docblock,
);

View File

@ -473,54 +473,60 @@ class TypeParseTest extends TestCase
public function testTKeyedListNonList(): void
{
$this->expectExceptionMessage('A list shape cannot describe a non-list!');
$this->expectExceptionMessage('A list shape cannot describe a non-list');
Type::parseString('list{a: 0, b: 1, c: 2}');
}
public function testTKeyedListNonListOptional(): void
{
$this->expectExceptionMessage('A list shape cannot describe a non-list!');
$this->expectExceptionMessage('A list shape cannot describe a non-list');
Type::parseString('list{a: 0, b?: 1, c?: 2}');
}
public function testTKeyedListNonListOptionalWrongOrder1(): void
{
$this->expectExceptionMessage('A list shape cannot describe a non-list!');
$this->expectExceptionMessage('A list shape cannot describe a non-list');
Type::parseString('list{0?: 0, 1: 1, 2: 2}');
}
public function testTKeyedListNonListOptionalWrongOrder2(): void
{
$this->expectExceptionMessage('A list shape cannot describe a non-list!');
$this->expectExceptionMessage('A list shape cannot describe a non-list');
Type::parseString('list{0: 0, 1?: 1, 2: 2}');
}
public function testTKeyedListWrongOrder(): void
{
$this->expectExceptionMessage('A list shape cannot describe a non-list!');
$this->expectExceptionMessage('A list shape cannot describe a non-list');
Type::parseString('list{1: 1, 0: 0}');
}
public function testTKeyedListNonListKeys(): void
{
$this->expectExceptionMessage('A list shape cannot describe a non-list!');
$this->expectExceptionMessage('A list shape cannot describe a non-list');
Type::parseString('list{1: 1, 2: 2}');
}
public function testTKeyedListNoExplicitAndImplicitKeys(): void
{
$this->expectExceptionMessage('Cannot mix explicit and implicit keys!');
$this->expectExceptionMessage('Cannot mix explicit and implicit keys');
Type::parseString('list{0: 0, 1}');
}
public function testTKeyedArrayNoExplicitAndImplicitKeys(): void
{
$this->expectExceptionMessage('Cannot mix explicit and implicit keys!');
$this->expectExceptionMessage('Cannot mix explicit and implicit keys');
Type::parseString('array{0, test: 1}');
}
public function testTKeyedArrayNoDuplicateKeys(): void
{
$this->expectExceptionMessage('Duplicate key a detected');
Type::parseString('array{a: int, a: int}');
}
public function testSimpleCallable(): void
{
$this->assertSame(