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:
parent
e784128902
commit
247d30f74c
@ -1478,6 +1478,10 @@ class TypeParser
|
|||||||
$had_optional = true;
|
$had_optional = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isset($properties[$property_key])) {
|
||||||
|
throw new TypeParseTreeException("Duplicate key $property_key detected");
|
||||||
|
}
|
||||||
|
|
||||||
$properties[$property_key] = $property_type;
|
$properties[$property_key] = $property_type;
|
||||||
if ($class_string) {
|
if ($class_string) {
|
||||||
$class_strings[$property_key] = true;
|
$class_strings[$property_key] = true;
|
||||||
@ -1485,7 +1489,7 @@ class TypeParser
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($had_explicit && $had_implicit) {
|
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') {
|
if ($type === 'object') {
|
||||||
@ -1500,7 +1504,7 @@ class TypeParser
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($callable && !$properties) {
|
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') {
|
if ($type !== 'array' && $type !== 'list') {
|
||||||
@ -1508,7 +1512,7 @@ class TypeParser
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($type === 'list' && !$is_list) {
|
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) {
|
if (!$properties) {
|
||||||
@ -1520,7 +1524,7 @@ class TypeParser
|
|||||||
$class_strings,
|
$class_strings,
|
||||||
$sealed
|
$sealed
|
||||||
? null
|
? null
|
||||||
: [$is_list ? Type::getInt() : Type::getArrayKey(), Type::getMixed()],
|
: [$is_list ? Type::getListKey() : Type::getArrayKey(), Type::getMixed()],
|
||||||
$is_list,
|
$is_list,
|
||||||
$from_docblock,
|
$from_docblock,
|
||||||
);
|
);
|
||||||
|
@ -473,54 +473,60 @@ class TypeParseTest extends TestCase
|
|||||||
|
|
||||||
public function testTKeyedListNonList(): void
|
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}');
|
Type::parseString('list{a: 0, b: 1, c: 2}');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function testTKeyedListNonListOptional(): void
|
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}');
|
Type::parseString('list{a: 0, b?: 1, c?: 2}');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testTKeyedListNonListOptionalWrongOrder1(): void
|
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}');
|
Type::parseString('list{0?: 0, 1: 1, 2: 2}');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testTKeyedListNonListOptionalWrongOrder2(): void
|
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}');
|
Type::parseString('list{0: 0, 1?: 1, 2: 2}');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function testTKeyedListWrongOrder(): void
|
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}');
|
Type::parseString('list{1: 1, 0: 0}');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testTKeyedListNonListKeys(): void
|
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}');
|
Type::parseString('list{1: 1, 2: 2}');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testTKeyedListNoExplicitAndImplicitKeys(): void
|
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}');
|
Type::parseString('list{0: 0, 1}');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testTKeyedArrayNoExplicitAndImplicitKeys(): void
|
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}');
|
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
|
public function testSimpleCallable(): void
|
||||||
{
|
{
|
||||||
$this->assertSame(
|
$this->assertSame(
|
||||||
|
Loading…
Reference in New Issue
Block a user