diff --git a/src/Psalm/Internal/Type/ParseTreeCreator.php b/src/Psalm/Internal/Type/ParseTreeCreator.php index edad53cb928..15256e5dedc 100644 --- a/src/Psalm/Internal/Type/ParseTreeCreator.php +++ b/src/Psalm/Internal/Type/ParseTreeCreator.php @@ -751,6 +751,13 @@ private function handleValue(array $type_token): void $new_parent ); ++$this->t; + + $nexter_token = $this->t + 1 < $this->type_token_count ? $this->type_tokens[$this->t + 1] : null; + + if ($nexter_token !== null && $nexter_token[0] === '}') { + $new_leaf->terminated = true; + ++$this->t; + } break; case '(': diff --git a/src/Psalm/Internal/Type/TypeParser.php b/src/Psalm/Internal/Type/TypeParser.php index 26e2658ca4b..d7b9f8268e6 100644 --- a/src/Psalm/Internal/Type/TypeParser.php +++ b/src/Psalm/Internal/Type/TypeParser.php @@ -1235,7 +1235,7 @@ private static function getTypeFromIndexAccessTree( /** * @param array> $template_type_map * @param array $type_aliases - * @return TCallableKeyedArray|TKeyedArray|TObjectWithProperties + * @return TCallableKeyedArray|TKeyedArray|TObjectWithProperties|TArray * @throws TypeParseTreeException */ private static function getTypeFromKeyedArrayTree( @@ -1314,7 +1314,7 @@ private static function getTypeFromKeyedArrayTree( } if (!$properties) { - throw new TypeParseTreeException('No properties supplied for TKeyedArray'); + return new TArray([Type::getNever(), Type::getNever()]); } if ($type === 'object') { diff --git a/tests/TypeParseTest.php b/tests/TypeParseTest.php index 30ea08a39a0..d2f29297ecc 100644 --- a/tests/TypeParseTest.php +++ b/tests/TypeParseTest.php @@ -893,6 +893,14 @@ public function testSingleLiteralString(): void ); } + public function testEmptyArrayShape(): void + { + $this->assertSame( + 'array', + (string)Type::parseString('array{}') + ); + } + public function testSingleLiteralInt(): void { $this->assertSame(