Skip to content

Commit

Permalink
[CodingStyle] Handle too detailed pair class string array on VarConst…
Browse files Browse the repository at this point in the history
…antCommentRector (#897)

* [CodingStyle] Handle pair class string array on VarConstantCommentRector

* [CodingStyle] Handle pair class string array on VarConstantCommentRector

* too detailed

* Fixed 🎉

* Fixed 🎉

* phpstan

* phpstan

* rename fixture
  • Loading branch information
samsonasik committed Sep 17, 2021
1 parent 74386d2 commit 1e3e5df
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 8 deletions.
44 changes: 37 additions & 7 deletions packages/PHPStanStaticTypeMapper/TypeMapper/ArrayTypeMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
use PHPStan\Reflection\ReflectionProvider;
use PHPStan\Type\ArrayType;
use PHPStan\Type\ClassStringType;
use PHPStan\Type\Constant\ConstantArrayType;
use PHPStan\Type\Constant\ConstantIntegerType;
use PHPStan\Type\Generic\GenericClassStringType;
Expand All @@ -26,6 +27,8 @@
use Rector\PHPStanStaticTypeMapper\PHPStanStaticTypeMapper;
use Rector\PHPStanStaticTypeMapper\TypeAnalyzer\UnionTypeCommonTypeNarrower;
use Rector\PHPStanStaticTypeMapper\ValueObject\TypeKind;
use Rector\TypeDeclaration\NodeTypeAnalyzer\DetailedTypeAnalyzer;
use Rector\TypeDeclaration\TypeAnalyzer\GenericClassStringTypeNormalizer;
use Symfony\Contracts\Service\Attribute\Required;

/**
Expand All @@ -46,17 +49,25 @@ final class ArrayTypeMapper implements TypeMapperInterface

private ReflectionProvider $reflectionProvider;

private GenericClassStringTypeNormalizer $genericClassStringTypeNormalizer;

private DetailedTypeAnalyzer $detailedTypeAnalyzer;

// To avoid circular dependency

#[Required]
public function autowireArrayTypeMapper(
PHPStanStaticTypeMapper $phpStanStaticTypeMapper,
UnionTypeCommonTypeNarrower $unionTypeCommonTypeNarrower,
ReflectionProvider $reflectionProvider
ReflectionProvider $reflectionProvider,
GenericClassStringTypeNormalizer $genericClassStringTypeNormalizer,
DetailedTypeAnalyzer $detailedTypeAnalyzer
): void {
$this->phpStanStaticTypeMapper = $phpStanStaticTypeMapper;
$this->unionTypeCommonTypeNarrower = $unionTypeCommonTypeNarrower;
$this->reflectionProvider = $reflectionProvider;
$this->genericClassStringTypeNormalizer = $genericClassStringTypeNormalizer;
$this->detailedTypeAnalyzer = $detailedTypeAnalyzer;
}

/**
Expand Down Expand Up @@ -166,10 +177,8 @@ private function createGenericArrayType(
TypeKind $typeKind,
bool $withKey = false
): GenericTypeNode {
$itemTypeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode(
$arrayType->getItemType(),
$typeKind
);
$itemType = $arrayType->getItemType();
$itemTypeNode = $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode($itemType, $typeKind);
$identifierTypeNode = new IdentifierTypeNode('array');

// is class-string[] list only
Expand All @@ -182,7 +191,15 @@ private function createGenericArrayType(
$arrayType->getKeyType(),
$typeKind
);
$genericTypes = [$keyTypeNode, $itemTypeNode];

if ($itemTypeNode instanceof BracketsAwareUnionTypeNode && $this->isPairClassTooDetailed($itemType)) {
$genericTypes = [$keyTypeNode, $this->phpStanStaticTypeMapper->mapToPHPStanPhpDocTypeNode(
new ClassStringType(),
$typeKind
)];
} else {
$genericTypes = [$keyTypeNode, $itemTypeNode];
}
} else {
$genericTypes = [$itemTypeNode];
}
Expand All @@ -198,6 +215,19 @@ private function createGenericArrayType(
return new GenericTypeNode($identifierTypeNode, $genericTypes);
}

private function isPairClassTooDetailed(Type $itemType): bool
{
if (! $itemType instanceof UnionType) {
return false;
}

if (! $this->genericClassStringTypeNormalizer->isAllGenericClassStringType($itemType)) {
return false;
}

return $this->detailedTypeAnalyzer->isTooDetailed($itemType);
}

private function isIntegerKeyAndNonNestedArray(ArrayType $arrayType): bool
{
if (! $arrayType->getKeyType() instanceof IntegerType) {
Expand Down Expand Up @@ -234,7 +264,7 @@ private function narrowConstantArrayTypeOfUnionType(
private function createTypeNodeFromGenericClassStringType(
GenericClassStringType $genericClassStringType,
TypeKind $typeKind
): TypeNode {
): IdentifierTypeNode|GenericTypeNode {
$genericType = $genericClassStringType->getGenericType();
if ($genericType instanceof ObjectType && ! $this->reflectionProvider->hasClass($genericType->getClassName())) {
return new IdentifierTypeNode($genericType->getClassName());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace Rector\Tests\CodingStyle\Rector\ClassConst\VarConstantCommentRector\Fixture;

class PairClassStringArray
{
private const CLASSES = [
\stdClass::class => PairClassStringArray::class,
];
}

?>
-----
<?php

namespace Rector\Tests\CodingStyle\Rector\ClassConst\VarConstantCommentRector\Fixture;

class PairClassStringArray
{
/**
* @var array<string, class-string<\Rector\Tests\CodingStyle\Rector\ClassConst\VarConstantCommentRector\Fixture\PairClassStringArray>>
*/
private const CLASSES = [
\stdClass::class => PairClassStringArray::class,
];
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

namespace Rector\Tests\CodingStyle\Rector\ClassConst\VarConstantCommentRector\Fixture;

class A { }
class Aa { }
class B { }
class Bb { }
class C { }
class Cc { }
class D { }
class Dd { }

class PairClassStringArrayTooMany
{
private const CLASSES = [
A::class => Aa::class,
B::class => Bb::class,
C::class => Cc::class,
D::class => Dd::class,
];
}

?>
-----
<?php

namespace Rector\Tests\CodingStyle\Rector\ClassConst\VarConstantCommentRector\Fixture;

class A { }
class Aa { }
class B { }
class Bb { }
class C { }
class Cc { }
class D { }
class Dd { }

class PairClassStringArrayTooMany
{
/**
* @var array<string, class-string>
*/
private const CLASSES = [
A::class => Aa::class,
B::class => Bb::class,
C::class => Cc::class,
D::class => Dd::class,
];
}

?>
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ private function resolveArrayTypeWithUnionKeyType(ArrayType $arrayType): ArrayTy
return $arrayType;
}

private function isAllGenericClassStringType(UnionType $unionType): bool
public function isAllGenericClassStringType(UnionType $unionType): bool
{
$types = $unionType->getTypes();

Expand Down

0 comments on commit 1e3e5df

Please sign in to comment.