Merge pull request #2534 from rectorphp/generic

improve generic type conversion
This commit is contained in:
Tomas Votruba 2019-12-31 00:16:19 +01:00 committed by GitHub
commit 24173b0290
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 72 additions and 33 deletions

View File

@ -611,7 +611,6 @@ final class StaticTypeMapper
}
// @todo improve - making many false positives now
$objectType = new ObjectType($typeNode->name);
return $this->objectTypeSpecifier->narrowToFullyQualifiedOrAlaisedObjectType($node, $objectType);
@ -644,45 +643,33 @@ final class StaticTypeMapper
}
if ($typeNode instanceof GenericTypeNode) {
if ($typeNode->type instanceof IdentifierTypeNode) {
$typeName = $typeNode->type->name;
$genericMainType = $this->mapPHPStanPhpDocTypeNodeToPHPStanType($typeNode->type, $node);
// remove extra prefix
$typeName = ltrim($typeName, '\\');
if ($genericMainType instanceof TypeWithClassName) {
$mainTypeAsString = $genericMainType->getClassName();
} else {
$mainTypeAsString = $typeNode->type->name;
}
if (in_array($typeName, ['array', 'iterable', 'Traversable'], true)) {
$genericTypes = [];
foreach ($typeNode->genericTypes as $genericTypeNode) {
$genericTypes[] = $this->mapPHPStanPhpDocTypeNodeToPHPStanType($genericTypeNode, $node);
}
$genericTypes = [];
foreach ($typeNode->genericTypes as $genericTypeNode) {
$genericTypes[] = $this->mapPHPStanPhpDocTypeNodeToPHPStanType($genericTypeNode, $node);
}
$genericType = $this->typeFactory->createMixedPassedOrUnionType($genericTypes);
// special use case for array
if (in_array($mainTypeAsString, ['array', 'iterable'], true)) {
$genericType = $this->typeFactory->createMixedPassedOrUnionType($genericTypes);
if ($typeName === 'array') {
return new ArrayType(new MixedType(), $genericType);
}
if ($typeName === 'Traversable') {
return new ObjectType('Traversable');
}
if ($mainTypeAsString === 'array') {
return new ArrayType(new MixedType(), $genericType);
}
if ($mainTypeAsString === 'iterable') {
return new IterableType(new MixedType(), $genericType);
}
}
$mainType = $this->mapPHPStanPhpDocTypeNodeToPHPStanType($typeNode->type, $node);
if ($mainType instanceof TypeWithClassName) {
$className = $mainType->getClassName();
} else {
throw new NotImplementedException();
}
$genericTypes = [];
foreach ($typeNode->genericTypes as $genericType) {
$genericTypes[] = $this->mapPHPStanPhpDocTypeNodeToPHPStanType($genericType, $node);
}
return new GenericObjectType($className, $genericTypes);
return new GenericObjectType($mainTypeAsString, $genericTypes);
}
throw new NotImplementedException(__METHOD__ . ' for ' . get_class($typeNode));

View File

@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
namespace Rector\NodeTypeResolver\Tests\StaticTypeMapper;
use Iterator;
use PhpParser\Node\Scalar\String_;
use PHPStan\PhpDocParser\Ast\Type\GenericTypeNode;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\Type\Generic\GenericObjectType;
use PHPStan\Type\IterableType;
use Rector\HttpKernel\RectorKernel;
use Rector\NodeTypeResolver\StaticTypeMapper;
use Symplify\PackageBuilder\Tests\AbstractKernelTestCase;
final class StaticTypeMapperTest extends AbstractKernelTestCase
{
/**
* @var StaticTypeMapper
*/
private $staticTypeMapper;
protected function setUp(): void
{
$this->bootKernel(RectorKernel::class);
$this->staticTypeMapper = self::$container->get(StaticTypeMapper::class);
}
/**
* @dataProvider provideDataForMapPHPStanPhpDocTypeNodeToPHPStanType()
*/
public function testMapPHPStanPhpDocTypeNodeToPHPStanType(TypeNode $typeNode, string $expectedType): void
{
$node = new String_('hey');
$phpStanType = $this->staticTypeMapper->mapPHPStanPhpDocTypeNodeToPHPStanType($typeNode, $node);
$this->assertInstanceOf($expectedType, $phpStanType);
}
public function provideDataForMapPHPStanPhpDocTypeNodeToPHPStanType(): Iterator
{
$genericTypeNode = new GenericTypeNode(new IdentifierTypeNode('Traversable'), []);
yield [$genericTypeNode, GenericObjectType::class];
$genericTypeNode = new GenericTypeNode(new IdentifierTypeNode('iterable'), []);
yield [$genericTypeNode, IterableType::class];
}
}

View File

@ -243,7 +243,7 @@ parameters:
- '#Method Rector\\BetterPhpDocParser\\PhpDocNodeFactory\\Gedmo\\(.*?)\:\:createFromNodeAndTokens\(\) should return Rector\\BetterPhpDocParser\\PhpDocNode\\Gedmo\\(.*?)\|null but returns PHPStan\\PhpDocParser\\Ast\\PhpDoc\\PhpDocTagValueNode\|null#'
- '#Access to an undefined property PhpParser\\Node\\Identifier\|PhpParser\\Node\\Name\|PhpParser\\Node\\NullableType\:\:\$type#'
- '#Call to an undefined method PhpParser\\Node\\Identifier\|PhpParser\\Node\\Name\|PhpParser\\Node\\NullableType\:\:toString\(\)#'
- '#Parameter \#1 \$expected of method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\) expects class\-string<object\>, string given#'
- '#Unable to resolve the template type ExpectedType in call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\)#'