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\ReturnTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ThrowsTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\ThrowsTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\VarTagValueNode;
use PHPStan\Type\Constant\ConstantArrayType;
use PHPStan\Type\MixedType; use PHPStan\Type\MixedType;
use PHPStan\Type\NeverType;
use PHPStan\Type\Type; use PHPStan\Type\Type;
use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\AttributeAwareParamTagValueNode; use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\AttributeAwareParamTagValueNode;
use Rector\AttributeAwarePhpDoc\Ast\PhpDoc\AttributeAwarePhpDocNode; 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\AttributeAwareNodeInterface;
use Rector\BetterPhpDocParser\Contract\PhpDocNode\ShortNameAwareTagInterface; use Rector\BetterPhpDocParser\Contract\PhpDocNode\ShortNameAwareTagInterface;
use Rector\BetterPhpDocParser\Contract\PhpDocNode\TypeAwareTagValueNodeInterface; use Rector\BetterPhpDocParser\Contract\PhpDocNode\TypeAwareTagValueNodeInterface;
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger;
use Rector\Core\Exception\NotImplementedException; use Rector\Core\Exception\NotImplementedException;
use Rector\Core\Exception\ShouldNotHappenException; use Rector\Core\Exception\ShouldNotHappenException;
use Rector\NodeTypeResolver\PHPStan\TypeComparator;
use Rector\PhpAttribute\Contract\PhpAttributableTagNodeInterface; use Rector\PhpAttribute\Contract\PhpAttributableTagNodeInterface;
use Rector\PHPStan\Type\FullyQualifiedObjectType; use Rector\PHPStan\Type\FullyQualifiedObjectType;
use Rector\PHPStan\Type\ShortenedObjectType; use Rector\PHPStan\Type\ShortenedObjectType;
use Rector\StaticTypeMapper\StaticTypeMapper; use Rector\StaticTypeMapper\StaticTypeMapper;
use Rector\TypeDeclaration\PhpDocParser\ParamPhpDocNodeFactory;
/** /**
* @see \Rector\BetterPhpDocParser\Tests\PhpDocInfo\PhpDocInfo\PhpDocInfoTest * @see \Rector\BetterPhpDocParser\Tests\PhpDocInfo\PhpDocInfo\PhpDocInfoTest
@ -73,21 +70,16 @@ final class PhpDocInfo
*/ */
private $node; private $node;
/**
* @var TypeComparator
*/
private $typeComparator;
/**
* @var ParamPhpDocNodeFactory
*/
private $paramPhpDocNodeFactory;
/** /**
* @var bool * @var bool
*/ */
private $isSingleLine = false; private $isSingleLine = false;
/**
* @var PhpDocTypeChanger
*/
private $phpDocTypeChanger;
/** /**
* @param mixed[] $tokens * @param mixed[] $tokens
*/ */
@ -97,8 +89,7 @@ final class PhpDocInfo
string $originalContent, string $originalContent,
StaticTypeMapper $staticTypeMapper, StaticTypeMapper $staticTypeMapper,
Node $node, Node $node,
TypeComparator $typeComparator, PhpDocTypeChanger $phpDocTypeChanger
ParamPhpDocNodeFactory $paramPhpDocNodeFactory
) { ) {
$this->phpDocNode = $attributeAwarePhpDocNode; $this->phpDocNode = $attributeAwarePhpDocNode;
$this->tokens = $tokens; $this->tokens = $tokens;
@ -106,8 +97,7 @@ final class PhpDocInfo
$this->originalContent = $originalContent; $this->originalContent = $originalContent;
$this->staticTypeMapper = $staticTypeMapper; $this->staticTypeMapper = $staticTypeMapper;
$this->node = $node; $this->node = $node;
$this->typeComparator = $typeComparator; $this->phpDocTypeChanger = $phpDocTypeChanger;
$this->paramPhpDocNodeFactory = $paramPhpDocNodeFactory;
} }
public function getOriginalContent(): string public function getOriginalContent(): string
@ -347,49 +337,12 @@ final class PhpDocInfo
public function changeVarType(Type $newType): void public function changeVarType(Type $newType): void
{ {
// make sure the tags are not identical, e.g imported class vs FQN class $this->phpDocTypeChanger->changeVarType($this, $newType);
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);
}
} }
public function changeReturnType(Type $newType): void public function changeReturnType(Type $newType): void
{ {
// make sure the tags are not identical, e.g imported class vs FQN class $this->phpDocTypeChanger->changeReturnType($this, $newType);
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);
}
} }
public function addBareTag(string $tag): void public function addBareTag(string $tag): void
@ -419,33 +372,26 @@ final class PhpDocInfo
public function changeParamType(Type $type, Param $param, string $paramName): void public function changeParamType(Type $type, Param $param, string $paramName): void
{ {
$paramTagValueNode = $this->getParamTagValueByName($paramName); $this->phpDocTypeChanger->changeParamType($this, $type, $param, $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);
} }
/** /**
* @return string[] * @return class-string[]
*/ */
public function getThrowsClassNames(): array public function getThrowsClassNames(): array
{ {
$throwsClasses = []; $throwsClasses = [];
foreach ($this->getThrowsTypes() as $throwsType) { foreach ($this->getThrowsTypes() as $throwsType) {
if ($throwsType instanceof ShortenedObjectType) { if ($throwsType instanceof ShortenedObjectType) {
$throwsClasses[] = $throwsType->getFullyQualifiedName(); /** @var class-string $className */
$className = $throwsType->getFullyQualifiedName();
$throwsClasses[] = $className;
} }
if ($throwsType instanceof FullyQualifiedObjectType) { 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; 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 */ /** @var AttributeAwareParamTagValueNode $paramTagValue */
foreach ($this->phpDocNode->getParamTagValues() as $paramTagValue) { foreach ($this->phpDocNode->getParamTagValues() as $paramTagValue) {
@ -483,13 +436,6 @@ final class PhpDocInfo
return $this->staticTypeMapper->mapPHPStanPhpDocTypeToPHPStanType($phpDocTagValueNode, $this->node); 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 private function ensureTypeIsTagValueNode(string $type, string $location): void
{ {
if (is_a($type, PhpDocTagValueNode::class, true)) { 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\Attributes\Attribute\Attribute;
use Rector\BetterPhpDocParser\Contract\PhpDocNode\AttributeAwareNodeInterface; use Rector\BetterPhpDocParser\Contract\PhpDocNode\AttributeAwareNodeInterface;
use Rector\BetterPhpDocParser\Contract\PhpDocNodeFactoryInterface; use Rector\BetterPhpDocParser\Contract\PhpDocNodeFactoryInterface;
use Rector\BetterPhpDocParser\PhpDocManipulator\PhpDocTypeChanger;
use Rector\BetterPhpDocParser\ValueObject\StartEndValueObject; use Rector\BetterPhpDocParser\ValueObject\StartEndValueObject;
use Rector\Core\Configuration\CurrentNodeProvider; use Rector\Core\Configuration\CurrentNodeProvider;
use Rector\NodeTypeResolver\Node\AttributeKey; use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\PHPStan\TypeComparator;
use Rector\StaticTypeMapper\StaticTypeMapper; use Rector\StaticTypeMapper\StaticTypeMapper;
use Rector\TypeDeclaration\PhpDocParser\ParamPhpDocNodeFactory;
final class PhpDocInfoFactory final class PhpDocInfoFactory
{ {
@ -42,37 +41,30 @@ final class PhpDocInfoFactory
*/ */
private $staticTypeMapper; private $staticTypeMapper;
/**
* @var TypeComparator
*/
private $typeComparator;
/** /**
* @var AttributeAwareNodeFactory * @var AttributeAwareNodeFactory
*/ */
private $attributeAwareNodeFactory; private $attributeAwareNodeFactory;
/** /**
* @var ParamPhpDocNodeFactory * @var PhpDocTypeChanger
*/ */
private $paramPhpDocNodeFactory; private $phpDocTypeChanger;
public function __construct( public function __construct(
AttributeAwareNodeFactory $attributeAwareNodeFactory, AttributeAwareNodeFactory $attributeAwareNodeFactory,
CurrentNodeProvider $currentNodeProvider, CurrentNodeProvider $currentNodeProvider,
Lexer $lexer, Lexer $lexer,
ParamPhpDocNodeFactory $paramPhpDocNodeFactory,
PhpDocParser $phpDocParser, PhpDocParser $phpDocParser,
StaticTypeMapper $staticTypeMapper, StaticTypeMapper $staticTypeMapper,
TypeComparator $typeComparator PhpDocTypeChanger $phpDocTypeChanger
) { ) {
$this->phpDocParser = $phpDocParser; $this->phpDocParser = $phpDocParser;
$this->lexer = $lexer; $this->lexer = $lexer;
$this->currentNodeProvider = $currentNodeProvider; $this->currentNodeProvider = $currentNodeProvider;
$this->staticTypeMapper = $staticTypeMapper; $this->staticTypeMapper = $staticTypeMapper;
$this->typeComparator = $typeComparator;
$this->attributeAwareNodeFactory = $attributeAwareNodeFactory; $this->attributeAwareNodeFactory = $attributeAwareNodeFactory;
$this->paramPhpDocNodeFactory = $paramPhpDocNodeFactory; $this->phpDocTypeChanger = $phpDocTypeChanger;
} }
public function createFromNode(Node $node): ?PhpDocInfo public function createFromNode(Node $node): ?PhpDocInfo
@ -155,8 +147,7 @@ final class PhpDocInfoFactory
$content, $content,
$this->staticTypeMapper, $this->staticTypeMapper,
$node, $node,
$this->typeComparator, $this->phpDocTypeChanger
$this->paramPhpDocNodeFactory
); );
$node->setAttribute(AttributeKey::PHP_DOC_INFO, $phpDocInfo); $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 Nette\Utils\Strings;
use PHPStan\PhpDocParser\Ast\PhpDoc\GenericTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\GenericTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\ParamTagValueNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode; use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTextNode; use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTextNode;
use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\ReturnTagValueNode;
@ -77,14 +76,21 @@ final class PhpDocInfoPrinter
*/ */
private $spacePatternFactory; private $spacePatternFactory;
/**
* @var EmptyPhpDocDetector
*/
private $emptyPhpDocDetector;
public function __construct( public function __construct(
MultilineSpaceFormatPreserver $multilineSpaceFormatPreserver, MultilineSpaceFormatPreserver $multilineSpaceFormatPreserver,
OriginalSpacingRestorer $originalSpacingRestorer, OriginalSpacingRestorer $originalSpacingRestorer,
SpacePatternFactory $spacePatternFactory SpacePatternFactory $spacePatternFactory,
EmptyPhpDocDetector $emptyPhpDocDetector
) { ) {
$this->originalSpacingRestorer = $originalSpacingRestorer; $this->originalSpacingRestorer = $originalSpacingRestorer;
$this->multilineSpaceFormatPreserver = $multilineSpaceFormatPreserver; $this->multilineSpaceFormatPreserver = $multilineSpaceFormatPreserver;
$this->spacePatternFactory = $spacePatternFactory; $this->spacePatternFactory = $spacePatternFactory;
$this->emptyPhpDocDetector = $emptyPhpDocDetector;
} }
/** /**
@ -127,7 +133,7 @@ final class PhpDocInfoPrinter
private function printPhpDocNode(AttributeAwarePhpDocNode $attributeAwarePhpDocNode): string private function printPhpDocNode(AttributeAwarePhpDocNode $attributeAwarePhpDocNode): string
{ {
// no nodes were, so empty doc // no nodes were, so empty doc
if ($this->isPhpDocNodeEmpty($attributeAwarePhpDocNode)) { if ($this->emptyPhpDocDetector->isPhpDocNodeEmpty($attributeAwarePhpDocNode)) {
return ''; return '';
} }
@ -162,25 +168,6 @@ final class PhpDocInfoPrinter
return Strings::replace($phpDocString, '#([^*])\*[ \t]+$#sm', '$1*'); 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( private function printNode(
AttributeAwareNodeInterface $attributeAwareNode, AttributeAwareNodeInterface $attributeAwareNode,
?StartEndValueObject $startEndValueObject = null, ?StartEndValueObject $startEndValueObject = null,
@ -371,16 +358,19 @@ final class PhpDocInfoPrinter
*/ */
private function resolveTagSpaceSeparator(PhpDocTagNode $phpDocTagNode): string private function resolveTagSpaceSeparator(PhpDocTagNode $phpDocTagNode): string
{ {
if ($this->isCommonTag($phpDocTagNode)) {
return ' ';
}
$originalContent = $this->phpDocInfo->getOriginalContent(); $originalContent = $this->phpDocInfo->getOriginalContent();
$spacePattern = $this->spacePatternFactory->createSpacePattern($phpDocTagNode); $spacePattern = $this->spacePatternFactory->createSpacePattern($phpDocTagNode);
$matches = Strings::match($originalContent, $spacePattern); $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 private function hasDescription(AttributeAwarePhpDocTagNode $attributeAwarePhpDocTagNode): bool

View File

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

View File

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

View File

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

View File

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

View File

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