mirror of
https://github.com/rectorphp/rector.git
synced 2024-06-08 04:10:51 +00:00
[Type Declaration] Create new unit test for ArrayTypeMapper with fixes (#4054)
* Create new unit test for ArrayTypeMapper with fixes * Fix typo in method name that breaks all tests * Remove skip too many fixtures to implement the fix for these fixtures as a next PR.
This commit is contained in:
parent
190d7cf616
commit
d40309963c
|
@ -238,7 +238,8 @@
|
|||
"Rector\\Decomplex\\Tests\\": "rules/decomplex/tests",
|
||||
"Rector\\Downgrade\\Tests\\": "rules/downgrade/tests",
|
||||
"Rector\\SymfonyPhpConfig\\Tests\\": "rules/symfony-php-config/tests",
|
||||
"Rector\\Injection\\Tests\\": "rules/injection/tests"
|
||||
"Rector\\Injection\\Tests\\": "rules/injection/tests",
|
||||
"Rector\\PHPStanStaticTypeMapper\\Tests\\": "packages/phpstan-static-type-mapper/tests"
|
||||
},
|
||||
"classmap": [
|
||||
"rules/cakephp/tests/Rector/Name/ImplicitShortClassNameUseStatementRector/Source",
|
||||
|
|
|
@ -6,6 +6,7 @@ namespace Rector\AttributeAwarePhpDoc\Ast\Type;
|
|||
|
||||
use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\CallableTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode;
|
||||
use Rector\BetterPhpDocParser\Attributes\Attribute\AttributeTrait;
|
||||
use Rector\BetterPhpDocParser\Contract\PhpDocNode\AttributeAwareNodeInterface;
|
||||
|
||||
|
@ -21,6 +22,10 @@ final class AttributeAwareArrayTypeNode extends ArrayTypeNode implements Attribu
|
|||
return sprintf('(%s)[]', $typeAsString);
|
||||
}
|
||||
|
||||
if ($this->type instanceof UnionTypeNode || $this->type instanceof ArrayTypeNode) {
|
||||
return sprintf('array<%s>', $typeAsString);
|
||||
}
|
||||
|
||||
return $typeAsString . '[]';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,9 +7,7 @@ namespace Rector\PHPStanStaticTypeMapper\TypeMapper;
|
|||
use PhpParser\Node;
|
||||
use PhpParser\Node\Name;
|
||||
use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
|
||||
use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode;
|
||||
use PHPStan\Type\ArrayType;
|
||||
use PHPStan\Type\Constant\ConstantArrayType;
|
||||
use PHPStan\Type\Constant\ConstantIntegerType;
|
||||
|
@ -18,6 +16,7 @@ use PHPStan\Type\MixedType;
|
|||
use PHPStan\Type\NeverType;
|
||||
use PHPStan\Type\Type;
|
||||
use PHPStan\Type\UnionType;
|
||||
use Rector\AttributeAwarePhpDoc\Ast\Type\AttributeAwareArrayTypeNode;
|
||||
use Rector\AttributeAwarePhpDoc\Ast\Type\AttributeAwareGenericTypeNode;
|
||||
use Rector\AttributeAwarePhpDoc\Ast\Type\AttributeAwareIdentifierTypeNode;
|
||||
use Rector\AttributeAwarePhpDoc\Ast\Type\AttributeAwareUnionTypeNode;
|
||||
|
@ -58,16 +57,17 @@ final class ArrayTypeMapper implements TypeMapperInterface
|
|||
*/
|
||||
public function mapToPHPStanPhpDocTypeNode(Type $type): TypeNode
|
||||
{
|
||||
$itemTypeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($type->getItemType());
|
||||
$itemType = $type->getItemType();
|
||||
|
||||
if ($itemTypeNode instanceof UnionTypeNode) {
|
||||
return $this->convertUnionArrayTypeNodesToArrayTypeOfUnionTypeNodes($itemTypeNode);
|
||||
if ($itemType instanceof UnionType) {
|
||||
return $this->createUnionType($itemType);
|
||||
}
|
||||
|
||||
if ($this->isGenericArrayCandidate($type)) {
|
||||
return $this->createGenericArrayType($type->getKeyType(), $itemTypeNode);
|
||||
if ($itemType instanceof ArrayType || $this->isGenericArrayCandidate($type)) {
|
||||
return $this->createGenericArrayType($type, $this->isGenericArrayCandidate($type));
|
||||
}
|
||||
|
||||
$itemTypeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($itemType);
|
||||
return new ArrayTypeNode($itemTypeNode);
|
||||
}
|
||||
|
||||
|
@ -94,24 +94,21 @@ final class ArrayTypeMapper implements TypeMapperInterface
|
|||
return $this->phpStanStaticTypeMapper->mapToDocString($itemType, $parentType) . '[]';
|
||||
}
|
||||
|
||||
private function convertUnionArrayTypeNodesToArrayTypeOfUnionTypeNodes(
|
||||
UnionTypeNode $unionTypeNode
|
||||
): AttributeAwareUnionTypeNode {
|
||||
private function createUnionType(UnionType $unionType): ArrayTypeNode
|
||||
{
|
||||
$unionedArrayType = [];
|
||||
foreach ($unionTypeNode->types as $unionedType) {
|
||||
if ($unionedType instanceof UnionTypeNode) {
|
||||
foreach ($unionedType->types as $key => $subUnionedType) {
|
||||
$unionedType->types[$key] = new ArrayTypeNode($subUnionedType);
|
||||
}
|
||||
|
||||
$unionedArrayType[] = $unionedType;
|
||||
continue;
|
||||
}
|
||||
|
||||
$unionedArrayType[] = new ArrayTypeNode($unionedType);
|
||||
foreach ($unionType->getTypes() as $unionedType) {
|
||||
$typeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($unionedType);
|
||||
$unionedArrayType[(string) $typeNode] = $typeNode;
|
||||
}
|
||||
|
||||
return new AttributeAwareUnionTypeNode($unionedArrayType);
|
||||
if (count($unionedArrayType) > 1) {
|
||||
return new AttributeAwareArrayTypeNode(new AttributeAwareUnionTypeNode($unionedArrayType));
|
||||
}
|
||||
|
||||
/** @var TypeNode $arrayType */
|
||||
$arrayType = array_shift($unionedArrayType);
|
||||
return new AttributeAwareArrayTypeNode($arrayType);
|
||||
}
|
||||
|
||||
private function isGenericArrayCandidate(ArrayType $arrayType): bool
|
||||
|
@ -148,15 +145,20 @@ final class ArrayTypeMapper implements TypeMapperInterface
|
|||
return false;
|
||||
}
|
||||
|
||||
private function createGenericArrayType(Type $keyType, TypeNode $itemTypeNode): AttributeAwareGenericTypeNode
|
||||
private function createGenericArrayType(ArrayType $arrayType, bool $withKey = false): AttributeAwareGenericTypeNode
|
||||
{
|
||||
/** @var IdentifierTypeNode $keyTypeNode */
|
||||
$keyTypeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($keyType);
|
||||
$itemTypeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($arrayType->getItemType());
|
||||
|
||||
$attributeAwareIdentifierTypeNode = new AttributeAwareIdentifierTypeNode('array');
|
||||
|
||||
if ($withKey) {
|
||||
$keyTypeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($arrayType->getKeyType());
|
||||
$genericTypes = [$keyTypeNode, $itemTypeNode];
|
||||
} else {
|
||||
$genericTypes = [$itemTypeNode];
|
||||
}
|
||||
|
||||
// @see https://github.com/phpstan/phpdoc-parser/blob/98a088b17966bdf6ee25c8a4b634df313d8aa531/tests/PHPStan/Parser/PhpDocParserTest.php#L2692-L2696
|
||||
$genericTypes = [$keyTypeNode, $itemTypeNode];
|
||||
return new AttributeAwareGenericTypeNode($attributeAwareIdentifierTypeNode, $genericTypes);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Rector\PHPStanStaticTypeMapper\TypeMapper;
|
||||
|
||||
use Iterator;
|
||||
use PHPStan\Type\ArrayType;
|
||||
use PHPStan\Type\IntegerType;
|
||||
use PHPStan\Type\MixedType;
|
||||
use PHPStan\Type\StringType;
|
||||
use PHPStan\Type\UnionType;
|
||||
use Rector\Core\HttpKernel\RectorKernel;
|
||||
use Symplify\PackageBuilder\Tests\AbstractKernelTestCase;
|
||||
|
||||
class ArrayTypeMapperTest extends AbstractKernelTestCase
|
||||
{
|
||||
/**
|
||||
* @var ArrayTypeMapper
|
||||
*/
|
||||
private $arrayTypeMapper;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
self::bootKernel(RectorKernel::class);
|
||||
|
||||
$this->arrayTypeMapper = self::$container->get(ArrayTypeMapper::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideDataMapToPHPStanPhpDocTypeNode()
|
||||
*/
|
||||
public function testMapToPHPStanPhpDocTypeNode(ArrayType $arrayType, string $expectedResult): void
|
||||
{
|
||||
$actualTypeNode = $this->arrayTypeMapper->mapToPHPStanPhpDocTypeNode($arrayType);
|
||||
self::assertSame($expectedResult, (string) $actualTypeNode);
|
||||
}
|
||||
|
||||
public function provideDataMapToPHPStanPhpDocTypeNode(): Iterator
|
||||
{
|
||||
$arrayType = new ArrayType(new MixedType(), new StringType());
|
||||
yield[$arrayType, 'string[]'];
|
||||
|
||||
$unionArrayType = new ArrayType(new MixedType(), new UnionType([new StringType(), new IntegerType()]));
|
||||
yield [$unionArrayType, 'array<int|string>'];
|
||||
|
||||
$unionArrayType = new ArrayType(new MixedType(), new UnionType([new StringType(), new IntegerType()]));
|
||||
$moreNestedUnionArrayType = new ArrayType(new MixedType(), $unionArrayType);
|
||||
yield [$moreNestedUnionArrayType, 'array<array<int|string>>'];
|
||||
|
||||
$evenMoreNestedUnionArrayType = new ArrayType(new MixedType(), $moreNestedUnionArrayType);
|
||||
yield [$evenMoreNestedUnionArrayType, 'array<array<array<int|string>>>'];
|
||||
|
||||
$arrayType = new ArrayType(new MixedType(), new UnionType([
|
||||
new StringType(),
|
||||
new StringType(),
|
||||
new StringType(),
|
||||
]));
|
||||
yield[$arrayType, 'string[]'];
|
||||
|
||||
$arrayType = new ArrayType(new StringType(), new ArrayType(new MixedType(), new StringType()));
|
||||
yield[$arrayType, 'array<string, string[]>'];
|
||||
|
||||
$arrayType = new ArrayType(new StringType(), new ArrayType(new MixedType(), new UnionType([
|
||||
new StringType(),
|
||||
new IntegerType(),
|
||||
])));
|
||||
yield[$arrayType, 'array<string, array<int|string>>'];
|
||||
|
||||
$arrayType = new ArrayType(new StringType(), new IntegerType());
|
||||
yield[$arrayType, 'array<string, int>'];
|
||||
}
|
||||
}
|
|
@ -54,7 +54,6 @@ final class ReturnTypeInferer extends AbstractPriorityAwareTypeInferer
|
|||
|
||||
$type = $this->typeNormalizer->normalizeArrayTypeAndArrayNever($originalType);
|
||||
$type = $this->typeNormalizer->uniqueateConstantArrayType($type);
|
||||
$type = $this->typeNormalizer->normalizeArrayOfUnionToUnionArray($type);
|
||||
|
||||
// in case of void, check return type of children methods
|
||||
if ($type instanceof MixedType) {
|
||||
|
|
|
@ -25,7 +25,7 @@ use Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddArrayReturnDocTypeRector\
|
|||
final class FullyQualifiedName
|
||||
{
|
||||
/**
|
||||
* @return \Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddArrayReturnDocTypeRector\Source\ValidationResult[]|bool[]
|
||||
* @return array<\Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddArrayReturnDocTypeRector\Source\ValidationResult|bool>
|
||||
*/
|
||||
public function isValidDataProvider(): array
|
||||
{
|
||||
|
|
|
@ -25,7 +25,7 @@ use Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddArrayReturnDocTypeRector\
|
|||
final class FullyQualifiedNameNestedArray
|
||||
{
|
||||
/**
|
||||
* @return string[][]|\Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddArrayReturnDocTypeRector\Source\ValidationResult[][]
|
||||
* @return array<string, array<string|\Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddArrayReturnDocTypeRector\Source\ValidationResult>>
|
||||
*/
|
||||
public function getValidationErrorMessagesAsStringDataProvider(): array
|
||||
{
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddArrayReturnDocTypeRector\Fixture;
|
||||
|
||||
class SkipTooMany
|
||||
{
|
||||
/**
|
||||
* @return mixed[][]
|
||||
*/
|
||||
public function provideFilesToContent(): array
|
||||
{
|
||||
return [
|
||||
[__DIR__ . '/Yaml/spaces.yml', [
|
||||
'address' => [
|
||||
'street' => '742 Evergreen Terrace',
|
||||
],
|
||||
]],
|
||||
# arrays
|
||||
[__DIR__ . '/Yaml/list.yml', [
|
||||
'services' => ['SomeService', 'SomeService'],
|
||||
]],
|
||||
[__DIR__ . '/Yaml/array.yml', [
|
||||
'services' => [
|
||||
'SomeService' => null,
|
||||
],
|
||||
]],
|
||||
# multi lines
|
||||
[__DIR__ . '/Yaml/multi-lines.yml', ['perex' => 'Multi' . PHP_EOL . 'line']],
|
||||
];
|
||||
}
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Rector\TypeDeclaration\Tests\Rector\ClassMethod\AddArrayReturnDocTypeRector\Fixture;
|
||||
|
||||
use Nette\Utils\Strings;
|
||||
|
||||
final class SkipToOMany2
|
||||
{
|
||||
/**
|
||||
* @param string[] $packageNames
|
||||
*/
|
||||
public function createPackagesData(array $packageNames): array
|
||||
{
|
||||
$packagesData = [];
|
||||
|
||||
foreach ($packageNames as $packageName) {
|
||||
$packageKey = $this->createPackageKey($packageName);
|
||||
|
||||
$packageDownloads = $this->provideForPackage($packageName);
|
||||
if ($packageDownloads === []) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// complete relative number of downloads
|
||||
$totalDownloads = array_sum($packageDownloads[1]);
|
||||
|
||||
foreach ($packageDownloads[1] as $version => $absoluteDownloads) {
|
||||
$relativeRate = 100 * ($absoluteDownloads / $totalDownloads);
|
||||
|
||||
$packageDownloads[1][$version] = [
|
||||
'absolute_downloads' => $absoluteDownloads,
|
||||
'relative_downloads' => round($relativeRate, 1),
|
||||
'version_publish_date' => $this->provideForPackageAndVersion(
|
||||
$packageName,
|
||||
$version
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
$packagesData[$packageKey] = [
|
||||
'package_name' => $packageName,
|
||||
'package_short_name' => Strings::after($packageName, '/'),
|
||||
] + $packageDownloads;
|
||||
}
|
||||
|
||||
return $packagesData;
|
||||
}
|
||||
|
||||
private function createPackageKey(string $packageName): string
|
||||
{
|
||||
return Strings::replace($packageName, '#(/|-)#', '_');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int[][]
|
||||
*/
|
||||
private function provideForPackage(string $packageName): array
|
||||
{
|
||||
return [[1, 3]];
|
||||
}
|
||||
|
||||
private function provideForPackageAndVersion(): ?string
|
||||
{
|
||||
return '' ? null : 'string';
|
||||
}
|
||||
}
|
|
@ -45,7 +45,7 @@ abstract class AbstractRectorTestCase extends AbstractGenericRectorTestCase
|
|||
$inputFileInfo = $inputFileInfoAndExpectedFileInfo->getInputFileInfo();
|
||||
$this->nodeScopeResolver->setAnalysedFiles([$inputFileInfo->getRealPath()]);
|
||||
|
||||
$expectedFileInfo = $inputFileInfoAndExpectedFileInfo->getExpectedFilenfo();
|
||||
$expectedFileInfo = $inputFileInfoAndExpectedFileInfo->getExpectedFileInfo();
|
||||
$this->doTestFileMatchesExpectedContent($inputFileInfo, $expectedFileInfo, $fixtureFileInfo);
|
||||
|
||||
$this->originalTempFileInfo = $inputFileInfo;
|
||||
|
|
Loading…
Reference in New Issue
Block a user