diff --git a/rules/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector.php b/rules/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector.php index 2aa5a921d28..2c5696b0427 100644 --- a/rules/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector.php +++ b/rules/TypeDeclarationDocblocks/Rector/Class_/ClassMethodArrayDocblockParamFromLocalCallsRector.php @@ -7,6 +7,7 @@ use PhpParser\Node; use PhpParser\Node\Stmt\Class_; use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode; +use PHPStan\Type\Type; use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfoFactory; use Rector\Comments\NodeDocBlock\DocBlockUpdater; use Rector\PhpParser\NodeFinder\LocalMethodCallFinder; @@ -94,7 +95,11 @@ public function refactor(Node $node): ?Node $classMethodParameterTypes = $this->callTypesResolver->resolveStrictTypesFromCalls($methodCalls); foreach ($classMethod->getParams() as $parameterPosition => $param) { - if ($param->type === null || ! $this->isName($param->type, 'array')) { + if ($param->type === null) { + continue; + } + + if (! $this->isName($param->type, 'array')) { continue; } @@ -107,7 +112,7 @@ public function refactor(Node $node): ?Node } $resolvedParameterType = $classMethodParameterTypes[$parameterPosition] ?? null; - if (! $resolvedParameterType instanceof \PHPStan\Type\Type) { + if (! $resolvedParameterType instanceof Type) { continue; } diff --git a/src/BetterPhpDocParser/PhpDocInfo/PhpDocInfo.php b/src/BetterPhpDocParser/PhpDocInfo/PhpDocInfo.php index b4f0db31db8..a4bfec55866 100644 --- a/src/BetterPhpDocParser/PhpDocInfo/PhpDocInfo.php +++ b/src/BetterPhpDocParser/PhpDocInfo/PhpDocInfo.php @@ -451,9 +451,22 @@ public function getGenericTagClassNames(): array $resolvedClasses = []; foreach ($genericTagValueNodes as $genericTagValueNode) { - if ($genericTagValueNode->value !== '') { + if ($genericTagValueNode->value === '') { + continue; + } + + if (! str_contains($genericTagValueNode->value, '::')) { + $resolvedClasses[] = $genericTagValueNode->value; + continue; + } + + $resolvedClass = $genericTagValueNode->getAttribute(PhpDocAttributeKey::RESOLVED_CLASS); + if ($resolvedClass === null) { $resolvedClasses[] = $genericTagValueNode->value; + continue; } + + $resolvedClasses[] = $resolvedClass; } return $resolvedClasses; diff --git a/src/BetterPhpDocParser/PhpDocParser/PhpDocTagGenericUsesDecorator.php b/src/BetterPhpDocParser/PhpDocParser/PhpDocTagGenericUsesDecorator.php new file mode 100644 index 00000000000..45f0444e9ee --- /dev/null +++ b/src/BetterPhpDocParser/PhpDocParser/PhpDocTagGenericUsesDecorator.php @@ -0,0 +1,75 @@ +__toString(), '::')) { + return; + } + + $this->phpDocNodeTraverser->traverseWithCallable($phpDocNode, '', function (Node $node) use ( + $phpNode + ): Node|null { + if (! $node instanceof PhpDocTagNode) { + return null; + } + + if (! $node->value instanceof GenericTagValueNode) { + return null; + } + + if (! in_array($node->name, ['@uses', '@used-by'], true)) { + return null; + } + + $reference = $node->value->value; + if (! str_contains($reference, '::')) { + return null; + } + + if ($node->value->hasAttribute(PhpDocAttributeKey::RESOLVED_CLASS)) { + return null; + } + + $classValue = explode('::', $reference)[0]; + $className = $this->resolveFullyQualifiedClass($classValue, $phpNode); + $node->value->setAttribute(PhpDocAttributeKey::RESOLVED_CLASS, $className); + + return $node; + }); + } + + private function resolveFullyQualifiedClass(string $classValue, PhpNode $phpNode): string + { + $nameScope = $this->nameScopeFactory->createNameScopeFromNodeWithoutTemplateTypes($phpNode); + return $nameScope->resolveStringName($classValue); + } +} diff --git a/src/DependencyInjection/LazyContainerFactory.php b/src/DependencyInjection/LazyContainerFactory.php index 6946e5cf318..3e78fa7f42a 100644 --- a/src/DependencyInjection/LazyContainerFactory.php +++ b/src/DependencyInjection/LazyContainerFactory.php @@ -29,6 +29,7 @@ use Rector\BetterPhpDocParser\PhpDocParser\BetterPhpDocParser; use Rector\BetterPhpDocParser\PhpDocParser\ConstExprClassNameDecorator; use Rector\BetterPhpDocParser\PhpDocParser\DoctrineAnnotationDecorator; +use Rector\BetterPhpDocParser\PhpDocParser\PhpDocTagGenericUsesDecorator; use Rector\BetterPhpDocParser\PhpDocParser\StaticDoctrineAnnotationParser; use Rector\BetterPhpDocParser\PhpDocParser\StaticDoctrineAnnotationParser\ArrayParser; use Rector\BetterPhpDocParser\PhpDocParser\StaticDoctrineAnnotationParser\PlainValueParser; @@ -316,6 +317,7 @@ final class LazyContainerFactory ConstExprClassNameDecorator::class, DoctrineAnnotationDecorator::class, ArrayItemClassNameDecorator::class, + PhpDocTagGenericUsesDecorator::class, ]; /** diff --git a/tests/Issues/NoNamespaced/Fixture/skip_used_in_used_by.php.inc b/tests/Issues/NoNamespaced/Fixture/skip_used_in_used_by.php.inc new file mode 100644 index 00000000000..53dc9960fe4 --- /dev/null +++ b/tests/Issues/NoNamespaced/Fixture/skip_used_in_used_by.php.inc @@ -0,0 +1,8 @@ +setLocations('locations'); \ No newline at end of file