decouple PhpDocTypeChanger

This commit is contained in:
TomasVotruba 2020-07-27 11:26:41 +02:00
parent 110316f471
commit 8b1cdcb46e
11 changed files with 233 additions and 166 deletions

View File

@ -15,9 +15,7 @@ use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ThrowsTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
use PHPStan\Type\Constant\ConstantArrayType;
use PHPStan\Type\MixedType;
use PHPStan\Type\NeverType;
use PHPStan\Type\Type;
use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\AttributeAwareParamTagValueNode;
use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\AttributeAwarePhpDocNode;
@ -29,14 +27,13 @@ use Rector\BetterPhpDocParser\Attributes\Ast\PhpDoc\SpacelessPhpDocTagNode;
use Rector\BetterPhpDocParser\Contract\PhpDocNode\AttributeAwareNodeInterface;
use Rector\BetterPhpDocParser\Contract\PhpDocNode\ShortNameAwareTagInterface;
use Rector\BetterPhpDocParser\Contract\PhpDocNode\TypeAwareTagValueNodeInterface;
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger;
use Rector\Core\Exception\NotImplementedException;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\NodeTypeResolver\PHPStan\TypeComparator;
use Rector\PhpAttribute\Contract\PhpAttributableTagNodeInterface;
use Rector\PHPStan\Type\FullyQualifiedObjectType;
use Rector\PHPStan\Type\ShortenedObjectType;
use Rector\StaticTypeMapper\StaticTypeMapper;
use Rector\TypeDeclaration\PhpDocParser\ParamPhpDocNodeFactory;
/**
* @see \Rector\BetterPhpDocParser\Tests\PhpDocInfo\PhpDocInfo\PhpDocInfoTest
@ -73,21 +70,16 @@ final class PhpDocInfo
*/
private $node;
/**
* @var TypeComparator
*/
private $typeComparator;
/**
* @var ParamPhpDocNodeFactory
*/
private $paramPhpDocNodeFactory;
/**
* @var bool
*/
private $isSingleLine = false;
/**
* @var PhpDocTypeChanger
*/
private $phpDocTypeChanger;
/**
* @param mixed[] $tokens
*/
@ -97,8 +89,7 @@ final class PhpDocInfo
string $originalContent,
StaticTypeMapper $staticTypeMapper,
Node $node,
TypeComparator $typeComparator,
ParamPhpDocNodeFactory $paramPhpDocNodeFactory
PhpDocTypeChanger $phpDocTypeChanger
) {
$this->phpDocNode = $attributeAwarePhpDocNode;
$this->tokens = $tokens;
@ -106,8 +97,7 @@ final class PhpDocInfo
$this->originalContent = $originalContent;
$this->staticTypeMapper = $staticTypeMapper;
$this->node = $node;
$this->typeComparator = $typeComparator;
$this->paramPhpDocNodeFactory = $paramPhpDocNodeFactory;
$this->phpDocTypeChanger = $phpDocTypeChanger;
}
public function getOriginalContent(): string
@ -347,49 +337,12 @@ final class PhpDocInfo
public function changeVarType(Type $newType): void
{
// make sure the tags are not identical, e.g imported class vs FQN class
if ($this->typeComparator->areTypesEquals($this->getVarType(), $newType)) {
return;
}
// prevent existing type override by mixed
if (! $this->getVarType() instanceof MixedType && $newType instanceof ConstantArrayType && $newType->getItemType() instanceof NeverType) {
return;
}
// override existing type
$newPHPStanPhpDocType = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode($newType);
$currentVarTagValueNode = $this->getVarTagValue();
if ($currentVarTagValueNode !== null) {
// only change type
$currentVarTagValueNode->type = $newPHPStanPhpDocType;
} else {
// add completely new one
$attributeAwareVarTagValueNode = new AttributeAwareVarTagValueNode($newPHPStanPhpDocType, '', '');
$this->addTagValueNode($attributeAwareVarTagValueNode);
}
$this->phpDocTypeChanger->changeVarType($this, $newType);
}
public function changeReturnType(Type $newType): void
{
// make sure the tags are not identical, e.g imported class vs FQN class
if ($this->typeComparator->areTypesEquals($this->getReturnType(), $newType)) {
return;
}
// override existing type
$newPHPStanPhpDocType = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode($newType);
$currentReturnTagValueNode = $this->getReturnTagValue();
if ($currentReturnTagValueNode !== null) {
// only change type
$currentReturnTagValueNode->type = $newPHPStanPhpDocType;
} else {
// add completely new one
$attributeAwareReturnTagValueNode = new AttributeAwareReturnTagValueNode($newPHPStanPhpDocType, '');
$this->addTagValueNode($attributeAwareReturnTagValueNode);
}
$this->phpDocTypeChanger->changeReturnType($this, $newType);
}
public function addBareTag(string $tag): void
@ -419,33 +372,26 @@ final class PhpDocInfo
public function changeParamType(Type $type, Param $param, string $paramName): void
{
$paramTagValueNode = $this->getParamTagValueByName($paramName);
$phpDocType = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode($type);
// override existing type
if ($paramTagValueNode !== null) {
$paramTagValueNode->type = $phpDocType;
return;
}
$paramTagValueNode = $this->paramPhpDocNodeFactory->create($type, $param);
$this->addTagValueNode($paramTagValueNode);
$this->phpDocTypeChanger->changeParamType($this, $type, $param, $paramName);
}
/**
* @return string[]
* @return class-string[]
*/
public function getThrowsClassNames(): array
{
$throwsClasses = [];
foreach ($this->getThrowsTypes() as $throwsType) {
if ($throwsType instanceof ShortenedObjectType) {
$throwsClasses[] = $throwsType->getFullyQualifiedName();
/** @var class-string $className */
$className = $throwsType->getFullyQualifiedName();
$throwsClasses[] = $className;
}
if ($throwsType instanceof FullyQualifiedObjectType) {
$throwsClasses[] = $throwsType->getClassName();
/** @var class-string $className */
$className = $throwsType->getClassName();
$throwsClasses[] = $className;
}
}
@ -462,7 +408,14 @@ final class PhpDocInfo
return $this->isSingleLine;
}
private function getParamTagValueByName(string $name): ?AttributeAwareParamTagValueNode
public function getReturnTagValue(): ?AttributeAwareReturnTagValueNode
{
/** @var AttributeAwareReturnTagValueNode[] $returnTagValueNodes */
$returnTagValueNodes = $this->phpDocNode->getReturnTagValues();
return $returnTagValueNodes[0] ?? null;
}
public function getParamTagValueByName(string $name): ?AttributeAwareParamTagValueNode
{
/** @var AttributeAwareParamTagValueNode $paramTagValue */
foreach ($this->phpDocNode->getParamTagValues() as $paramTagValue) {
@ -483,13 +436,6 @@ final class PhpDocInfo
return $this->staticTypeMapper->mapPHPStanPhpDocTypeToPHPStanType($phpDocTagValueNode, $this->node);
}
private function getReturnTagValue(): ?AttributeAwareReturnTagValueNode
{
/** @var AttributeAwareReturnTagValueNode[] $returnTagValueNodes */
$returnTagValueNodes = $this->phpDocNode->getReturnTagValues();
return $returnTagValueNodes[0] ?? null;
}
private function ensureTypeIsTagValueNode(string $type, string $location): void
{
if (is_a($type, PhpDocTagValueNode::class, true)) {

View File

@ -13,12 +13,11 @@ use Rector\BetterPhpDocParser\Attributes\Ast\AttributeAwareNodeFactory;
use Rector\BetterPhpDocParser\Attributes\Attribute\Attribute;
use Rector\BetterPhpDocParser\Contract\PhpDocNode\AttributeAwareNodeInterface;
use Rector\BetterPhpDocParser\Contract\PhpDocNodeFactoryInterface;
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger;
use Rector\BetterPhpDocParser\ValueObject\StartEndValueObject;
use Rector\Core\Configuration\CurrentNodeProvider;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\PHPStan\TypeComparator;
use Rector\StaticTypeMapper\StaticTypeMapper;
use Rector\TypeDeclaration\PhpDocParser\ParamPhpDocNodeFactory;
final class PhpDocInfoFactory
{
@ -42,37 +41,30 @@ final class PhpDocInfoFactory
*/
private $staticTypeMapper;
/**
* @var TypeComparator
*/
private $typeComparator;
/**
* @var AttributeAwareNodeFactory
*/
private $attributeAwareNodeFactory;
/**
* @var ParamPhpDocNodeFactory
* @var PhpDocTypeChanger
*/
private $paramPhpDocNodeFactory;
private $phpDocTypeChanger;
public function __construct(
AttributeAwareNodeFactory $attributeAwareNodeFactory,
CurrentNodeProvider $currentNodeProvider,
Lexer $lexer,
ParamPhpDocNodeFactory $paramPhpDocNodeFactory,
PhpDocParser $phpDocParser,
StaticTypeMapper $staticTypeMapper,
TypeComparator $typeComparator
PhpDocTypeChanger $phpDocTypeChanger
) {
$this->phpDocParser = $phpDocParser;
$this->lexer = $lexer;
$this->currentNodeProvider = $currentNodeProvider;
$this->staticTypeMapper = $staticTypeMapper;
$this->typeComparator = $typeComparator;
$this->attributeAwareNodeFactory = $attributeAwareNodeFactory;
$this->paramPhpDocNodeFactory = $paramPhpDocNodeFactory;
$this->phpDocTypeChanger = $phpDocTypeChanger;
}
public function createFromNode(Node $node): ?PhpDocInfo
@ -155,8 +147,7 @@ final class PhpDocInfoFactory
$content,
$this->staticTypeMapper,
$node,
$this->typeComparator,
$this->paramPhpDocNodeFactory
$this->phpDocTypeChanger
);
$node->setAttribute(AttributeKey::PHP_DOC_INFO, $phpDocInfo);

View File

@ -0,0 +1,109 @@
<?php
declare(strict_types=1);
namespace Rector\BetterPhpDocParser\PhpDocManipulator;
use PhpParser\Node\Param;
use PHPStan\Type\Constant\ConstantArrayType;
use PHPStan\Type\MixedType;
use PHPStan\Type\NeverType;
use PHPStan\Type\Type;
use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\AttributeAwareReturnTagValueNode;
use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\AttributeAwareVarTagValueNode;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\NodeTypeResolver\PHPStan\TypeComparator;
use Rector\StaticTypeMapper\StaticTypeMapper;
use Rector\TypeDeclaration\PhpDocParser\ParamPhpDocNodeFactory;
final class PhpDocTypeChanger
{
/**
* @var TypeComparator
*/
private $typeComparator;
/**
* @var StaticTypeMapper
*/
private $staticTypeMapper;
/**
* @var ParamPhpDocNodeFactory
*/
private $paramPhpDocNodeFactory;
public function __construct(
TypeComparator $typeComparator,
StaticTypeMapper $staticTypeMapper,
ParamPhpDocNodeFactory $paramPhpDocNodeFactory
) {
$this->typeComparator = $typeComparator;
$this->staticTypeMapper = $staticTypeMapper;
$this->paramPhpDocNodeFactory = $paramPhpDocNodeFactory;
}
public function changeVarType(PhpDocInfo $phpDocInfo, Type $newType): void
{
// make sure the tags are not identical, e.g imported class vs FQN class
if ($this->typeComparator->areTypesEquals($phpDocInfo->getVarType(), $newType)) {
return;
}
// prevent existing type override by mixed
if (! $phpDocInfo->getVarType() instanceof MixedType && $newType instanceof ConstantArrayType && $newType->getItemType() instanceof NeverType) {
return;
}
// override existing type
$newPHPStanPhpDocType = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode($newType);
$currentVarTagValueNode = $phpDocInfo->getVarTagValue();
if ($currentVarTagValueNode !== null) {
// only change type
$currentVarTagValueNode->type = $newPHPStanPhpDocType;
} else {
// add completely new one
$attributeAwareVarTagValueNode = new AttributeAwareVarTagValueNode($newPHPStanPhpDocType, '', '');
$phpDocInfo->addTagValueNode($attributeAwareVarTagValueNode);
}
}
public function changeReturnType(PhpDocInfo $phpDocInfo, Type $newType): void
{
// make sure the tags are not identical, e.g imported class vs FQN class
if ($this->typeComparator->areTypesEquals($phpDocInfo->getReturnType(), $newType)) {
return;
}
// override existing type
$newPHPStanPhpDocType = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode($newType);
$currentReturnTagValueNode = $phpDocInfo->getReturnTagValue();
if ($currentReturnTagValueNode !== null) {
// only change type
$currentReturnTagValueNode->type = $newPHPStanPhpDocType;
} else {
// add completely new one
$attributeAwareReturnTagValueNode = new AttributeAwareReturnTagValueNode($newPHPStanPhpDocType, '');
$phpDocInfo->addTagValueNode($attributeAwareReturnTagValueNode);
}
}
public function changeParamType(PhpDocInfo $phpDocInfo, Type $type, Param $param, string $paramName): void
{
$paramTagValueNode = $phpDocInfo->getParamTagValueByName($paramName);
$phpDocType = $this->staticTypeMapper->mapPHPStanTypeToPHPStanPhpDocTypeNode($type);
// override existing type
if ($paramTagValueNode !== null) {
$paramTagValueNode->type = $phpDocType;
return;
}
$paramTagValueNode = $this->paramPhpDocNodeFactory->create($type, $param);
$phpDocInfo->addTagValueNode($paramTagValueNode);
}
}

View File

@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Rector\BetterPhpDocParser\Printer;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTextNode;
final class EmptyPhpDocDetector
{
public function isPhpDocNodeEmpty(PhpDocNode $phpDocNode): bool
{
if (count($phpDocNode->children) === 0) {
return true;
}
foreach ($phpDocNode->children as $phpDocChildNode) {
if ($phpDocChildNode instanceof PhpDocTextNode) {
if ($phpDocChildNode->text !== '') {
return false;
}
} else {
return false;
}
}
return true;
}
}

View File

@ -7,7 +7,6 @@ namespace Rector\BetterPhpDocParser\Printer;
use Nette\Utils\Strings;
use PHPStan\PhpDocParser\Ast\PhpDoc\GenericTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTextNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
@ -77,14 +76,21 @@ final class PhpDocInfoPrinter
*/
private $spacePatternFactory;
/**
* @var EmptyPhpDocDetector
*/
private $emptyPhpDocDetector;
public function __construct(
MultilineSpaceFormatPreserver $multilineSpaceFormatPreserver,
OriginalSpacingRestorer $originalSpacingRestorer,
SpacePatternFactory $spacePatternFactory
SpacePatternFactory $spacePatternFactory,
EmptyPhpDocDetector $emptyPhpDocDetector
) {
$this->originalSpacingRestorer = $originalSpacingRestorer;
$this->multilineSpaceFormatPreserver = $multilineSpaceFormatPreserver;
$this->spacePatternFactory = $spacePatternFactory;
$this->emptyPhpDocDetector = $emptyPhpDocDetector;
}
/**
@ -127,7 +133,7 @@ final class PhpDocInfoPrinter
private function printPhpDocNode(AttributeAwarePhpDocNode $attributeAwarePhpDocNode): string
{
// no nodes were, so empty doc
if ($this->isPhpDocNodeEmpty($attributeAwarePhpDocNode)) {
if ($this->emptyPhpDocDetector->isPhpDocNodeEmpty($attributeAwarePhpDocNode)) {
return '';
}
@ -162,25 +168,6 @@ final class PhpDocInfoPrinter
return Strings::replace($phpDocString, '#([^*])\*[ \t]+$#sm', '$1*');
}
private function isPhpDocNodeEmpty(PhpDocNode $phpDocNode): bool
{
if (count($phpDocNode->children) === 0) {
return true;
}
foreach ($phpDocNode->children as $phpDocChildNode) {
if ($phpDocChildNode instanceof PhpDocTextNode) {
if ($phpDocChildNode->text !== '') {
return false;
}
} else {
return false;
}
}
return true;
}
private function printNode(
AttributeAwareNodeInterface $attributeAwareNode,
?StartEndValueObject $startEndValueObject = null,
@ -371,16 +358,19 @@ final class PhpDocInfoPrinter
*/
private function resolveTagSpaceSeparator(PhpDocTagNode $phpDocTagNode): string
{
if ($this->isCommonTag($phpDocTagNode)) {
return ' ';
}
$originalContent = $this->phpDocInfo->getOriginalContent();
$spacePattern = $this->spacePatternFactory->createSpacePattern($phpDocTagNode);
$matches = Strings::match($originalContent, $spacePattern);
if (isset($matches['space'])) {
return $matches['space'];
}
return $matches['space'] ?? '';
if ($this->isCommonTag($phpDocTagNode)) {
return ' ';
}
return '';
}
private function hasDescription(AttributeAwarePhpDocTagNode $attributeAwarePhpDocTagNode): bool

View File

@ -3,58 +3,38 @@
declare(strict_types=1);
use Rector\Core\Configuration\Option;
use Rector\Injection\Rector\StaticCall\StaticCallToAnotherServiceConstructorInjectionRector;
use Rector\Injection\ValueObject\StaticCallToMethodCall;
use Rector\Set\ValueObject\SetList;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use function Symfony\Component\DependencyInjection\Loader\Configurator\ref;
return static function (ContainerConfigurator $containerConfigurator): void {
$containerConfigurator->import(__DIR__ . '/create-rector.php', null, 'not_found');
$services = $containerConfigurator->services();
// @todo improve this
$services->set('value_object', StaticCallToMethodCall::class)
->args(['Nette\Utils\FileSystem', 'write', 'Symplify\SmartFileSystem\SmartFileSystem', 'dumpFile'])
->autowire(false);
$services->set(StaticCallToAnotherServiceConstructorInjectionRector::class)
->arg('$staticCallsToMethodCalls', [ref('value_object')]);
$parameters = $containerConfigurator->parameters();
# Rector\Naming\Rector\ClassMethod\RenameVariableToMatchNewTypeRector: null
# Rector\Autodiscovery\Rector\FileSystem\MoveInterfacesToContractNamespaceDirectoryRector: null
# bleeding edge feature
# is_cache_enabled: true
$parameters->set(Option::AUTO_IMPORT_NAMES, true);
$parameters->set(Option::SETS, [SetList::NAMING]);
$parameters->set(
Option::PATHS,
[
__DIR__ . '/src',
__DIR__ . '/tests',
__DIR__ . '/rules',
__DIR__ . '/utils',
__DIR__ . '/packages',
__DIR__ . '/bin/rector',
]
);
$parameters->set(Option::PATHS, [
__DIR__ . '/src',
__DIR__ . '/tests',
__DIR__ . '/rules',
__DIR__ . '/utils',
__DIR__ . '/packages',
__DIR__ . '/bin/rector',
]);
$parameters->set(
Option::EXCLUDE_PATHS,
[
'/Source/',
'/*Source/',
'/Fixture/',
'/Expected/',
__DIR__ . '/packages/doctrine-annotation-generated/src/*',
'*.php.inc',
]
);
$parameters->set(Option::EXCLUDE_PATHS, [
'/Source/',
'/*Source/',
'/Fixture/',
'/Expected/',
__DIR__ . '/packages/doctrine-annotation-generated/src/*',
'*.php.inc',
]);
# so Rector code is still PHP 7.2 compatible
$parameters->set(Option::PHP_VERSION_FEATURES, '7.2');

View File

@ -221,6 +221,9 @@ PHP
return new AttributeAwarePhpDocTagNode('@throws', $throwsTagValueNode);
}
/**
* @return array<class-string>
*/
private function identifyThrownThrowablesInStaticCall(StaticCall $staticCall): array
{
$thrownClass = $staticCall->class;
@ -242,13 +245,14 @@ PHP
return [];
}
return $methodCall->getAttribute('parentNode') instanceof Throw_
? $this->extractMethodReturns($fullyQualified, $methodName)
$parent = $methodCall->getAttribute(AttributeKey::PARENT_NODE);
return $parent instanceof Throw_ ? $this->extractMethodReturns($fullyQualified, $methodName)
: $this->extractMethodThrows($fullyQualified, $methodName);
}
/**
* @return string[]
* @return class-string[]
*/
private function extractAlreadyAnnotatedThrowables(Node $node): array
{
@ -288,6 +292,9 @@ PHP
return count($this->throwablesToAnnotate);
}
/**
* @return array<class-string>
*/
private function extractMethodReturns(FullyQualified $fullyQualified, Identifier $identifier): array
{
$method = $identifier->name;
@ -300,6 +307,9 @@ PHP
return $this->classMethodReflectionHelper->extractTagsFromMethodDockblock($class, $method, '@return');
}
/**
* @return array<class-string>
*/
private function extractMethodThrows(FullyQualified $fullyQualified, Identifier $identifier): array
{
$method = $identifier->name;

View File

@ -270,12 +270,12 @@ PHP
}
/**
* @param Name|Identifier $usedName
* @param Name|Identifier $usedNameNode
*/
private function renameInterface(string $lastName, Interface_ $interface, Node $usedName): void
private function renameInterface(string $lastName, Interface_ $interface, Node $usedNameNode): void
{
foreach ($interface->extends as $key => $extendInterfaceName) {
if ($this->areNamesEqual($extendInterfaceName, $usedName)) {
if ($this->areNamesEqual($extendInterfaceName, $usedNameNode)) {
$interface->extends[$key] = new Name($lastName);
}
}

View File

@ -15,6 +15,7 @@ use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Name;
use PhpParser\Node\Param;
use PhpParser\Node\Stmt\Property;
use PHPStan\Type\ArrayType;
use PHPStan\Type\MixedType;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\NodeNameResolver\NodeNameResolver;
@ -176,6 +177,11 @@ final class ExpectedNameResolver
}
$returnedType = $this->nodeTypeResolver->getStaticType($expr);
if ($returnedType instanceof ArrayType) {
return null;
}
if ($returnedType instanceof MixedType) {
return null;
}

View File

@ -142,7 +142,7 @@ PHP
}
$expectedName = $this->expectedNameResolver->resolveForCall($variableAndCallAssign->getCall());
if ($expectedName === null || $this->isName($node, $expectedName)) {
if ($expectedName === null || $this->isName($node->var, $expectedName)) {
return null;
}

View File

@ -27,6 +27,9 @@ final class ClassMethodReflectionHelper
$this->phpDocTagsFinder = $phpDocTagsFinder;
}
/**
* @return array<class-string>
*/
public function extractTagsFromMethodDockblock(string $class, string $method, string $tag): array
{
$reflectedMethod = $this->classMethodReflectionFactory->createReflectionMethodIfExists($class, $method);
@ -45,7 +48,9 @@ final class ClassMethodReflectionHelper
$classes = [];
foreach ($extractedTags as $returnTag) {
$classes[] = Reflection::expandClassName($returnTag, $reflectedMethod->getDeclaringClass());
/** @var class-string $className */
$className = Reflection::expandClassName($returnTag, $reflectedMethod->getDeclaringClass());
$classes[] = $className;
}
return $classes;