From 2b04a8511d84315d2691b8ad866d471854d593ff Mon Sep 17 00:00:00 2001 From: TomasVotruba Date: Fri, 29 Jan 2021 20:12:21 +0100 Subject: [PATCH 1/6] decoupling --- ...GenericsPHPStormMethodAnnotationRector.php | 38 +------------------ .../GenericClassReflectionAnalyzer.php | 38 ++++++++++++++++++- .../Fixture/skip_already_method.php.inc | 17 +++++++++ .../ConstructorPropertyTypeInferer.php | 2 - 4 files changed, 56 insertions(+), 39 deletions(-) create mode 100644 rules/generics/tests/Rector/Class_/GenericsPHPStormMethodAnnotationRector/Fixture/skip_already_method.php.inc diff --git a/rules/generics/src/Rector/Class_/GenericsPHPStormMethodAnnotationRector.php b/rules/generics/src/Rector/Class_/GenericsPHPStormMethodAnnotationRector.php index 8ff6cab7f23f..754e80ee89d5 100644 --- a/rules/generics/src/Rector/Class_/GenericsPHPStormMethodAnnotationRector.php +++ b/rules/generics/src/Rector/Class_/GenericsPHPStormMethodAnnotationRector.php @@ -6,14 +6,11 @@ use PhpParser\Node; use PhpParser\Node\Stmt\Class_; -use PHPStan\Analyser\Scope; -use PHPStan\Reflection\ClassReflection; use Rector\Core\Rector\AbstractRector; use Rector\Generics\NodeType\GenericTypeSpecifier; use Rector\Generics\Reflection\ClassGenericMethodResolver; use Rector\Generics\Reflection\GenericClassReflectionAnalyzer; use Rector\Generics\ValueObject\GenericChildParentClassReflections; -use Rector\NodeTypeResolver\Node\AttributeKey; use Symplify\RuleDocGenerator\ValueObject\CodeSample\CodeSample; use Symplify\RuleDocGenerator\ValueObject\RuleDefinition; @@ -119,11 +116,7 @@ public function getNodeTypes(): array */ public function refactor(Node $node): ?Node { - if ($node->extends === null) { - return null; - } - - $genericChildParentClassReflections = $this->resolveGenericChildParentClassReflections($node); + $genericChildParentClassReflections = $this->genericClassReflectionAnalyzer->resolveChildParent($node); if (! $genericChildParentClassReflections instanceof GenericChildParentClassReflections) { return null; } @@ -140,38 +133,11 @@ public function refactor(Node $node): ?Node ); $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); + foreach ($methodTagValueNodes as $methodTagValueNode) { $phpDocInfo->addTagValueNode($methodTagValueNode); } return $node; } - - private function resolveGenericChildParentClassReflections(Class_ $class): ?GenericChildParentClassReflections - { - $scope = $class->getAttribute(AttributeKey::SCOPE); - if (! $scope instanceof Scope) { - return null; - } - - $classReflection = $scope->getClassReflection(); - if (! $classReflection instanceof ClassReflection) { - return null; - } - - if (! $this->genericClassReflectionAnalyzer->isGeneric($classReflection)) { - return null; - } - - $parentClassReflection = $classReflection->getParentClass(); - if (! $parentClassReflection instanceof ClassReflection) { - return null; - } - - if (! $this->genericClassReflectionAnalyzer->isGeneric($parentClassReflection)) { - return null; - } - - return new GenericChildParentClassReflections($classReflection, $parentClassReflection); - } } diff --git a/rules/generics/src/Reflection/GenericClassReflectionAnalyzer.php b/rules/generics/src/Reflection/GenericClassReflectionAnalyzer.php index d549a5329048..09bd18f19478 100644 --- a/rules/generics/src/Reflection/GenericClassReflectionAnalyzer.php +++ b/rules/generics/src/Reflection/GenericClassReflectionAnalyzer.php @@ -4,16 +4,52 @@ namespace Rector\Generics\Reflection; +use PhpParser\Node\Stmt\Class_; +use PHPStan\Analyser\Scope; use PHPStan\PhpDoc\ResolvedPhpDocBlock; use PHPStan\Reflection\ClassReflection; +use Rector\Generics\ValueObject\GenericChildParentClassReflections; +use Rector\NodeTypeResolver\Node\AttributeKey; final class GenericClassReflectionAnalyzer { + public function resolveChildParent(Class_ $class): ?GenericChildParentClassReflections + { + if ($class->extends === null) { + return null; + } + + $scope = $class->getAttribute(AttributeKey::SCOPE); + if (! $scope instanceof Scope) { + return null; + } + + $classReflection = $scope->getClassReflection(); + if (! $classReflection instanceof ClassReflection) { + return null; + } + + if (! $this->isGeneric($classReflection)) { + return null; + } + + $parentClassReflection = $classReflection->getParentClass(); + if (! $parentClassReflection instanceof ClassReflection) { + return null; + } + + if (! $this->isGeneric($parentClassReflection)) { + return null; + } + + return new GenericChildParentClassReflections($classReflection, $parentClassReflection); + } + /** * Solve isGeneric() ignores extends and similar tags, * so it has to be extended with "@extends" and "@implements" */ - public function isGeneric(ClassReflection $classReflection): bool + private function isGeneric(ClassReflection $classReflection): bool { if ($classReflection->isGeneric()) { return true; diff --git a/rules/generics/tests/Rector/Class_/GenericsPHPStormMethodAnnotationRector/Fixture/skip_already_method.php.inc b/rules/generics/tests/Rector/Class_/GenericsPHPStormMethodAnnotationRector/Fixture/skip_already_method.php.inc new file mode 100644 index 000000000000..683a90dc69af --- /dev/null +++ b/rules/generics/tests/Rector/Class_/GenericsPHPStormMethodAnnotationRector/Fixture/skip_already_method.php.inc @@ -0,0 +1,17 @@ + + * @method \Rector\Generics\Tests\Rector\Class_\GenericsPHPStormMethodAnnotationRector\Source\RealObject find($id) + */ +final class SkipAlreadyMethod extends AbstractGenericRepository +{ +} diff --git a/rules/type-declaration/src/TypeInferer/PropertyTypeInferer/ConstructorPropertyTypeInferer.php b/rules/type-declaration/src/TypeInferer/PropertyTypeInferer/ConstructorPropertyTypeInferer.php index c221c7c9d0de..728fc7a6ba9a 100644 --- a/rules/type-declaration/src/TypeInferer/PropertyTypeInferer/ConstructorPropertyTypeInferer.php +++ b/rules/type-declaration/src/TypeInferer/PropertyTypeInferer/ConstructorPropertyTypeInferer.php @@ -16,8 +16,6 @@ use PhpParser\Node\Stmt\Property; use PhpParser\NodeTraverser; use PHPStan\Type\ArrayType; -use PHPStan\Type\Constant\ConstantIntegerType; -use PHPStan\Type\IntegerType; use PHPStan\Type\MixedType; use PHPStan\Type\NullType; use PHPStan\Type\Type; From e580b6ff4aa4d9a6f31d6809f8fe06daea69695a Mon Sep 17 00:00:00 2001 From: TomasVotruba Date: Fri, 29 Jan 2021 20:16:57 +0100 Subject: [PATCH 2/6] [Generics] Skip existing method tags --- .../src/PhpDocInfo/PhpDocInfo.php | 13 ++++++++ ...GenericsPHPStormMethodAnnotationRector.php | 33 +++++++++++++++++-- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/packages/better-php-doc-parser/src/PhpDocInfo/PhpDocInfo.php b/packages/better-php-doc-parser/src/PhpDocInfo/PhpDocInfo.php index 0c097be1a99f..4631a92fde55 100644 --- a/packages/better-php-doc-parser/src/PhpDocInfo/PhpDocInfo.php +++ b/packages/better-php-doc-parser/src/PhpDocInfo/PhpDocInfo.php @@ -464,6 +464,19 @@ public function hasChanged(): bool return $this->hasChanged; } + /** + * @return string[] + */ + public function getMethodTagNames(): array + { + $methodTagNames = []; + foreach ($this->phpDocNode->getMethodTagValues() as $methodTagValueNode) { + $methodTagNames[] = $methodTagValueNode->methodName; + } + + return $methodTagNames; + } + private function getTypeOrMixed(?PhpDocTagValueNode $phpDocTagValueNode): Type { if ($phpDocTagValueNode === null) { diff --git a/rules/generics/src/Rector/Class_/GenericsPHPStormMethodAnnotationRector.php b/rules/generics/src/Rector/Class_/GenericsPHPStormMethodAnnotationRector.php index 754e80ee89d5..4ae6fcb8e986 100644 --- a/rules/generics/src/Rector/Class_/GenericsPHPStormMethodAnnotationRector.php +++ b/rules/generics/src/Rector/Class_/GenericsPHPStormMethodAnnotationRector.php @@ -6,6 +6,8 @@ use PhpParser\Node; use PhpParser\Node\Stmt\Class_; +use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueNode; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; use Rector\Core\Rector\AbstractRector; use Rector\Generics\NodeType\GenericTypeSpecifier; use Rector\Generics\Reflection\ClassGenericMethodResolver; @@ -126,18 +128,45 @@ public function refactor(Node $node): ?Node $genericChildParentClassReflections->getParentClassReflection() ); + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); + + $methodTagValueNodes = $this->filterOutExistingMethodTagValuesNodes($methodTagValueNodes, $phpDocInfo); + $this->genericTypeSpecifier->replaceGenericTypesWithSpecificTypes( $methodTagValueNodes, $node, $genericChildParentClassReflections->getChildClassReflection() ); - $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); - foreach ($methodTagValueNodes as $methodTagValueNode) { $phpDocInfo->addTagValueNode($methodTagValueNode); } return $node; } + + /** + * @param MethodTagValueNode[] $methodTagValueNodes + * @return MethodTagValueNode[] + */ + private function filterOutExistingMethodTagValuesNodes( + array $methodTagValueNodes, + PhpDocInfo $phpDocInfo + ): array { + $methodTagNames = $phpDocInfo->getMethodTagNames(); + if ($methodTagNames === []) { + return $methodTagValueNodes; + } + + $filteredMethodTagValueNodes = []; + foreach ($methodTagValueNodes as $methodTagValueNode) { + if (in_array($methodTagValueNode->methodName, $methodTagNames, true)) { + continue; + } + + $filteredMethodTagValueNodes[] = $methodTagValueNode; + } + + return $filteredMethodTagValueNodes; + } } From 58ee1742b6af3c72e56aee7b96ebcf196386ab68 Mon Sep 17 00:00:00 2001 From: TomasVotruba Date: Fri, 29 Jan 2021 20:24:19 +0100 Subject: [PATCH 3/6] [Generics] Add nullable type support --- .../MethodTagValueNodeFactory.php | 42 +++++++++++++++---- .../Fixture/nullable_get.php.inc | 38 +++++++++++++++++ .../Source/AbstractMaybeGenericRepository.php | 18 ++++++++ 3 files changed, 91 insertions(+), 7 deletions(-) create mode 100644 rules/generics/tests/Rector/Class_/GenericsPHPStormMethodAnnotationRector/Fixture/nullable_get.php.inc create mode 100644 rules/generics/tests/Rector/Class_/GenericsPHPStormMethodAnnotationRector/Source/AbstractMaybeGenericRepository.php diff --git a/rules/generics/src/TagValueNodeFactory/MethodTagValueNodeFactory.php b/rules/generics/src/TagValueNodeFactory/MethodTagValueNodeFactory.php index 4cdaba586b5a..7a04f4e7d1e8 100644 --- a/rules/generics/src/TagValueNodeFactory/MethodTagValueNodeFactory.php +++ b/rules/generics/src/TagValueNodeFactory/MethodTagValueNodeFactory.php @@ -8,8 +8,11 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueParameterNode; use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode; use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode; +use PHPStan\PhpDocParser\Ast\Type\TypeNode; +use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode; use PHPStan\Reflection\MethodReflection; use PHPStan\Reflection\ParameterReflection; +use PHPStan\Type\Generic\TemplateTypeMap; use PHPStan\Type\Type; use Rector\StaticTypeMapper\StaticTypeMapper; @@ -47,14 +50,23 @@ public function createFromMethodReflectionAndReturnTagValueNode( $returnTagTypeNode = $returnTagValueNode->type; - if ($returnTagValueNode->type instanceof IdentifierTypeNode) { - $typeName = $returnTagValueNode->type->name; - $genericType = $templateTypeMap->getType($typeName); - - if ($genericType instanceof Type) { - $returnTagType = $genericType; - $returnTagTypeNode = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode($returnTagType); + if ($returnTagValueNode->type instanceof UnionTypeNode) { + $resolvedTypes = []; + foreach ($returnTagValueNode->type->types as $unionedTypeNode) { + $resolvedTypes[] = $this->resolveIdentifierTypeNode( + $unionedTypeNode, + $templateTypeMap, + $unionedTypeNode + ); } + + $returnTagTypeNode = new UnionTypeNode($resolvedTypes); + } elseif ($returnTagValueNode->type instanceof IdentifierTypeNode) { + $returnTagTypeNode = $this->resolveIdentifierTypeNode( + $returnTagValueNode->type, + $templateTypeMap, + $returnTagTypeNode + ); } return new MethodTagValueNode( @@ -82,4 +94,20 @@ private function resolveStringParameters(array $parameterReflections): array return $stringParameters; } + + private function resolveIdentifierTypeNode( + IdentifierTypeNode $identifierTypeNode, + TemplateTypeMap $templateTypeMap, + TypeNode $fallbackTypeNode + ): TypeNode { + $typeName = $identifierTypeNode->name; + $genericType = $templateTypeMap->getType($typeName); + + if ($genericType instanceof Type) { + $returnTagType = $genericType; + return $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode($returnTagType); + } + + return $fallbackTypeNode; + } } diff --git a/rules/generics/tests/Rector/Class_/GenericsPHPStormMethodAnnotationRector/Fixture/nullable_get.php.inc b/rules/generics/tests/Rector/Class_/GenericsPHPStormMethodAnnotationRector/Fixture/nullable_get.php.inc new file mode 100644 index 000000000000..9d2816e6952f --- /dev/null +++ b/rules/generics/tests/Rector/Class_/GenericsPHPStormMethodAnnotationRector/Fixture/nullable_get.php.inc @@ -0,0 +1,38 @@ + + */ +final class NullableGet extends AbstractMaybeGenericRepository +{ +} + +?> +----- + + * @method \Rector\Generics\Tests\Rector\Class_\GenericsPHPStormMethodAnnotationRector\Source\RealObject|null find($id) + */ +final class NullableGet extends AbstractMaybeGenericRepository +{ +} + +?> diff --git a/rules/generics/tests/Rector/Class_/GenericsPHPStormMethodAnnotationRector/Source/AbstractMaybeGenericRepository.php b/rules/generics/tests/Rector/Class_/GenericsPHPStormMethodAnnotationRector/Source/AbstractMaybeGenericRepository.php new file mode 100644 index 000000000000..693ce62ade21 --- /dev/null +++ b/rules/generics/tests/Rector/Class_/GenericsPHPStormMethodAnnotationRector/Source/AbstractMaybeGenericRepository.php @@ -0,0 +1,18 @@ + Date: Fri, 29 Jan 2021 20:26:57 +0100 Subject: [PATCH 4/6] add array-like --- .../MethodTagValueNodeFactory.php | 4 ++ .../Fixture/array_like.php.inc | 38 +++++++++++++++++++ .../Source/AbstractGenericArrayRepository.php | 18 +++++++++ 3 files changed, 60 insertions(+) create mode 100644 rules/generics/tests/Rector/Class_/GenericsPHPStormMethodAnnotationRector/Fixture/array_like.php.inc create mode 100644 rules/generics/tests/Rector/Class_/GenericsPHPStormMethodAnnotationRector/Source/AbstractGenericArrayRepository.php diff --git a/rules/generics/src/TagValueNodeFactory/MethodTagValueNodeFactory.php b/rules/generics/src/TagValueNodeFactory/MethodTagValueNodeFactory.php index 7a04f4e7d1e8..76dfc2a34bc4 100644 --- a/rules/generics/src/TagValueNodeFactory/MethodTagValueNodeFactory.php +++ b/rules/generics/src/TagValueNodeFactory/MethodTagValueNodeFactory.php @@ -53,6 +53,10 @@ public function createFromMethodReflectionAndReturnTagValueNode( if ($returnTagValueNode->type instanceof UnionTypeNode) { $resolvedTypes = []; foreach ($returnTagValueNode->type->types as $unionedTypeNode) { + if (! $unionedTypeNode instanceof IdentifierTypeNode) { + continue; + } + $resolvedTypes[] = $this->resolveIdentifierTypeNode( $unionedTypeNode, $templateTypeMap, diff --git a/rules/generics/tests/Rector/Class_/GenericsPHPStormMethodAnnotationRector/Fixture/array_like.php.inc b/rules/generics/tests/Rector/Class_/GenericsPHPStormMethodAnnotationRector/Fixture/array_like.php.inc new file mode 100644 index 000000000000..df7c8ac37c5a --- /dev/null +++ b/rules/generics/tests/Rector/Class_/GenericsPHPStormMethodAnnotationRector/Fixture/array_like.php.inc @@ -0,0 +1,38 @@ + + */ +final class ArrayLike extends AbstractGenericArrayRepository +{ +} + +?> +----- + + * @method \Rector\Generics\Tests\Rector\Class_\GenericsPHPStormMethodAnnotationRector\Source\RealObject[] findAll() + */ +final class ArrayLike extends AbstractGenericArrayRepository +{ +} + +?> diff --git a/rules/generics/tests/Rector/Class_/GenericsPHPStormMethodAnnotationRector/Source/AbstractGenericArrayRepository.php b/rules/generics/tests/Rector/Class_/GenericsPHPStormMethodAnnotationRector/Source/AbstractGenericArrayRepository.php new file mode 100644 index 000000000000..f19778966c28 --- /dev/null +++ b/rules/generics/tests/Rector/Class_/GenericsPHPStormMethodAnnotationRector/Source/AbstractGenericArrayRepository.php @@ -0,0 +1,18 @@ + Date: Fri, 29 Jan 2021 20:41:01 +0100 Subject: [PATCH 5/6] bump deps --- composer.json | 32 ++++----- .../MethodTagValueNodeFactory.php | 72 ++++++++++++------- .../Fixture/array_like_nullable.php.inc | 38 ++++++++++ .../AbstractGenericMaybeArrayRepository.php | 18 +++++ 4 files changed, 120 insertions(+), 40 deletions(-) create mode 100644 rules/generics/tests/Rector/Class_/GenericsPHPStormMethodAnnotationRector/Fixture/array_like_nullable.php.inc create mode 100644 rules/generics/tests/Rector/Class_/GenericsPHPStormMethodAnnotationRector/Source/AbstractGenericMaybeArrayRepository.php diff --git a/composer.json b/composer.json index 34055f384ea0..bb2af18b65e6 100644 --- a/composer.json +++ b/composer.json @@ -48,16 +48,16 @@ "symfony/finder": "^4.4.8|^5.1", "symfony/http-kernel": "^4.4.8|^5.1", "symfony/process": "^4.4.8|^5.1", - "symplify/astral": "^9.0.34", - "symplify/autowire-array-parameter": "^9.0.34", - "symplify/console-color-diff": "^9.0.34", - "symplify/package-builder": "^9.0.34", - "symplify/rule-doc-generator": "^9.0.34", - "symplify/set-config-resolver": "^9.0.34", - "symplify/simple-php-doc-parser": "^9.0", - "symplify/skipper": "^9.0.34", - "symplify/smart-file-system": "^9.0.34", - "symplify/symfony-php-config": "^9.0.34", + "symplify/astral": "^9.0.48", + "symplify/autowire-array-parameter": "^9.0.48", + "symplify/console-color-diff": "^9.0.48", + "symplify/package-builder": "^9.0.48", + "symplify/rule-doc-generator": "^9.0.48", + "symplify/set-config-resolver": "^9.0.48", + "symplify/simple-php-doc-parser": "^9.0.48", + "symplify/skipper": "^9.0.48", + "symplify/smart-file-system": "^9.0.48", + "symplify/symfony-php-config": "^9.0.48", "webmozart/assert": "^1.9" }, "require-dev": { @@ -72,12 +72,12 @@ "sebastian/diff": "^4.0.4", "symfony/security-core": "^5.2", "symfony/security-http": "^5.2", - "symplify/changelog-linker": "^9.0.34", - "symplify/coding-standard": "^9.0.34", - "symplify/easy-coding-standard": "^9.0.34", - "symplify/easy-testing": "^9.0.34", - "symplify/phpstan-extensions": "^9.0.34", - "symplify/phpstan-rules": "^9.0.34", + "symplify/changelog-linker": "^9.0.48", + "symplify/coding-standard": "^9.0.48", + "symplify/easy-coding-standard": "^9.0.48", + "symplify/easy-testing": "^9.0.48", + "symplify/phpstan-extensions": "^9.0.48", + "symplify/phpstan-rules": "^9.0.48", "tracy/tracy": "^2.7" }, "replace": { diff --git a/rules/generics/src/TagValueNodeFactory/MethodTagValueNodeFactory.php b/rules/generics/src/TagValueNodeFactory/MethodTagValueNodeFactory.php index 76dfc2a34bc4..56e5b03ffb29 100644 --- a/rules/generics/src/TagValueNodeFactory/MethodTagValueNodeFactory.php +++ b/rules/generics/src/TagValueNodeFactory/MethodTagValueNodeFactory.php @@ -7,6 +7,7 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\MethodTagValueParameterNode; use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode; +use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode; use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode; use PHPStan\PhpDocParser\Ast\Type\TypeNode; use PHPStan\PhpDocParser\Ast\Type\UnionTypeNode; @@ -14,6 +15,7 @@ use PHPStan\Reflection\ParameterReflection; use PHPStan\Type\Generic\TemplateTypeMap; use PHPStan\Type\Type; +use Rector\Core\Exception\ShouldNotHappenException; use Rector\StaticTypeMapper\StaticTypeMapper; final class MethodTagValueNodeFactory @@ -48,30 +50,7 @@ public function createFromMethodReflectionAndReturnTagValueNode( $classReflection = $methodReflection->getDeclaringClass(); $templateTypeMap = $classReflection->getTemplateTypeMap(); - $returnTagTypeNode = $returnTagValueNode->type; - - if ($returnTagValueNode->type instanceof UnionTypeNode) { - $resolvedTypes = []; - foreach ($returnTagValueNode->type->types as $unionedTypeNode) { - if (! $unionedTypeNode instanceof IdentifierTypeNode) { - continue; - } - - $resolvedTypes[] = $this->resolveIdentifierTypeNode( - $unionedTypeNode, - $templateTypeMap, - $unionedTypeNode - ); - } - - $returnTagTypeNode = new UnionTypeNode($resolvedTypes); - } elseif ($returnTagValueNode->type instanceof IdentifierTypeNode) { - $returnTagTypeNode = $this->resolveIdentifierTypeNode( - $returnTagValueNode->type, - $templateTypeMap, - $returnTagTypeNode - ); - } + $returnTagTypeNode = $this->resolveReturnTagTypeNode($returnTagValueNode, $templateTypeMap); return new MethodTagValueNode( false, @@ -99,6 +78,23 @@ private function resolveStringParameters(array $parameterReflections): array return $stringParameters; } + private function resolveReturnTagTypeNode(ReturnTagValueNode $returnTagValueNode, TemplateTypeMap $templateTypeMap): TypeNode + { + $returnTagTypeNode = $returnTagValueNode->type; + + if ($returnTagValueNode->type instanceof UnionTypeNode) { + return $this->resolveUnionTypeNode($returnTagValueNode->type, $templateTypeMap); + } elseif ($returnTagValueNode->type instanceof IdentifierTypeNode) { + return $this->resolveIdentifierTypeNode( + $returnTagValueNode->type, + $templateTypeMap, + $returnTagTypeNode + ); + } + + return $returnTagTypeNode; + } + private function resolveIdentifierTypeNode( IdentifierTypeNode $identifierTypeNode, TemplateTypeMap $templateTypeMap, @@ -114,4 +110,32 @@ private function resolveIdentifierTypeNode( return $fallbackTypeNode; } + + private function resolveUnionTypeNode(UnionTypeNode $unionTypeNode, TemplateTypeMap $templateTypeMap): UnionTypeNode + { + $resolvedTypes = []; + foreach ($unionTypeNode->types as $unionedTypeNode) { + if ($unionedTypeNode instanceof ArrayTypeNode) { + if (! $unionedTypeNode->type instanceof IdentifierTypeNode) { + throw new ShouldNotHappenException(); + } + + $resolvedType = $this->resolveIdentifierTypeNode( + $unionedTypeNode->type, + $templateTypeMap, + $unionedTypeNode + ); + + $resolvedTypes[] = new ArrayTypeNode($resolvedType); + } elseif ($unionedTypeNode instanceof IdentifierTypeNode) { + $resolvedTypes[] = $this->resolveIdentifierTypeNode( + $unionedTypeNode, + $templateTypeMap, + $unionedTypeNode + ); + } + } + + return new UnionTypeNode($resolvedTypes); + } } diff --git a/rules/generics/tests/Rector/Class_/GenericsPHPStormMethodAnnotationRector/Fixture/array_like_nullable.php.inc b/rules/generics/tests/Rector/Class_/GenericsPHPStormMethodAnnotationRector/Fixture/array_like_nullable.php.inc new file mode 100644 index 000000000000..86641e0f8f55 --- /dev/null +++ b/rules/generics/tests/Rector/Class_/GenericsPHPStormMethodAnnotationRector/Fixture/array_like_nullable.php.inc @@ -0,0 +1,38 @@ + + */ +final class ArrayLikeNullable extends AbstractGenericMaybeArrayRepository +{ +} + +?> +----- + + * @method \Rector\Generics\Tests\Rector\Class_\GenericsPHPStormMethodAnnotationRector\Source\RealObject[]|null findAllMaybe(string $some, int $type, $unknown) + */ +final class ArrayLikeNullable extends AbstractGenericMaybeArrayRepository +{ +} + +?> diff --git a/rules/generics/tests/Rector/Class_/GenericsPHPStormMethodAnnotationRector/Source/AbstractGenericMaybeArrayRepository.php b/rules/generics/tests/Rector/Class_/GenericsPHPStormMethodAnnotationRector/Source/AbstractGenericMaybeArrayRepository.php new file mode 100644 index 000000000000..401c5e60cf0f --- /dev/null +++ b/rules/generics/tests/Rector/Class_/GenericsPHPStormMethodAnnotationRector/Source/AbstractGenericMaybeArrayRepository.php @@ -0,0 +1,18 @@ + Date: Fri, 29 Jan 2021 20:16:48 +0000 Subject: [PATCH 6/6] [ci-review] Rector Rectify --- .../MethodTagValueNodeFactory.php | 43 ++++++++++--------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/rules/generics/src/TagValueNodeFactory/MethodTagValueNodeFactory.php b/rules/generics/src/TagValueNodeFactory/MethodTagValueNodeFactory.php index 56e5b03ffb29..27fc6985d974 100644 --- a/rules/generics/src/TagValueNodeFactory/MethodTagValueNodeFactory.php +++ b/rules/generics/src/TagValueNodeFactory/MethodTagValueNodeFactory.php @@ -78,13 +78,16 @@ private function resolveStringParameters(array $parameterReflections): array return $stringParameters; } - private function resolveReturnTagTypeNode(ReturnTagValueNode $returnTagValueNode, TemplateTypeMap $templateTypeMap): TypeNode - { + private function resolveReturnTagTypeNode( + ReturnTagValueNode $returnTagValueNode, + TemplateTypeMap $templateTypeMap + ): TypeNode { $returnTagTypeNode = $returnTagValueNode->type; - if ($returnTagValueNode->type instanceof UnionTypeNode) { return $this->resolveUnionTypeNode($returnTagValueNode->type, $templateTypeMap); - } elseif ($returnTagValueNode->type instanceof IdentifierTypeNode) { + } + + if ($returnTagValueNode->type instanceof IdentifierTypeNode) { return $this->resolveIdentifierTypeNode( $returnTagValueNode->type, $templateTypeMap, @@ -95,22 +98,6 @@ private function resolveReturnTagTypeNode(ReturnTagValueNode $returnTagValueNode return $returnTagTypeNode; } - private function resolveIdentifierTypeNode( - IdentifierTypeNode $identifierTypeNode, - TemplateTypeMap $templateTypeMap, - TypeNode $fallbackTypeNode - ): TypeNode { - $typeName = $identifierTypeNode->name; - $genericType = $templateTypeMap->getType($typeName); - - if ($genericType instanceof Type) { - $returnTagType = $genericType; - return $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode($returnTagType); - } - - return $fallbackTypeNode; - } - private function resolveUnionTypeNode(UnionTypeNode $unionTypeNode, TemplateTypeMap $templateTypeMap): UnionTypeNode { $resolvedTypes = []; @@ -138,4 +125,20 @@ private function resolveUnionTypeNode(UnionTypeNode $unionTypeNode, TemplateType return new UnionTypeNode($resolvedTypes); } + + private function resolveIdentifierTypeNode( + IdentifierTypeNode $identifierTypeNode, + TemplateTypeMap $templateTypeMap, + TypeNode $fallbackTypeNode + ): TypeNode { + $typeName = $identifierTypeNode->name; + $genericType = $templateTypeMap->getType($typeName); + + if ($genericType instanceof Type) { + $returnTagType = $genericType; + return $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode($returnTagType); + } + + return $fallbackTypeNode; + } }