diff --git a/packages/NodeTypeResolver/src/StaticTypeMapper.php b/packages/NodeTypeResolver/src/StaticTypeMapper.php index d88882f7b4f..d67114d5d22 100644 --- a/packages/NodeTypeResolver/src/StaticTypeMapper.php +++ b/packages/NodeTypeResolver/src/StaticTypeMapper.php @@ -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)); diff --git a/packages/NodeTypeResolver/tests/StaticTypeMapper/StaticTypeMapperTest.php b/packages/NodeTypeResolver/tests/StaticTypeMapper/StaticTypeMapperTest.php new file mode 100644 index 00000000000..2c28c3f82e4 --- /dev/null +++ b/packages/NodeTypeResolver/tests/StaticTypeMapper/StaticTypeMapperTest.php @@ -0,0 +1,52 @@ +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]; + } +} diff --git a/phpstan.neon b/phpstan.neon index d2ebf26553a..3036261e701 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -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, string given#' + - '#Unable to resolve the template type ExpectedType in call to method PHPUnit\\Framework\\Assert\:\:assertInstanceOf\(\)#'