mirror of
https://github.com/danog/psalm.git
synced 2024-11-26 20:34:47 +01:00
Use objects for type parsing
This commit is contained in:
parent
24490aac0e
commit
3f90bceabf
@ -51,10 +51,6 @@ abstract class Type
|
||||
|
||||
$type_string = preg_replace('/\?(?=[a-zA-Z])/', 'null|', $type_string);
|
||||
|
||||
if (preg_match('/[\[\]()]/', $type_string)) {
|
||||
throw new TypeParseTreeException('Invalid characters in type');
|
||||
}
|
||||
|
||||
$type_tokens = self::tokenize($type_string);
|
||||
|
||||
if (count($type_tokens) === 1) {
|
||||
@ -68,8 +64,6 @@ abstract class Type
|
||||
$parsed_type = self::getTypeFromTree($parse_tree, $php_compatible);
|
||||
} catch (TypeParseTreeException $e) {
|
||||
throw $e;
|
||||
} catch (\Exception $e) {
|
||||
throw new TypeParseTreeException($e->getMessage());
|
||||
}
|
||||
|
||||
if (!($parsed_type instanceof Union)) {
|
||||
@ -131,12 +125,8 @@ abstract class Type
|
||||
*/
|
||||
private static function getTypeFromTree(ParseTree $parse_tree, $php_compatible)
|
||||
{
|
||||
if (!$parse_tree->value) {
|
||||
throw new \InvalidArgumentException('Parse tree must have a value');
|
||||
}
|
||||
|
||||
if ($parse_tree->value === ParseTree::GENERIC) {
|
||||
$generic_type = array_shift($parse_tree->children);
|
||||
if ($parse_tree instanceof ParseTree\GenericTree) {
|
||||
$generic_type = $parse_tree->value;
|
||||
|
||||
$generic_params = array_map(
|
||||
/**
|
||||
@ -150,11 +140,7 @@ abstract class Type
|
||||
$parse_tree->children
|
||||
);
|
||||
|
||||
if (!$generic_type->value) {
|
||||
throw new \InvalidArgumentException('Generic type must have a value');
|
||||
}
|
||||
|
||||
$generic_type_value = self::fixScalarTerms($generic_type->value, false);
|
||||
$generic_type_value = self::fixScalarTerms($generic_type, false);
|
||||
|
||||
if (($generic_type_value === 'array' || $generic_type_value === 'Generator') &&
|
||||
count($generic_params) === 1
|
||||
@ -173,7 +159,7 @@ abstract class Type
|
||||
return new TGenericObject($generic_type_value, $generic_params);
|
||||
}
|
||||
|
||||
if ($parse_tree->value === ParseTree::UNION) {
|
||||
if ($parse_tree instanceof ParseTree\UnionTree) {
|
||||
$union_types = array_map(
|
||||
/**
|
||||
* @return Atomic
|
||||
@ -195,7 +181,7 @@ abstract class Type
|
||||
return self::combineTypes($union_types);
|
||||
}
|
||||
|
||||
if ($parse_tree->value === ParseTree::INTERSECTION) {
|
||||
if ($parse_tree instanceof ParseTree\IntersectionTree) {
|
||||
$intersection_types = array_map(
|
||||
/**
|
||||
* @return Atomic
|
||||
@ -228,22 +214,24 @@ abstract class Type
|
||||
return new Type\Union([$first_type]);
|
||||
}
|
||||
|
||||
if ($parse_tree->value === ParseTree::OBJECT_LIKE) {
|
||||
if ($parse_tree instanceof ParseTree\ObjectLikeTree) {
|
||||
$properties = [];
|
||||
|
||||
$type = array_shift($parse_tree->children);
|
||||
$type = $parse_tree->value;
|
||||
|
||||
foreach ($parse_tree->children as $i => $property_branch) {
|
||||
if ($property_branch->value !== ParseTree::OBJECT_PROPERTY) {
|
||||
if (!$property_branch instanceof ParseTree\ObjectLikePropertyTree) {
|
||||
$property_type = self::getTypeFromTree($property_branch, false);
|
||||
$property_maybe_undefined = false;
|
||||
$property_key = (string)$i;
|
||||
} elseif (count($property_branch->children) === 2) {
|
||||
$property_type = self::getTypeFromTree($property_branch->children[1], false);
|
||||
} elseif (count($property_branch->children) === 1) {
|
||||
$property_type = self::getTypeFromTree($property_branch->children[0], false);
|
||||
$property_maybe_undefined = $property_branch->possibly_undefined;
|
||||
$property_key = (string)($property_branch->children[0]->value);
|
||||
$property_key = $property_branch->value;
|
||||
} else {
|
||||
throw new \InvalidArgumentException('Unexpected number of property parts');
|
||||
throw new \InvalidArgumentException(
|
||||
'Unexpected number of property parts (' . count($property_branch->children) . ')'
|
||||
);
|
||||
}
|
||||
|
||||
if (!$property_type instanceof Union) {
|
||||
@ -257,7 +245,7 @@ abstract class Type
|
||||
$properties[$property_key] = $property_type;
|
||||
}
|
||||
|
||||
if ($type->value !== 'array') {
|
||||
if ($type !== 'array') {
|
||||
throw new \InvalidArgumentException('Object-like type must be array');
|
||||
}
|
||||
|
||||
@ -268,6 +256,10 @@ abstract class Type
|
||||
return new ObjectLike($properties);
|
||||
}
|
||||
|
||||
if (!$parse_tree instanceof ParseTree\Value) {
|
||||
throw new \InvalidArgumentException('Unrecognised parse tree type');
|
||||
}
|
||||
|
||||
$atomic_type = self::fixScalarTerms($parse_tree->value, $php_compatible);
|
||||
|
||||
return Atomic::create($atomic_type, $php_compatible);
|
||||
@ -309,6 +301,8 @@ abstract class Type
|
||||
$char === '}' ||
|
||||
$char === '[' ||
|
||||
$char === ']' ||
|
||||
$char === '(' ||
|
||||
$char === ')' ||
|
||||
$char === ' ' ||
|
||||
$char === '&' ||
|
||||
$char === ':'
|
||||
|
@ -5,22 +5,11 @@ use Psalm\Exception\TypeParseTreeException;
|
||||
|
||||
class ParseTree
|
||||
{
|
||||
const GENERIC = '<>';
|
||||
const OBJECT_LIKE = '{}';
|
||||
const OBJECT_PROPERTY = ':';
|
||||
const UNION = '|';
|
||||
const INTERSECTION = '&';
|
||||
|
||||
/**
|
||||
* @var array<int, ParseTree>
|
||||
*/
|
||||
public $children = [];
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
public $value;
|
||||
|
||||
/**
|
||||
* @var null|ParseTree
|
||||
*/
|
||||
@ -32,12 +21,10 @@ class ParseTree
|
||||
public $possibly_undefined = false;
|
||||
|
||||
/**
|
||||
* @param string|null $value
|
||||
* @param ParseTree|null $parent
|
||||
*/
|
||||
public function __construct($value, ParseTree $parent = null)
|
||||
public function __construct(ParseTree $parent = null)
|
||||
{
|
||||
$this->value = $value;
|
||||
$this->parent = $parent;
|
||||
}
|
||||
|
||||
@ -51,35 +38,19 @@ class ParseTree
|
||||
public static function createFromTokens(array $type_tokens)
|
||||
{
|
||||
// We construct a parse tree corresponding to the type
|
||||
$parse_tree = new self(null, null);
|
||||
$parse_tree = new ParseTree\Root();
|
||||
|
||||
$current_leaf = $parse_tree;
|
||||
|
||||
$last_token = null;
|
||||
|
||||
while ($type_tokens) {
|
||||
$type_token = array_shift($type_tokens);
|
||||
for ($i = 0, $c = count($type_tokens); $i < $c; ++$i) {
|
||||
$last_token = $i > 0 ? $type_tokens[$i - 1] : null;
|
||||
$type_token = $type_tokens[$i];
|
||||
$next_token = $i + 1 < $c ? $type_tokens[$i + 1] : null;
|
||||
|
||||
switch ($type_token) {
|
||||
case '<':
|
||||
case '{':
|
||||
$current_parent = $current_leaf->parent;
|
||||
$new_parent_leaf = new self(
|
||||
$type_token === '<' ? ParseTree::GENERIC : ParseTree::OBJECT_LIKE,
|
||||
$current_parent
|
||||
);
|
||||
|
||||
$new_parent_leaf->children = [$current_leaf];
|
||||
$current_leaf->parent = $new_parent_leaf;
|
||||
|
||||
if ($current_parent) {
|
||||
array_pop($current_parent->children);
|
||||
$current_parent->children[] = $new_parent_leaf;
|
||||
} else {
|
||||
$parse_tree = $new_parent_leaf;
|
||||
}
|
||||
|
||||
break;
|
||||
throw new TypeParseTreeException('Unexpected token');
|
||||
|
||||
case '>':
|
||||
do {
|
||||
@ -88,7 +59,7 @@ class ParseTree
|
||||
}
|
||||
|
||||
$current_leaf = $current_leaf->parent;
|
||||
} while ($current_leaf->value !== self::GENERIC);
|
||||
} while (!$current_leaf instanceof ParseTree\GenericTree);
|
||||
|
||||
break;
|
||||
|
||||
@ -99,7 +70,7 @@ class ParseTree
|
||||
}
|
||||
|
||||
$current_leaf = $current_leaf->parent;
|
||||
} while ($current_leaf->value !== self::OBJECT_LIKE);
|
||||
} while (!$current_leaf instanceof ParseTree\ObjectLikeTree);
|
||||
|
||||
break;
|
||||
|
||||
@ -112,9 +83,15 @@ class ParseTree
|
||||
|
||||
$context_node = $current_leaf;
|
||||
|
||||
while ($context_node &&
|
||||
$context_node->value !== self::GENERIC &&
|
||||
$context_node->value !== self::OBJECT_LIKE
|
||||
if ($context_node instanceof ParseTree\GenericTree
|
||||
|| $context_node instanceof ParseTree\ObjectLikeTree
|
||||
) {
|
||||
$context_node = $context_node->parent;
|
||||
}
|
||||
|
||||
while ($context_node
|
||||
&& !$context_node instanceof ParseTree\GenericTree
|
||||
&& !$context_node instanceof ParseTree\ObjectLikeTree
|
||||
) {
|
||||
$context_node = $context_node->parent;
|
||||
}
|
||||
@ -123,26 +100,14 @@ class ParseTree
|
||||
throw new TypeParseTreeException('Cannot parse comma in non-generic/array type');
|
||||
}
|
||||
|
||||
if ($context_node->value === self::GENERIC && $current_parent->value !== self::GENERIC) {
|
||||
if (!isset($current_parent->parent) || !$current_parent->parent->value) {
|
||||
throw new TypeParseTreeException('Cannot parse comma in non-generic/array type');
|
||||
}
|
||||
|
||||
$current_leaf = $current_leaf->parent;
|
||||
} elseif ($context_node->value === self::OBJECT_LIKE
|
||||
&& $current_parent->value !== self::OBJECT_LIKE
|
||||
) {
|
||||
do {
|
||||
$current_leaf = $current_leaf->parent;
|
||||
} while ($current_leaf->parent && $current_leaf->parent->value !== self::OBJECT_LIKE);
|
||||
}
|
||||
$current_leaf = $context_node;
|
||||
|
||||
break;
|
||||
|
||||
case ':':
|
||||
$current_parent = $current_leaf->parent;
|
||||
|
||||
if ($current_parent && $current_parent->value === ParseTree::OBJECT_PROPERTY) {
|
||||
if ($current_parent && $current_parent instanceof ParseTree\ObjectLikePropertyTree) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -150,14 +115,19 @@ class ParseTree
|
||||
throw new TypeParseTreeException('Cannot process colon without parent');
|
||||
}
|
||||
|
||||
$new_parent_leaf = new self(self::OBJECT_PROPERTY, $current_parent);
|
||||
$new_parent_leaf->children = [$current_leaf];
|
||||
if (!$current_leaf instanceof ParseTree\Value) {
|
||||
throw new TypeParseTreeException('Unexpected LHS of property');
|
||||
}
|
||||
|
||||
$new_parent_leaf = new ParseTree\ObjectLikePropertyTree($current_leaf->value, $current_parent);
|
||||
$new_parent_leaf->possibly_undefined = $last_token === '?';
|
||||
$current_leaf->parent = $new_parent_leaf;
|
||||
|
||||
array_pop($current_parent->children);
|
||||
$current_parent->children[] = $new_parent_leaf;
|
||||
|
||||
$current_leaf = $new_parent_leaf;
|
||||
|
||||
break;
|
||||
|
||||
case '?':
|
||||
@ -166,11 +136,12 @@ class ParseTree
|
||||
case '|':
|
||||
$current_parent = $current_leaf->parent;
|
||||
|
||||
if ($current_parent && $current_parent->value === ParseTree::UNION) {
|
||||
if ($current_parent && $current_parent instanceof ParseTree\UnionTree) {
|
||||
$current_leaf = $current_parent;
|
||||
continue;
|
||||
}
|
||||
|
||||
$new_parent_leaf = new self(self::UNION, $current_parent);
|
||||
$new_parent_leaf = new ParseTree\UnionTree($current_parent);
|
||||
$new_parent_leaf->children = [$current_leaf];
|
||||
$current_leaf->parent = $new_parent_leaf;
|
||||
|
||||
@ -181,16 +152,18 @@ class ParseTree
|
||||
$parse_tree = $new_parent_leaf;
|
||||
}
|
||||
|
||||
$current_leaf = $new_parent_leaf;
|
||||
|
||||
break;
|
||||
|
||||
case '&':
|
||||
$current_parent = $current_leaf->parent;
|
||||
|
||||
if ($current_parent && $current_parent->value === ParseTree::INTERSECTION) {
|
||||
if ($current_parent && $current_parent instanceof ParseTree\IntersectionTree) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$new_parent_leaf = new self(self::INTERSECTION, $current_parent);
|
||||
$new_parent_leaf = new ParseTree\IntersectionTree($current_parent);
|
||||
$new_parent_leaf->children = [$current_leaf];
|
||||
$current_leaf->parent = $new_parent_leaf;
|
||||
|
||||
@ -201,26 +174,52 @@ class ParseTree
|
||||
$parse_tree = $new_parent_leaf;
|
||||
}
|
||||
|
||||
$current_leaf = $new_parent_leaf;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
if ($current_leaf->value === null) {
|
||||
$current_leaf->value = $type_token;
|
||||
$new_parent = !$current_leaf instanceof ParseTree\Root ? $current_leaf : null;
|
||||
|
||||
switch ($next_token) {
|
||||
case '<':
|
||||
$new_leaf = new ParseTree\GenericTree(
|
||||
$type_token,
|
||||
$new_parent
|
||||
);
|
||||
++$i;
|
||||
break;
|
||||
|
||||
case '{':
|
||||
$new_leaf = new ParseTree\ObjectLikeTree(
|
||||
$type_token,
|
||||
$new_parent
|
||||
);
|
||||
++$i;
|
||||
break;
|
||||
|
||||
case '(':
|
||||
throw new TypeParseTreeException('Cannot process bracket yet');
|
||||
|
||||
default:
|
||||
$new_leaf = new ParseTree\Value(
|
||||
$type_token,
|
||||
$new_parent
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
if ($current_leaf instanceof ParseTree\Root) {
|
||||
$current_leaf = $parse_tree = $new_leaf;
|
||||
continue;
|
||||
}
|
||||
|
||||
$new_leaf = new self($type_token, $current_leaf->parent);
|
||||
|
||||
if (!isset($current_leaf->parent)) {
|
||||
throw new TypeParseTreeException('Current leaf must have a parent');
|
||||
if ($new_leaf->parent) {
|
||||
$new_leaf->parent->children[] = $new_leaf;
|
||||
}
|
||||
|
||||
$current_leaf->parent->children[] = $new_leaf;
|
||||
|
||||
$current_leaf = $new_leaf;
|
||||
}
|
||||
|
||||
$last_token = $type_token;
|
||||
}
|
||||
|
||||
return $parse_tree;
|
||||
|
20
src/Psalm/Type/ParseTree/GenericTree.php
Normal file
20
src/Psalm/Type/ParseTree/GenericTree.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
namespace Psalm\Type\ParseTree;
|
||||
|
||||
class GenericTree extends \Psalm\Type\ParseTree
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $value;
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @param \Psalm\Type\ParseTree|null $parent
|
||||
*/
|
||||
public function __construct($value, \Psalm\Type\ParseTree $parent = null)
|
||||
{
|
||||
$this->value = $value;
|
||||
$this->parent = $parent;
|
||||
}
|
||||
}
|
6
src/Psalm/Type/ParseTree/IntersectionTree.php
Normal file
6
src/Psalm/Type/ParseTree/IntersectionTree.php
Normal file
@ -0,0 +1,6 @@
|
||||
<?php
|
||||
namespace Psalm\Type\ParseTree;
|
||||
|
||||
class IntersectionTree extends \Psalm\Type\ParseTree
|
||||
{
|
||||
}
|
20
src/Psalm/Type/ParseTree/ObjectLikePropertyTree.php
Normal file
20
src/Psalm/Type/ParseTree/ObjectLikePropertyTree.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
namespace Psalm\Type\ParseTree;
|
||||
|
||||
class ObjectLikePropertyTree extends \Psalm\Type\ParseTree
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $value;
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @param \Psalm\Type\ParseTree|null $parent
|
||||
*/
|
||||
public function __construct($value, \Psalm\Type\ParseTree $parent = null)
|
||||
{
|
||||
$this->value = $value;
|
||||
$this->parent = $parent;
|
||||
}
|
||||
}
|
20
src/Psalm/Type/ParseTree/ObjectLikeTree.php
Normal file
20
src/Psalm/Type/ParseTree/ObjectLikeTree.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
namespace Psalm\Type\ParseTree;
|
||||
|
||||
class ObjectLikeTree extends \Psalm\Type\ParseTree
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $value;
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @param \Psalm\Type\ParseTree|null $parent
|
||||
*/
|
||||
public function __construct($value, \Psalm\Type\ParseTree $parent = null)
|
||||
{
|
||||
$this->value = $value;
|
||||
$this->parent = $parent;
|
||||
}
|
||||
}
|
6
src/Psalm/Type/ParseTree/Root.php
Normal file
6
src/Psalm/Type/ParseTree/Root.php
Normal file
@ -0,0 +1,6 @@
|
||||
<?php
|
||||
namespace Psalm\Type\ParseTree;
|
||||
|
||||
class Root extends \Psalm\Type\ParseTree
|
||||
{
|
||||
}
|
6
src/Psalm/Type/ParseTree/UnionTree.php
Normal file
6
src/Psalm/Type/ParseTree/UnionTree.php
Normal file
@ -0,0 +1,6 @@
|
||||
<?php
|
||||
namespace Psalm\Type\ParseTree;
|
||||
|
||||
class UnionTree extends \Psalm\Type\ParseTree
|
||||
{
|
||||
}
|
20
src/Psalm/Type/ParseTree/Value.php
Normal file
20
src/Psalm/Type/ParseTree/Value.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
namespace Psalm\Type\ParseTree;
|
||||
|
||||
class Value extends \Psalm\Type\ParseTree
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $value;
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @param \Psalm\Type\ParseTree|null $parent
|
||||
*/
|
||||
public function __construct($value, \Psalm\Type\ParseTree $parent = null)
|
||||
{
|
||||
$this->value = $value;
|
||||
$this->parent = $parent;
|
||||
}
|
||||
}
|
@ -10,7 +10,7 @@ class TypeParseTest extends TestCase
|
||||
*/
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
//parent::setUp();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -21,6 +21,14 @@ class TypeParseTest extends TestCase
|
||||
$this->assertSame('int|string', (string) Type::parseString('int|string'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testBoolOrIntOrString()
|
||||
{
|
||||
$this->assertSame('bool|int|string', (string) Type::parseString('bool|int|string'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
@ -37,6 +45,29 @@ class TypeParseTest extends TestCase
|
||||
$this->assertSame('array<int, int>', (string) Type::parseString('array<int, int>'));
|
||||
$this->assertSame('array<int, string>', (string) Type::parseString('array<int, string>'));
|
||||
$this->assertSame('array<int, static>', (string) Type::parseString('array<int, static>'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testArrayWithSingleArg()
|
||||
{
|
||||
$this->assertSame('array<mixed, int>', (string) Type::parseString('array<int>'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testArrayWithNestedSingleArg()
|
||||
{
|
||||
$this->assertSame('array<mixed, array<mixed, int>>', (string) Type::parseString('array<array<int>>'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testArrayWithUnion()
|
||||
{
|
||||
$this->assertSame('array<int|string, string>', (string) Type::parseString('array<int|string, string>'));
|
||||
}
|
||||
|
||||
@ -59,13 +90,48 @@ class TypeParseTest extends TestCase
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testPhpDocStyle()
|
||||
public function testPhpDocSimpleArray()
|
||||
{
|
||||
$this->assertSame('array<mixed, A>', (string) Type::parseString('A[]'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testPhpDocUnionArray()
|
||||
{
|
||||
$this->assertSame('array<mixed, A|B>', (string) Type::parseString('(A|B)[]'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testPhpDocMultiDimensionalArray()
|
||||
{
|
||||
$this->assertSame('array<mixed, array<mixed, A>>', (string) Type::parseString('A[][]'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testPhpDocMultidimensionalUnionArray()
|
||||
{
|
||||
$this->assertSame('array<mixed, array<mixed, A|B>>', (string) Type::parseString('(A|B)[][]'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testPhpDocUnionOfArrays()
|
||||
{
|
||||
$this->assertSame('array<mixed, A|B>', (string) Type::parseString('A[]|B[]'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testPhpDocUnionOfArraysOrObject()
|
||||
{
|
||||
$this->assertSame('array<mixed, A|B>|C', (string) Type::parseString('A[]|B[]|C'));
|
||||
}
|
||||
|
||||
@ -90,24 +156,49 @@ class TypeParseTest extends TestCase
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testObjectLike()
|
||||
public function testObjectLikeWithSimpleArgs()
|
||||
{
|
||||
$this->assertSame('array{a:int, b:string}', (string) Type::parseString('array{a:int, b:string}'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testObjectLikeWithUnionArgs()
|
||||
{
|
||||
$this->assertSame(
|
||||
'array{a:int|string, b:string}',
|
||||
(string) Type::parseString('array{a:int|string, b:string}')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testObjectLikeWithGenericArgs()
|
||||
{
|
||||
$this->assertSame(
|
||||
'array{a:array<int, string|int>, b:string}',
|
||||
(string) Type::parseString('array{a:array<int, string|int>, b:string}')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testObjectLikeWithIntKeysAndUnionArgs()
|
||||
{
|
||||
$this->assertSame(
|
||||
'array{0:stdClass|null}',
|
||||
(string)Type::parseString('array{stdClass|null}')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testObjectLikeWithIntKeysAndGenericArgs()
|
||||
{
|
||||
$this->assertSame(
|
||||
'array{0:array<mixed, mixed>}',
|
||||
(string)Type::parseString('array{array}')
|
||||
@ -117,11 +208,6 @@ class TypeParseTest extends TestCase
|
||||
'array{0:array<int, string>}',
|
||||
(string)Type::parseString('array{array<int, string>}')
|
||||
);
|
||||
|
||||
$this->assertSame(
|
||||
'array{a:int, b?:int}',
|
||||
(string)Type::parseString('array{a:int, b?:int}')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user