Skip to content

Commit

Permalink
Add list pseudo-type
Browse files Browse the repository at this point in the history
  • Loading branch information
enumag committed Oct 29, 2021
1 parent 9dc4bb0 commit b755ce9
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 3 deletions.
28 changes: 28 additions & 0 deletions src/PseudoTypes/List_.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php declare(strict_types = 1);

namespace phpDocumentor\Reflection\PseudoTypes;

use phpDocumentor\Reflection\Type;
use phpDocumentor\Reflection\Types\Array_;
use phpDocumentor\Reflection\Types\Integer;
use phpDocumentor\Reflection\Types\Mixed_;

final class List_ extends Array_
{
public function __construct(?Type $valueType = null)
{
parent::__construct($valueType, new Integer());
}

/**
* Returns a rendered output of the Type as it would be used in a DocBlock.
*/
public function __toString(): string
{
if ($this->valueType instanceof Mixed_) {
return 'list';
}

return 'list<' . $this->valueType . '>';
}
}
10 changes: 8 additions & 2 deletions src/TypeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
use phpDocumentor\Reflection\Types\InterfaceString;
use phpDocumentor\Reflection\Types\Intersection;
use phpDocumentor\Reflection\Types\Iterable_;
use phpDocumentor\Reflection\PseudoTypes\List_;
use phpDocumentor\Reflection\Types\Nullable;
use phpDocumentor\Reflection\Types\Object_;
use phpDocumentor\Reflection\Types\String_;
Expand Down Expand Up @@ -521,10 +522,11 @@ private function resolveCollection(ArrayIterator $tokens, Type $classType, Conte
{
$isArray = ((string) $classType === 'array');
$isIterable = ((string) $classType === 'iterable');
$isList = ((string) $classType === 'list');

// allow only "array", "iterable" or class name before "<"
if (
!$isArray && !$isIterable
!$isArray && !$isIterable && !$isList
&& (!$classType instanceof Object_ || $classType->getFqsen() === null)
) {
throw new RuntimeException(
Expand All @@ -538,7 +540,7 @@ private function resolveCollection(ArrayIterator $tokens, Type $classType, Conte
$keyType = null;

$token = $tokens->current();
if ($token !== null && trim($token) === ',') {
if ($token !== null && trim($token) === ',' && !$isList) {
// if we have a comma, then we just parsed the key type, not the value type
$keyType = $valueType;
if ($isArray) {
Expand Down Expand Up @@ -596,6 +598,10 @@ private function resolveCollection(ArrayIterator $tokens, Type $classType, Conte
return new Iterable_($valueType, $keyType);
}

if ($isList) {
return new List_($valueType);
}

if ($classType instanceof Object_) {
return $this->makeCollectionFromObject($classType, $valueType, $keyType);
}
Expand Down
2 changes: 1 addition & 1 deletion src/Types/Array_.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,6 @@
*
* @psalm-immutable
*/
final class Array_ extends AbstractList
class Array_ extends AbstractList
{
}
25 changes: 25 additions & 0 deletions tests/unit/CollectionResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

namespace phpDocumentor\Reflection;

use phpDocumentor\Reflection\PseudoTypes\List_;
use phpDocumentor\Reflection\Types\Array_;
use phpDocumentor\Reflection\Types\Collection;
use phpDocumentor\Reflection\Types\Context;
Expand Down Expand Up @@ -297,4 +298,28 @@ public function testResolvingCollectionAsArray(): void
$this->assertInstanceOf(Types\Float_::class, $valueType);
$this->assertInstanceOf(Types\String_::class, $keyType);
}

/**
* @uses \phpDocumentor\Reflection\Types\Context
* @uses \phpDocumentor\Reflection\Types\String_
*
* @covers ::__construct
* @covers ::resolve
*/
public function testResolvingList(): void
{
$fixture = new TypeResolver();

$resolvedType = $fixture->resolve('list<string>', new Context(''));

$this->assertInstanceOf(List_::class, $resolvedType);
$this->assertSame('list<string>', (string) $resolvedType);

$valueType = $resolvedType->getValueType();

$keyType = $resolvedType->getKeyType();

$this->assertInstanceOf(Types\String_::class, $valueType);
$this->assertInstanceOf(Types\Integer::class, $keyType);
}
}
48 changes: 48 additions & 0 deletions tests/unit/PseudoTypes/ListTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

declare(strict_types=1);

/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @link http://phpdoc.org
*/

namespace phpDocumentor\Reflection\PseudoTypes;

use phpDocumentor\Reflection\Types\Compound;
use phpDocumentor\Reflection\Types\Integer;
use phpDocumentor\Reflection\Types\Mixed_;
use phpDocumentor\Reflection\Types\String_;
use PHPUnit\Framework\TestCase;

/**
* @coversDefaultClass \phpDocumentor\Reflection\PseudoTypes\List_
*/
class ListTest extends TestCase
{
/**
* @dataProvider provideArrays
* @covers ::__toString
*/
public function testArrayStringifyCorrectly(List_ $array, string $expectedString): void
{
$this->assertSame($expectedString, (string) $array);
}

/**
* @return mixed[]
*/
public function provideArrays(): array
{
return [
'simple list' => [new List_(), 'list'],
'list of mixed' => [new List_(new Mixed_()), 'list'],
'list of single type' => [new List_(new String_()), 'list<string>'],
'list of compound type' => [new List_(new Compound([new Integer(), new String_()])), 'list<int|string>'],
];
}
}
1 change: 1 addition & 0 deletions tests/unit/TypeResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -759,6 +759,7 @@ public function provideKeywords(): array
['iterable', Types\Iterable_::class],
['never', Types\Never_::class],
['literal-string', PseudoTypes\LiteralString::class],
['list', PseudoTypes\List_::class],
];
}

Expand Down

0 comments on commit b755ce9

Please sign in to comment.