From e56804dd659e3eae21cedd9911e5fbc82689bea9 Mon Sep 17 00:00:00 2001 From: TomasVotruba Date: Fri, 31 Jan 2020 09:01:07 +0100 Subject: [PATCH 1/4] remove deprecated removeBy* in DocBlockManipulator --- .../src/PhpDocInfo/PhpDocInfo.php | 28 ++++++++ .../ReturnArrayClassMethodToYieldRector.php | 9 ++- .../NodeAnalyzer/DocBlockManipulator.php | 58 --------------- .../DocBlockManipulator/RemoveTest.php | 70 ------------------- .../src/Rector/ExceptionAnnotationRector.php | 8 ++- .../TemplateAnnotationRector.php | 8 ++- ...ertyInjectToConstructorInjectionRector.php | 16 ++++- .../MethodBody/ReturnThisRemoveRector.php | 8 ++- .../Property/InjectAnnotationClassRector.php | 6 +- 9 files changed, 73 insertions(+), 138 deletions(-) delete mode 100644 packages/NodeTypeResolver/tests/PhpDoc/NodeAnalyzer/DocBlockManipulator/RemoveTest.php diff --git a/packages/BetterPhpDocParser/src/PhpDocInfo/PhpDocInfo.php b/packages/BetterPhpDocParser/src/PhpDocInfo/PhpDocInfo.php index da521eb8f3fc..27bd5f11b687 100644 --- a/packages/BetterPhpDocParser/src/PhpDocInfo/PhpDocInfo.php +++ b/packages/BetterPhpDocParser/src/PhpDocInfo/PhpDocInfo.php @@ -222,6 +222,11 @@ public function hasByType(string $type): bool return (bool) $this->getByType($type); } + public function hasByName(string $name): bool + { + return (bool) $this->getTagsByName($name); + } + public function getByType(string $type): ?PhpDocTagValueNode { $this->ensureTypeIsTagValueNode($type, __METHOD__); @@ -256,6 +261,21 @@ public function removeByType(string $type): void } } + public function removeByName(string $nameToRemove): void + { + foreach ($this->phpDocNode->children as $key => $phpDocChildNode) { + if (! $phpDocChildNode instanceof PhpDocTagNode) { + continue; + } + + if (! $this->areAnnotationNamesEqual($nameToRemove, $phpDocChildNode->name)) { + continue; + } + + unset($this->phpDocNode->children[$key]); + } + } + private function getParamTagValueByName(string $name): ?AttributeAwareParamTagValueNode { $phpDocNode = $this->getPhpDocNode(); @@ -283,4 +303,12 @@ private function ensureTypeIsTagValueNode(string $type, string $location): void PhpDocTagValueNode::class )); } + + private function areAnnotationNamesEqual(string $firstAnnotationName, string $secondAnnotationName): bool + { + $firstAnnotationName = trim($firstAnnotationName, '@'); + $secondAnnotationName = trim($secondAnnotationName, '@'); + + return $firstAnnotationName === $secondAnnotationName; + } } diff --git a/packages/CodingStyle/src/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector.php b/packages/CodingStyle/src/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector.php index 6ab1ba7b7886..e7f76bbc8b8e 100644 --- a/packages/CodingStyle/src/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector.php +++ b/packages/CodingStyle/src/Rector/ClassMethod/ReturnArrayClassMethodToYieldRector.php @@ -12,6 +12,8 @@ use PhpParser\Node\Name\FullyQualified; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Return_; +use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; use Rector\Exception\ShouldNotHappenException; use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\PhpParser\NodeTransformer; @@ -156,8 +158,11 @@ private function transformArrayToYieldsOnMethodNode(ClassMethod $classMethod, Ar $this->removeNode($parentNode); - // remove doc block - $this->docBlockManipulator->removeTagFromNode($classMethod, 'return'); + /** @var PhpDocInfo|null $phpDocInfo */ + $phpDocInfo = $classMethod->getAttribute(AttributeKey::PHP_DOC_INFO); + if ($phpDocInfo instanceof PhpDocInfo) { + $phpDocInfo->removeByType(ReturnTagValueNode::class); + } // change return typehint $classMethod->returnType = new FullyQualified(Iterator::class); diff --git a/packages/NodeTypeResolver/src/PhpDoc/NodeAnalyzer/DocBlockManipulator.php b/packages/NodeTypeResolver/src/PhpDoc/NodeAnalyzer/DocBlockManipulator.php index 14f8aa11f754..290627d1e600 100644 --- a/packages/NodeTypeResolver/src/PhpDoc/NodeAnalyzer/DocBlockManipulator.php +++ b/packages/NodeTypeResolver/src/PhpDoc/NodeAnalyzer/DocBlockManipulator.php @@ -15,7 +15,6 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocChildNode; use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode; use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode; -use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode; use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode; @@ -161,21 +160,6 @@ public function addTagValueNodeWithShortName(Node $node, AbstractTagValueNode $t $this->addTag($node, $spacelessPhpDocTagNode); } - /** - * @deprecated - * Use @see PhpDocInfo::removeByType(x) directly - */ - public function removeTagFromNode(Node $node, string $name): void - { - /** @var PhpDocInfo|null $phpDocInfo */ - $phpDocInfo = $node->getAttribute(AttributeKey::PHP_DOC_INFO); - if ($phpDocInfo === null) { - return; - } - - $this->removeTagByName($phpDocInfo, $name); - } - public function changeType(Node $node, Type $oldType, Type $newType): void { if (! $this->hasNodeTypeTags($node)) { @@ -335,48 +319,6 @@ public function getVarType(Node $node): Type return $phpDocInfo->getVarType(); } - public function removeTagByName(PhpDocInfo $phpDocInfo, string $tagName): void - { - $phpDocNode = $phpDocInfo->getPhpDocNode(); - - // A. remove class-based tag - if (class_exists($tagName)) { - $phpDocInfo->removeByType($tagName); - } - - // B. remove string-based tags - $tagName = AnnotationNaming::normalizeName($tagName); - $phpDocTagNodes = $phpDocInfo->getTagsByName($tagName); - foreach ($phpDocTagNodes as $phpDocTagNode) { - $this->removeTagFromPhpDocNode($phpDocNode, $phpDocTagNode); - } - } - - /** - * @param PhpDocTagNode|PhpDocTagValueNode $phpDocTagOrPhpDocTagValueNode - */ - public function removeTagFromPhpDocNode(PhpDocNode $phpDocNode, $phpDocTagOrPhpDocTagValueNode): void - { - // remove specific tag - foreach ($phpDocNode->children as $key => $phpDocChildNode) { - if ($phpDocChildNode === $phpDocTagOrPhpDocTagValueNode) { - unset($phpDocNode->children[$key]); - return; - } - } - - // or by type - foreach ($phpDocNode->children as $key => $phpDocChildNode) { - if (! $phpDocChildNode instanceof PhpDocTagNode) { - continue; - } - - if ($phpDocChildNode->value === $phpDocTagOrPhpDocTagValueNode) { - unset($phpDocNode->children[$key]); - } - } - } - public function replaceTagByAnother(PhpDocNode $phpDocNode, string $oldTag, string $newTag): void { $oldTag = AnnotationNaming::normalizeName($oldTag); diff --git a/packages/NodeTypeResolver/tests/PhpDoc/NodeAnalyzer/DocBlockManipulator/RemoveTest.php b/packages/NodeTypeResolver/tests/PhpDoc/NodeAnalyzer/DocBlockManipulator/RemoveTest.php deleted file mode 100644 index e1d6b64c2c25..000000000000 --- a/packages/NodeTypeResolver/tests/PhpDoc/NodeAnalyzer/DocBlockManipulator/RemoveTest.php +++ /dev/null @@ -1,70 +0,0 @@ -bootKernel(RectorKernel::class); - - $this->phpDocInfoFactory = self::$container->get(PhpDocInfoFactory::class); - $this->phpDocInfoPrinter = self::$container->get(PhpDocInfoPrinter::class); - $this->docBlockManipulator = self::$container->get(DocBlockManipulator::class); - } - - /** - * @dataProvider provideDataForRemoveTagByName() - */ - public function testRemoveTagByName(string $phpDocBeforeFilePath, string $phpDocAfter, string $tagName): void - { - $phpDocInfo = $this->createPhpDocInfoFromFile($phpDocBeforeFilePath); - $this->docBlockManipulator->removeTagByName($phpDocInfo, $tagName); - - $this->assertSame($phpDocAfter, $this->phpDocInfoPrinter->printFormatPreserving($phpDocInfo)); - } - - public function provideDataForRemoveTagByName(): Iterator - { - yield [__DIR__ . '/RemoveSource/before.txt', '', 'var']; - yield [__DIR__ . '/RemoveSource/before.txt', '', '@var']; - } - - private function createPhpDocInfoFromFile(string $phpDocBeforeFilePath): PhpDocInfo - { - $phpDocBefore = FileSystem::read($phpDocBeforeFilePath); - - $node = new Nop(); - $node->setDocComment(new Doc($phpDocBefore)); - - return $this->phpDocInfoFactory->createFromNode($node); - } -} diff --git a/packages/PHPUnit/src/Rector/ExceptionAnnotationRector.php b/packages/PHPUnit/src/Rector/ExceptionAnnotationRector.php index 8b6a9097f013..0252b7f9cc41 100644 --- a/packages/PHPUnit/src/Rector/ExceptionAnnotationRector.php +++ b/packages/PHPUnit/src/Rector/ExceptionAnnotationRector.php @@ -10,6 +10,8 @@ use PhpParser\Node\Stmt\Expression; use PHPStan\PhpDocParser\Ast\PhpDoc\GenericTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; +use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\Rector\AbstractPHPUnitRector; use Rector\RectorDefinition\CodeSample; use Rector\RectorDefinition\RectorDefinition; @@ -95,7 +97,11 @@ public function refactor(Node $node): ?Node $node->stmts = array_merge($methodCallExpressions, (array) $node->stmts); - $this->docBlockManipulator->removeTagFromNode($node, $annotation); + /** @var PhpDocInfo|null $phpDocInfo */ + $phpDocInfo = $node->getAttribute(AttributeKey::PHP_DOC_INFO); + if ($phpDocInfo !== null) { + $phpDocInfo->removeByName($annotation); + } } return $node; diff --git a/packages/Sensio/src/Rector/FrameworkExtraBundle/TemplateAnnotationRector.php b/packages/Sensio/src/Rector/FrameworkExtraBundle/TemplateAnnotationRector.php index a85fec33f153..d6898268b490 100644 --- a/packages/Sensio/src/Rector/FrameworkExtraBundle/TemplateAnnotationRector.php +++ b/packages/Sensio/src/Rector/FrameworkExtraBundle/TemplateAnnotationRector.php @@ -12,6 +12,7 @@ use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Return_; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; use Rector\BetterPhpDocParser\PhpDocNode\Sensio\SensioTemplateTagValueNode; use Rector\NodeContainer\ParsedNodesByType; use Rector\NodeTypeResolver\Node\AttributeKey; @@ -139,8 +140,11 @@ private function replaceTemplateAnnotation(ClassMethod $classMethod): ?Node $this->updateReturnType($classMethod); $this->refactorClassMethod($classMethod, $sensioTemplateTagValueNode); - // remove annotation - $this->docBlockManipulator->removeTagFromNode($classMethod, SensioTemplateTagValueNode::class); + /** @var PhpDocInfo|null $phpDocInfo */ + $phpDocInfo = $classMethod->getAttribute(AttributeKey::PHP_DOC_INFO); + if ($phpDocInfo instanceof PhpDocInfo) { + $phpDocInfo->removeByType(SensioTemplateTagValueNode::class); + } return $classMethod; } diff --git a/src/Rector/Architecture/DependencyInjection/AnnotatedPropertyInjectToConstructorInjectionRector.php b/src/Rector/Architecture/DependencyInjection/AnnotatedPropertyInjectToConstructorInjectionRector.php index b46de1ab8ae4..dd52187b86ff 100644 --- a/src/Rector/Architecture/DependencyInjection/AnnotatedPropertyInjectToConstructorInjectionRector.php +++ b/src/Rector/Architecture/DependencyInjection/AnnotatedPropertyInjectToConstructorInjectionRector.php @@ -8,6 +8,7 @@ use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\Property; use PHPStan\Type\UnionType; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\Rector\AbstractRector; use Rector\RectorDefinition\CodeSample; @@ -76,7 +77,9 @@ public function refactor(Node $node): ?Node return null; } - $this->docBlockManipulator->removeTagFromNode($node, self::INJECT_ANNOTATION); + /** @var PhpDocInfo $phpDocInfo */ + $phpDocInfo = $node->getAttribute(AttributeKey::PHP_DOC_INFO); + $phpDocInfo->removeByName(self::INJECT_ANNOTATION); // set to private $this->makePrivate($node); @@ -89,11 +92,18 @@ public function refactor(Node $node): ?Node private function shouldSkipProperty(Node $node): bool { - if (! $this->docBlockManipulator->hasTag($node, self::INJECT_ANNOTATION)) { + /** @var PhpDocInfo|null $phpDocInfo */ + $phpDocInfo = $node->getAttribute(AttributeKey::PHP_DOC_INFO); + if ($phpDocInfo === null) { return true; } + + if (! $phpDocInfo->hasByName(self::INJECT_ANNOTATION)) { + return true; + } + // it needs @var tag as well, to get the type - return ! $this->docBlockManipulator->hasTag($node, 'var'); + return ! $phpDocInfo->getVarTagValue(); } private function addPropertyToCollector(Property $property): void diff --git a/src/Rector/MethodBody/ReturnThisRemoveRector.php b/src/Rector/MethodBody/ReturnThisRemoveRector.php index 7839c477dda5..22a830f4b8e6 100644 --- a/src/Rector/MethodBody/ReturnThisRemoveRector.php +++ b/src/Rector/MethodBody/ReturnThisRemoveRector.php @@ -7,6 +7,8 @@ use PhpParser\Node; use PhpParser\Node\Expr\Variable; use PhpParser\Node\Stmt\Return_; +use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; use Rector\Exception\ShouldNotHappenException; use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\Rector\AbstractRector; @@ -100,7 +102,11 @@ public function refactor(Node $node): ?Node throw new ShouldNotHappenException(); } - $this->docBlockManipulator->removeTagFromNode($methodNode, 'return'); + /** @var PhpDocInfo|null $phpDocInfo */ + $phpDocInfo = $methodNode->getAttribute(AttributeKey::PHP_DOC_INFO); + if ($phpDocInfo instanceof PhpDocInfo) { + $phpDocInfo->removeByType(ReturnTagValueNode::class); + } return null; } diff --git a/src/Rector/Property/InjectAnnotationClassRector.php b/src/Rector/Property/InjectAnnotationClassRector.php index 857a77e7a470..be88b3167e3d 100644 --- a/src/Rector/Property/InjectAnnotationClassRector.php +++ b/src/Rector/Property/InjectAnnotationClassRector.php @@ -191,7 +191,11 @@ private function refactorPropertyWithAnnotation(Property $property, Type $type, } $this->docBlockManipulator->changeVarTag($property, $type); - $this->docBlockManipulator->removeTagFromNode($property, $tagClass); + + $phpDocInfo = $property->getAttribute(AttributeKey::PHP_DOC_INFO); + if ($phpDocInfo instanceof PhpDocInfo) { + $phpDocInfo->removeByType($tagClass); + } $classNode = $property->getAttribute(AttributeKey::CLASS_NODE); if (! $classNode instanceof Class_) { From 452f2e364313e1aea4b8aad2ab47bd3ad38e0631 Mon Sep 17 00:00:00 2001 From: TomasVotruba Date: Fri, 31 Jan 2020 09:18:45 +0100 Subject: [PATCH 2/4] decouple TypeComparator --- .../src/PHPStan/TypeComparator.php | 107 +++++++++++ .../PerNodeTypeResolver/ParamTypeResolver.php | 23 ++- .../VariableTypeResolver.php | 22 +-- .../NodeAnalyzer/DocBlockManipulator.php | 169 +++--------------- .../PropertyTypeDeclarationRector.php | 11 +- .../Property/InjectAnnotationClassRector.php | 5 +- 6 files changed, 167 insertions(+), 170 deletions(-) create mode 100644 packages/NodeTypeResolver/src/PHPStan/TypeComparator.php diff --git a/packages/NodeTypeResolver/src/PHPStan/TypeComparator.php b/packages/NodeTypeResolver/src/PHPStan/TypeComparator.php new file mode 100644 index 000000000000..e38a3b43db13 --- /dev/null +++ b/packages/NodeTypeResolver/src/PHPStan/TypeComparator.php @@ -0,0 +1,107 @@ +typeHasher = $typeHasher; + } + + public function areTypesEquals(Type $firstType, Type $secondType): bool + { + if ($this->areBothSameScalarType($firstType, $secondType)) { + return true; + } + + // aliases and types + if ($this->areAliasedObjectMatchingFqnObject($firstType, $secondType)) { + return true; + } + + if ($this->typeHasher->areTypesEqual($firstType, $secondType)) { + return true; + } + + return $this->areArrayTypeWithSingleObjectChildToParent($firstType, $secondType); + } + + private function areBothSameScalarType(Type $firstType, Type $secondType): bool + { + if ($firstType instanceof StringType && $secondType instanceof StringType) { + return true; + } + + if ($firstType instanceof IntegerType && $secondType instanceof IntegerType) { + return true; + } + + if ($firstType instanceof FloatType && $secondType instanceof FloatType) { + return true; + } + return $firstType instanceof BooleanType && $secondType instanceof BooleanType; + } + + private function areAliasedObjectMatchingFqnObject(Type $firstType, Type $secondType): bool + { + if ($firstType instanceof AliasedObjectType && $secondType instanceof ObjectType && $firstType->getFullyQualifiedClass() === $secondType->getClassName()) { + return true; + } + return $secondType instanceof AliasedObjectType && $firstType instanceof ObjectType && $secondType->getFullyQualifiedClass() === $firstType->getClassName(); + } + + /** + * E.g. class A extends B, class B → A[] is subtype of B[] → keep A[] + */ + private function areArrayTypeWithSingleObjectChildToParent(Type $firstType, Type $secondType): bool + { + if (! $firstType instanceof ArrayType || ! $secondType instanceof ArrayType) { + return false; + } + + $firstArrayItemType = $firstType->getItemType(); + $secondArrayItemType = $secondType->getItemType(); + + if ($firstArrayItemType instanceof ObjectType && $secondArrayItemType instanceof ObjectType) { + $firstFqnClassName = $this->getFqnClassName($firstArrayItemType); + $secondFqnClassName = $this->getFqnClassName($secondArrayItemType); + + if (is_a($firstFqnClassName, $secondFqnClassName, true)) { + return true; + } + + if (is_a($secondFqnClassName, $firstFqnClassName, true)) { + return true; + } + } + + return false; + } + + private function getFqnClassName(ObjectType $objectType): string + { + if ($objectType instanceof ShortenedObjectType) { + return $objectType->getFullyQualifiedName(); + } + + return $objectType->getClassName(); + } +} diff --git a/packages/NodeTypeResolver/src/PerNodeTypeResolver/ParamTypeResolver.php b/packages/NodeTypeResolver/src/PerNodeTypeResolver/ParamTypeResolver.php index ba540ee47117..707e38377a3a 100644 --- a/packages/NodeTypeResolver/src/PerNodeTypeResolver/ParamTypeResolver.php +++ b/packages/NodeTypeResolver/src/PerNodeTypeResolver/ParamTypeResolver.php @@ -13,10 +13,10 @@ use PHPStan\Type\MixedType; use PHPStan\Type\ObjectType; use PHPStan\Type\Type; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; use Rector\NodeTypeResolver\Contract\PerNodeTypeResolver\PerNodeTypeResolverInterface; use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\NodeTypeResolver\NodeTypeResolver; -use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockManipulator; use Rector\PhpParser\Node\Resolver\NameResolver; use Rector\PhpParser\NodeTraverser\CallableNodeTraverser; @@ -30,11 +30,6 @@ final class ParamTypeResolver implements PerNodeTypeResolverInterface */ private $nameResolver; - /** - * @var DocBlockManipulator - */ - private $docBlockManipulator; - /** * @var CallableNodeTraverser */ @@ -45,13 +40,9 @@ final class ParamTypeResolver implements PerNodeTypeResolverInterface */ private $nodeTypeResolver; - public function __construct( - NameResolver $nameResolver, - DocBlockManipulator $docBlockManipulator, - CallableNodeTraverser $callableNodeTraverser - ) { + public function __construct(NameResolver $nameResolver, CallableNodeTraverser $callableNodeTraverser) + { $this->nameResolver = $nameResolver; - $this->docBlockManipulator = $docBlockManipulator; $this->callableNodeTraverser = $callableNodeTraverser; } @@ -129,7 +120,13 @@ private function resolveFromFunctionDocBlock(Param $param): Type /** @var string $paramName */ $paramName = $this->nameResolver->getName($param); - return $this->docBlockManipulator->getParamTypeByName($parentNode, '$' . $paramName); + /** @var PhpDocInfo|null $phpDocInfo */ + $phpDocInfo = $parentNode->getAttribute(AttributeKey::PHP_DOC_INFO); + if ($phpDocInfo === null) { + return new MixedType(); + } + + return $phpDocInfo->getParamType($paramName); } private function resolveFromType(Node $node) diff --git a/packages/NodeTypeResolver/src/PerNodeTypeResolver/VariableTypeResolver.php b/packages/NodeTypeResolver/src/PerNodeTypeResolver/VariableTypeResolver.php index b299894aa64e..e73dfc122e8b 100644 --- a/packages/NodeTypeResolver/src/PerNodeTypeResolver/VariableTypeResolver.php +++ b/packages/NodeTypeResolver/src/PerNodeTypeResolver/VariableTypeResolver.php @@ -11,10 +11,10 @@ use PHPStan\Analyser\Scope; use PHPStan\Type\MixedType; use PHPStan\Type\Type; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; use Rector\NodeTypeResolver\Contract\PerNodeTypeResolver\PerNodeTypeResolverInterface; use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\NodeTypeResolver\NodeTypeResolver; -use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockManipulator; use Rector\NodeTypeResolver\PHPStan\Collector\TraitNodeScopeCollector; use Rector\PhpParser\Node\Resolver\NameResolver; @@ -23,11 +23,6 @@ */ final class VariableTypeResolver implements PerNodeTypeResolverInterface { - /** - * @var DocBlockManipulator - */ - private $docBlockManipulator; - /** * @var NameResolver */ @@ -43,12 +38,8 @@ final class VariableTypeResolver implements PerNodeTypeResolverInterface */ private $traitNodeScopeCollector; - public function __construct( - DocBlockManipulator $docBlockManipulator, - NameResolver $nameResolver, - TraitNodeScopeCollector $traitNodeScopeCollector - ) { - $this->docBlockManipulator = $docBlockManipulator; + public function __construct(NameResolver $nameResolver, TraitNodeScopeCollector $traitNodeScopeCollector) + { $this->nameResolver = $nameResolver; $this->traitNodeScopeCollector = $traitNodeScopeCollector; } @@ -82,7 +73,12 @@ public function resolve(Node $variableNode): Type } // get from annotation - return $this->docBlockManipulator->getVarType($variableNode); + $phpDocInfo = $variableNode->getAttribute(AttributeKey::PHP_DOC_INFO); + if ($phpDocInfo instanceof PhpDocInfo) { + $phpDocInfo->getVarType(); + } + + return new MixedType(); } /** diff --git a/packages/NodeTypeResolver/src/PhpDoc/NodeAnalyzer/DocBlockManipulator.php b/packages/NodeTypeResolver/src/PhpDoc/NodeAnalyzer/DocBlockManipulator.php index 290627d1e600..d883ef917e1e 100644 --- a/packages/NodeTypeResolver/src/PhpDoc/NodeAnalyzer/DocBlockManipulator.php +++ b/packages/NodeTypeResolver/src/PhpDoc/NodeAnalyzer/DocBlockManipulator.php @@ -18,15 +18,10 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode; use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode; -use PHPStan\Type\ArrayType; -use PHPStan\Type\BooleanType; use PHPStan\Type\Constant\ConstantArrayType; -use PHPStan\Type\FloatType; -use PHPStan\Type\IntegerType; use PHPStan\Type\MixedType; use PHPStan\Type\NeverType; use PHPStan\Type\ObjectType; -use PHPStan\Type\StringType; use PHPStan\Type\Type; use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\AttributeAwarePhpDocTagNode; use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\AttributeAwareVarTagValueNode; @@ -40,13 +35,10 @@ use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; use Rector\BetterPhpDocParser\PhpDocNode\AbstractTagValueNode; use Rector\BetterPhpDocParser\Printer\PhpDocInfoPrinter; -use Rector\Exception\ShouldNotHappenException; use Rector\NodeTypeResolver\Exception\MissingTagException; use Rector\NodeTypeResolver\Node\AttributeKey; -use Rector\NodeTypeResolver\PHPStan\TypeHasher; +use Rector\NodeTypeResolver\PHPStan\TypeComparator; use Rector\NodeTypeResolver\StaticTypeMapper; -use Rector\PHPStan\Type\AliasedObjectType; -use Rector\PHPStan\Type\ShortenedObjectType; /** * @see \Rector\NodeTypeResolver\Tests\PhpDoc\NodeAnalyzer\DocBlockManipulatorTest @@ -89,14 +81,14 @@ final class DocBlockManipulator private $docBlockNameImporter; /** - * @var TypeHasher + * @var PhpDocInfoFactory */ - private $typeHasher; + private $phpDocInfoFactory; /** - * @var PhpDocInfoFactory + * @var TypeComparator */ - private $phpDocInfoFactory; + private $typeComparator; public function __construct( PhpDocInfoPrinter $phpDocInfoPrinter, @@ -105,8 +97,8 @@ public function __construct( StaticTypeMapper $staticTypeMapper, DocBlockClassRenamer $docBlockClassRenamer, DocBlockNameImporter $docBlockNameImporter, - TypeHasher $typeHasher, - PhpDocInfoFactory $phpDocInfoFactory + PhpDocInfoFactory $phpDocInfoFactory, + TypeComparator $typeComparator ) { $this->phpDocInfoPrinter = $phpDocInfoPrinter; $this->attributeAwareNodeFactory = $attributeAwareNodeFactory; @@ -114,8 +106,8 @@ public function __construct( $this->staticTypeMapper = $staticTypeMapper; $this->docBlockClassRenamer = $docBlockClassRenamer; $this->docBlockNameImporter = $docBlockNameImporter; - $this->typeHasher = $typeHasher; $this->phpDocInfoFactory = $phpDocInfoFactory; + $this->typeComparator = $typeComparator; } public function hasTag(Node $node, string $name): bool @@ -238,10 +230,16 @@ public function getTagsByName(Node $node, string $name): array public function changeVarTag(Node $node, Type $newType): void { - $currentVarType = $this->getVarType($node); + /** @var PhpDocInfo|null $phpDocInfo */ + $phpDocInfo = $node->getAttribute(AttributeKey::PHP_DOC_INFO); + if ($phpDocInfo instanceof PhpDocInfo) { + $currentVarType = $phpDocInfo->getVarType(); + } else { + $currentVarType = new MixedType(); + } // make sure the tags are not identical, e.g imported class vs FQN class - if ($this->areTypesEquals($currentVarType, $newType)) { + if ($this->typeComparator->areTypesEquals($currentVarType, $newType)) { return; } @@ -272,26 +270,27 @@ public function addReturnTag(Node $node, Type $newType): void $currentReturnType = $this->getReturnType($node); // make sure the tags are not identical, e.g imported class vs FQN class - if ($this->areTypesEquals($currentReturnType, $newType)) { + if ($this->typeComparator->areTypesEquals($currentReturnType, $newType)) { return; } /** @var PhpDocInfo|null $phpDocInfo */ $phpDocInfo = $node->getAttribute(AttributeKey::PHP_DOC_INFO); - if ($phpDocInfo !== null) { - $returnTagValueNode = $phpDocInfo->getByType(ReturnTagValueNode::class); + if ($phpDocInfo === null) { + $this->addTypeSpecificTag($node, 'return', $newType); + return; + } - // overide existing type - if ($returnTagValueNode !== null) { - $newPHPStanPhpDocType = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode($newType); - $returnTagValueNode->type = $newPHPStanPhpDocType; + $returnTagValueNode = $phpDocInfo->getByType(ReturnTagValueNode::class); - return; - } + // overide existing type + if ($returnTagValueNode === null) { + return; } - $this->addTypeSpecificTag($node, 'return', $newType); + $newPHPStanPhpDocType = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode($newType); + $returnTagValueNode->type = $newPHPStanPhpDocType; } /** @@ -308,17 +307,6 @@ public function getTagByName(Node $node, string $name): PhpDocTagNode return array_shift($foundTags); } - public function getVarType(Node $node): Type - { - /** @var PhpDocInfo|null $phpDocInfo */ - $phpDocInfo = $node->getAttribute(AttributeKey::PHP_DOC_INFO); - if ($phpDocInfo === null) { - return new MixedType(); - } - - return $phpDocInfo->getVarType(); - } - public function replaceTagByAnother(PhpDocNode $phpDocNode, string $oldTag, string $newTag): void { $oldTag = AnnotationNaming::normalizeName($oldTag); @@ -483,35 +471,6 @@ public function getDoctrineFqnTargetEntity(Node $node): ?string return $relationTagValueNode->getFqnTargetEntity(); } - public function getParamTypeByName(FunctionLike $functionLike, string $paramName): Type - { - $this->ensureParamNameStartsWithDollar($paramName, __METHOD__); - - $paramTypes = $this->getParamTypesByName($functionLike); - return $paramTypes[$paramName] ?? new MixedType(); - } - - /** - * @todo Extract this logic to own service - */ - private function areTypesEquals(Type $firstType, Type $secondType): bool - { - if ($this->areBothSameScalarType($firstType, $secondType)) { - return true; - } - - // aliases and types - if ($this->areAliasedObjectMatchingFqnObject($firstType, $secondType)) { - return true; - } - - if ($this->typeHasher->areTypesEqual($firstType, $secondType)) { - return true; - } - - return $this->areArrayTypeWithSingleObjectChildToParent($firstType, $secondType); - } - /** * All class-type tags are FQN by default to keep default convention through the code. * Some people prefer FQN, some short. FQN can be shorten with \Rector\CodingStyle\Rector\Namespace_\ImportFullyQualifiedNamesRector later, while short prolonged not @@ -540,78 +499,4 @@ private function addTypeSpecificTag(Node $node, string $name, Type $type): void $node->setDocComment(new Doc($varDocComment)); } } - - private function ensureParamNameStartsWithDollar(string $paramName, string $location): void - { - if (Strings::startsWith($paramName, '$')) { - return; - } - - throw new ShouldNotHappenException(sprintf( - 'Param name "%s" must start with "$" in "%s()" method.', - $paramName, - $location - )); - } - - /** - * E.g. class A extends B, class B → A[] is subtype of B[] → keep A[] - */ - private function areArrayTypeWithSingleObjectChildToParent(Type $firstType, Type $secondType): bool - { - if (! $firstType instanceof ArrayType || ! $secondType instanceof ArrayType) { - return false; - } - - $firstArrayItemType = $firstType->getItemType(); - $secondArrayItemType = $secondType->getItemType(); - - if ($firstArrayItemType instanceof ObjectType && $secondArrayItemType instanceof ObjectType) { - $firstFqnClassName = $this->getFqnClassName($firstArrayItemType); - $secondFqnClassName = $this->getFqnClassName($secondArrayItemType); - - if (is_a($firstFqnClassName, $secondFqnClassName, true)) { - return true; - } - - if (is_a($secondFqnClassName, $firstFqnClassName, true)) { - return true; - } - } - - return false; - } - - private function getFqnClassName(ObjectType $objectType): string - { - if ($objectType instanceof ShortenedObjectType) { - return $objectType->getFullyQualifiedName(); - } - - return $objectType->getClassName(); - } - - private function areBothSameScalarType(Type $firstType, Type $secondType): bool - { - if ($firstType instanceof StringType && $secondType instanceof StringType) { - return true; - } - - if ($firstType instanceof IntegerType && $secondType instanceof IntegerType) { - return true; - } - - if ($firstType instanceof FloatType && $secondType instanceof FloatType) { - return true; - } - return $firstType instanceof BooleanType && $secondType instanceof BooleanType; - } - - private function areAliasedObjectMatchingFqnObject(Type $firstType, Type $secondType): bool - { - if ($firstType instanceof AliasedObjectType && $secondType instanceof ObjectType && $firstType->getFullyQualifiedClass() === $secondType->getClassName()) { - return true; - } - return $secondType instanceof AliasedObjectType && $firstType instanceof ObjectType && $secondType->getFullyQualifiedClass() === $firstType->getClassName(); - } } diff --git a/packages/TypeDeclaration/src/Rector/Property/PropertyTypeDeclarationRector.php b/packages/TypeDeclaration/src/Rector/Property/PropertyTypeDeclarationRector.php index 3a4ecaa54be9..4d508db910df 100644 --- a/packages/TypeDeclaration/src/Rector/Property/PropertyTypeDeclarationRector.php +++ b/packages/TypeDeclaration/src/Rector/Property/PropertyTypeDeclarationRector.php @@ -7,6 +7,8 @@ use PhpParser\Node; use PhpParser\Node\Stmt\Property; use PHPStan\Type\MixedType; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; +use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\Rector\AbstractRector; use Rector\RectorDefinition\RectorDefinition; use Rector\TypeDeclaration\TypeInferer\PropertyTypeInferer; @@ -51,7 +53,14 @@ public function refactor(Node $node): ?Node } // is already set - $currentVarType = $this->docBlockManipulator->getVarType($node); + /** @var PhpDocInfo|null $phpDocInfo */ + $phpDocInfo = $node->getAttribute(AttributeKey::PHP_DOC_INFO); + if ($phpDocInfo instanceof PhpDocInfo) { + $currentVarType = $phpDocInfo->getVarType(); + } else { + $currentVarType = new MixedType(); + } + if (! $currentVarType instanceof MixedType) { return null; } diff --git a/src/Rector/Property/InjectAnnotationClassRector.php b/src/Rector/Property/InjectAnnotationClassRector.php index be88b3167e3d..a807019e86b2 100644 --- a/src/Rector/Property/InjectAnnotationClassRector.php +++ b/src/Rector/Property/InjectAnnotationClassRector.php @@ -173,7 +173,10 @@ private function resolveType(Node $node, PhpDocTagValueNode $phpDocTagValueNode) } if ($phpDocTagValueNode instanceof PHPDIInjectTagValueNode) { - return $this->docBlockManipulator->getVarType($node); + /** @var PhpDocInfo $phpDocInfo */ + $phpDocInfo = $node->getAttribute(AttributeKey::PHP_DOC_INFO); + + return $phpDocInfo->getVarType(); } throw new ShouldNotHappenException(); From e5b36d999c72ae20d8b028e350c7f2d119dae80b Mon Sep 17 00:00:00 2001 From: TomasVotruba Date: Fri, 31 Jan 2020 09:28:32 +0100 Subject: [PATCH 3/4] remove getReturtType() --- .../NodeAnalyzer/DocBlockManipulator.php | 16 ++++---------- .../GetterNodeParamTypeInferer.php | 22 +++++++++---------- .../ReturnTagReturnTypeInferer.php | 22 +++++++++---------- 3 files changed, 24 insertions(+), 36 deletions(-) diff --git a/packages/NodeTypeResolver/src/PhpDoc/NodeAnalyzer/DocBlockManipulator.php b/packages/NodeTypeResolver/src/PhpDoc/NodeAnalyzer/DocBlockManipulator.php index d883ef917e1e..f457380724d4 100644 --- a/packages/NodeTypeResolver/src/PhpDoc/NodeAnalyzer/DocBlockManipulator.php +++ b/packages/NodeTypeResolver/src/PhpDoc/NodeAnalyzer/DocBlockManipulator.php @@ -175,17 +175,6 @@ public function replaceAnnotationInNode(Node $node, string $oldAnnotation, strin $this->replaceTagByAnother($phpDocInfo->getPhpDocNode(), $oldAnnotation, $newAnnotation); } - public function getReturnType(Node $node): Type - { - /** @var PhpDocInfo|null $phpDocInfo */ - $phpDocInfo = $node->getAttribute(AttributeKey::PHP_DOC_INFO); - if ($phpDocInfo === null) { - return new MixedType(); - } - - return $phpDocInfo->getReturnType(); - } - /** * With "name" as key * @@ -267,7 +256,10 @@ public function changeVarTag(Node $node, Type $newType): void public function addReturnTag(Node $node, Type $newType): void { - $currentReturnType = $this->getReturnType($node); + /** @var PhpDocInfo|null $phpDocInfo */ + $phpDocInfo = $node->getAttribute(AttributeKey::PHP_DOC_INFO); + + $currentReturnType = $phpDocInfo !== null ? $phpDocInfo->getReturnType() : new MixedType(); // make sure the tags are not identical, e.g imported class vs FQN class if ($this->typeComparator->areTypesEquals($currentReturnType, $newType)) { diff --git a/packages/TypeDeclaration/src/TypeInferer/ParamTypeInferer/GetterNodeParamTypeInferer.php b/packages/TypeDeclaration/src/TypeInferer/ParamTypeInferer/GetterNodeParamTypeInferer.php index cbf2fdb0ec5a..0664ae180f79 100644 --- a/packages/TypeDeclaration/src/TypeInferer/ParamTypeInferer/GetterNodeParamTypeInferer.php +++ b/packages/TypeDeclaration/src/TypeInferer/ParamTypeInferer/GetterNodeParamTypeInferer.php @@ -12,8 +12,8 @@ use PhpParser\NodeTraverser; use PHPStan\Type\MixedType; use PHPStan\Type\Type; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; use Rector\NodeTypeResolver\Node\AttributeKey; -use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockManipulator; use Rector\PhpParser\Node\Manipulator\PropertyFetchManipulator; use Rector\TypeDeclaration\Contract\TypeInferer\ParamTypeInfererInterface; use Rector\TypeDeclaration\TypeInferer\AbstractTypeInferer; @@ -25,17 +25,9 @@ final class GetterNodeParamTypeInferer extends AbstractTypeInferer implements Pa */ private $propertyFetchManipulator; - /** - * @var DocBlockManipulator - */ - private $docBlockManipulator; - - public function __construct( - PropertyFetchManipulator $propertyFetchManipulator, - DocBlockManipulator $docBlockManipulator - ) { + public function __construct(PropertyFetchManipulator $propertyFetchManipulator) + { $this->propertyFetchManipulator = $propertyFetchManipulator; - $this->docBlockManipulator = $docBlockManipulator; } public function inferParam(Param $param): Type @@ -80,7 +72,13 @@ public function inferParam(Param $param): Type return null; } - $methodReturnType = $this->docBlockManipulator->getReturnType($methodNode); + /** @var PhpDocInfo|null $phpDocInfo */ + $phpDocInfo = $methodNode->getAttribute(AttributeKey::PHP_DOC_INFO); + if ($phpDocInfo === null) { + return null; + } + + $methodReturnType = $phpDocInfo->getReturnType(); if ($methodReturnType instanceof MixedType) { return null; } diff --git a/packages/TypeDeclaration/src/TypeInferer/ReturnTypeInferer/ReturnTagReturnTypeInferer.php b/packages/TypeDeclaration/src/TypeInferer/ReturnTypeInferer/ReturnTagReturnTypeInferer.php index 8e8132ba4d53..c88c9c5b8568 100644 --- a/packages/TypeDeclaration/src/TypeInferer/ReturnTypeInferer/ReturnTagReturnTypeInferer.php +++ b/packages/TypeDeclaration/src/TypeInferer/ReturnTypeInferer/ReturnTagReturnTypeInferer.php @@ -8,29 +8,27 @@ use PhpParser\Node\FunctionLike; use PhpParser\Node\Stmt\ClassMethod; use PhpParser\Node\Stmt\Function_; +use PHPStan\Type\MixedType; use PHPStan\Type\Type; -use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockManipulator; +use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; +use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\TypeDeclaration\Contract\TypeInferer\ReturnTypeInfererInterface; use Rector\TypeDeclaration\TypeInferer\AbstractTypeInferer; final class ReturnTagReturnTypeInferer extends AbstractTypeInferer implements ReturnTypeInfererInterface { - /** - * @var DocBlockManipulator - */ - private $docBlockManipulator; - - public function __construct(DocBlockManipulator $docBlockManipulator) - { - $this->docBlockManipulator = $docBlockManipulator; - } - /** * @param ClassMethod|Closure|Function_ $functionLike */ public function inferFunctionLike(FunctionLike $functionLike): Type { - return $this->docBlockManipulator->getReturnType($functionLike); + /** @var PhpDocInfo|null $phpDocInfo */ + $phpDocInfo = $functionLike->getAttribute(AttributeKey::PHP_DOC_INFO); + if ($phpDocInfo === null) { + return new MixedType(); + } + + return $phpDocInfo->getReturnType(); } public function getPriority(): int From cdd2febe75b744e48515549e5c5c262e339b8389 Mon Sep 17 00:00:00 2001 From: TomasVotruba Date: Fri, 31 Jan 2020 09:52:34 +0100 Subject: [PATCH 4/4] remove getTagByName() --- ecs.yaml | 1 + .../NodeAnalyzer/DocBlockManipulator.php | 36 ++++++------------- .../RemoveNonExistingVarAnnotationRector.php | 35 ++++++++---------- .../BetterStandardPrinterTrait.php | 8 +++++ 4 files changed, 34 insertions(+), 46 deletions(-) diff --git a/ecs.yaml b/ecs.yaml index 04467c01588b..d6348df7e65d 100644 --- a/ecs.yaml +++ b/ecs.yaml @@ -76,6 +76,7 @@ parameters: - 'src/Rector/AbstractRector.php' Symplify\CodingStandard\Sniffs\CleanCode\CognitiveComplexitySniff: + - 'packages/NodeTypeResolver/src/PhpDoc/NodeAnalyzer/DocBlockManipulator.php' - 'packages/MinimalScope/src/Rector/Class_/ChangeLocalPropertyToVariableRector.php' - 'packages/CodingStyle/src/Rector/ClassMethod/NewlineBeforeNewAssignSetRector.php' # solve later diff --git a/packages/NodeTypeResolver/src/PhpDoc/NodeAnalyzer/DocBlockManipulator.php b/packages/NodeTypeResolver/src/PhpDoc/NodeAnalyzer/DocBlockManipulator.php index f457380724d4..a95f9866e4d5 100644 --- a/packages/NodeTypeResolver/src/PhpDoc/NodeAnalyzer/DocBlockManipulator.php +++ b/packages/NodeTypeResolver/src/PhpDoc/NodeAnalyzer/DocBlockManipulator.php @@ -16,7 +16,6 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode; use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode; use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode; -use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode; use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode; use PHPStan\Type\Constant\ConstantArrayType; use PHPStan\Type\MixedType; @@ -35,7 +34,6 @@ use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; use Rector\BetterPhpDocParser\PhpDocNode\AbstractTagValueNode; use Rector\BetterPhpDocParser\Printer\PhpDocInfoPrinter; -use Rector\NodeTypeResolver\Exception\MissingTagException; use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\NodeTypeResolver\PHPStan\TypeComparator; use Rector\NodeTypeResolver\StaticTypeMapper; @@ -237,15 +235,14 @@ public function changeVarTag(Node $node, Type $newType): void return; } - if ($this->hasTag($node, '@var')) { - // just change the type - $varTag = $this->getTagByName($node, '@var'); - - /** @var VarTagValueNode $varTagValueNode */ - $varTagValueNode = $varTag->value; - - $phpDocType = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode($newType); - $varTagValueNode->type = $phpDocType; + if ($phpDocInfo !== null) { + $currentVarTagValue = $phpDocInfo->getVarTagValue(); + if ($currentVarTagValue !== null) { + $phpDocType = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode($newType); + $currentVarTagValue->type = $phpDocType; + } else { + $this->addTypeSpecificTag($node, 'var', $newType); + } } else { $this->addTypeSpecificTag($node, 'var', $newType); } @@ -285,20 +282,6 @@ public function addReturnTag(Node $node, Type $newType): void $returnTagValueNode->type = $newPHPStanPhpDocType; } - /** - * @final - */ - public function getTagByName(Node $node, string $name): PhpDocTagNode - { - if (! $this->hasTag($node, $name)) { - throw new MissingTagException(sprintf('Tag "%s" was not found at "%s" node.', $name, get_class($node))); - } - - /** @var PhpDocTagNode[] $foundTags */ - $foundTags = $this->getTagsByName($node, $name); - return array_shift($foundTags); - } - public function replaceTagByAnother(PhpDocNode $phpDocNode, string $oldTag, string $newTag): void { $oldTag = AnnotationNaming::normalizeName($oldTag); @@ -489,6 +472,9 @@ private function addTypeSpecificTag(Node $node, string $name, Type $type): void // create completely new docblock $varDocComment = sprintf("/**\n * @%s %s\n */", $name, $docStringType); $node->setDocComment(new Doc($varDocComment)); + + // bind new content with node + $this->phpDocInfoFactory->createFromNode($node); } } } diff --git a/packages/PHPStan/src/Rector/Node/RemoveNonExistingVarAnnotationRector.php b/packages/PHPStan/src/Rector/Node/RemoveNonExistingVarAnnotationRector.php index b444d34812ac..6659332ccff9 100644 --- a/packages/PHPStan/src/Rector/Node/RemoveNonExistingVarAnnotationRector.php +++ b/packages/PHPStan/src/Rector/Node/RemoveNonExistingVarAnnotationRector.php @@ -19,7 +19,6 @@ use PhpParser\Node\Stmt\Throw_; use PhpParser\Node\Stmt\While_; use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode; -use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\AttributeAwareVarTagValueNode; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo; use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\Rector\AbstractRector; @@ -28,6 +27,7 @@ /** * @see \Rector\PHPStan\Tests\Rector\Node\RemoveNonExistingVarAnnotationRector\RemoveNonExistingVarAnnotationRectorTest + * * @see https://github.com/phpstan/phpstan/commit/d17e459fd9b45129c5deafe12bca56f30ea5ee99#diff-9f3541876405623b0d18631259763dc1 */ final class RemoveNonExistingVarAnnotationRector extends AbstractRector @@ -74,22 +74,29 @@ public function refactor(Node $node): ?Node return null; } - $variableName = $this->getVarTagVariableName($node); + /** @var PhpDocInfo|null $phpDocInfo */ + $phpDocInfo = $node->getAttribute(AttributeKey::PHP_DOC_INFO); + if ($phpDocInfo === null) { + return null; + } + + $varTagValue = $phpDocInfo->getVarTagValue(); + if ($varTagValue === null) { + return null; + } + + $variableName = $varTagValue->variableName; if ($variableName === null) { return null; } - $nodeContent = $this->print($node); - // clear phpdoc - @see https://regex101.com/r/uwY5KW/1 - $nodeContentWithoutPhpDoc = Strings::replace($nodeContent, '#\/\*\*(.*?)*\/#'); + $nodeContentWithoutPhpDoc = $this->printWithoutComments($node); // it's there if (Strings::match($nodeContentWithoutPhpDoc, '#' . preg_quote($variableName, '#') . '\b#')) { return null; } - /** @var PhpDocInfo $phpDocInfo */ - $phpDocInfo = $node->getAttribute(AttributeKey::PHP_DOC_INFO); $phpDocInfo->removeByType(VarTagValueNode::class); return $node; @@ -110,18 +117,4 @@ private function shouldSkip(Node $node): bool && ! $node instanceof Switch_ && ! $node instanceof Nop; } - - private function getVarTagVariableName(Node $node): ?string - { - if (! $this->docBlockManipulator->hasTag($node, 'var')) { - return null; - } - - $varTag = $this->docBlockManipulator->getTagByName($node, 'var'); - - /** @var AttributeAwareVarTagValueNode $varTagValue */ - $varTagValue = $varTag->value; - - return $varTagValue->variableName; - } } diff --git a/src/Rector/AbstractRector/BetterStandardPrinterTrait.php b/src/Rector/AbstractRector/BetterStandardPrinterTrait.php index 0ac658b40368..35d565144682 100644 --- a/src/Rector/AbstractRector/BetterStandardPrinterTrait.php +++ b/src/Rector/AbstractRector/BetterStandardPrinterTrait.php @@ -44,6 +44,14 @@ public function print($node): string return $this->betterStandardPrinter->print($node); } + /** + * @param Node|Node[]|null $node + */ + public function printWithoutComments($node): string + { + return $this->betterStandardPrinter->printWithoutComments($node); + } + /** * @param Node|Node[]|null $node */