move name-importing to PostRector

This commit is contained in:
TomasVotruba 2020-03-31 19:32:54 +02:00
parent a9ff5debf4
commit 38fbcb28e5
13 changed files with 418 additions and 336 deletions

View File

@ -11,7 +11,6 @@ use PHPStan\PhpDocParser\Ast\Node as PhpDocParserNode;
use PHPStan\PhpDocParser\Ast\Type\IdentifierTypeNode;
use Rector\BetterPhpDocParser\Ast\PhpDocNodeTraverser;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\CodingStyle\Application\UseAddingCommander;
use Rector\CodingStyle\Imports\ImportSkipper;
use Rector\Core\Configuration\Option;
use Rector\Core\PhpParser\Printer\BetterStandardPrinter;
@ -20,6 +19,7 @@ use Rector\NodeTypeResolver\ClassExistenceStaticHelper;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\PHPStan\Type\FullyQualifiedObjectType;
use Rector\PHPStan\Type\ShortenedObjectType;
use Rector\PostRector\Collector\UseNodesToAddCollector;
use Rector\StaticTypeMapper\StaticTypeMapper;
use Symplify\PackageBuilder\Parameter\ParameterProvider;
@ -45,11 +45,6 @@ final class DocBlockNameImporter
*/
private $staticTypeMapper;
/**
* @var UseAddingCommander
*/
private $useAddingCommander;
/**
* @var NodeNameResolver
*/
@ -70,22 +65,27 @@ final class DocBlockNameImporter
*/
private $parameterProvider;
/**
* @var UseNodesToAddCollector
*/
private $useNodesToAddCollector;
public function __construct(
PhpDocNodeTraverser $phpDocNodeTraverser,
StaticTypeMapper $staticTypeMapper,
UseAddingCommander $useAddingCommander,
NodeNameResolver $nodeNameResolver,
BetterStandardPrinter $betterStandardPrinter,
ImportSkipper $importSkipper,
ParameterProvider $parameterProvider
ParameterProvider $parameterProvider,
UseNodesToAddCollector $useNodesToAddCollector
) {
$this->phpDocNodeTraverser = $phpDocNodeTraverser;
$this->staticTypeMapper = $staticTypeMapper;
$this->useAddingCommander = $useAddingCommander;
$this->nodeNameResolver = $nodeNameResolver;
$this->betterStandardPrinter = $betterStandardPrinter;
$this->importSkipper = $importSkipper;
$this->parameterProvider = $parameterProvider;
$this->useNodesToAddCollector = $useNodesToAddCollector;
}
public function importNames(PhpDocInfo $phpDocInfo, Node $phpParserNode): bool
@ -132,8 +132,8 @@ final class DocBlockNameImporter
return $identifierTypeNode;
}
if ($this->useAddingCommander->isShortImported($node, $fullyQualifiedObjectType)) {
if ($this->useAddingCommander->isImportShortable($node, $fullyQualifiedObjectType)) {
if ($this->useNodesToAddCollector->isShortImported($node, $fullyQualifiedObjectType)) {
if ($this->useNodesToAddCollector->isImportShortable($node, $fullyQualifiedObjectType)) {
$identifierTypeNode->name = $fullyQualifiedObjectType->getShortName();
$this->hasPhpDocChanged = true;
}
@ -143,7 +143,7 @@ final class DocBlockNameImporter
$identifierTypeNode->name = $fullyQualifiedObjectType->getShortName();
$this->hasPhpDocChanged = true;
$this->useAddingCommander->addUseImport($node, $fullyQualifiedObjectType);
$this->useNodesToAddCollector->addUseImport($node, $fullyQualifiedObjectType);
return $identifierTypeNode;
}

View File

@ -8,10 +8,10 @@ use PhpParser\Node;
use PhpParser\NodeTraverser;
use Rector\Core\Contract\PhpParser\Node\CommanderInterface;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\Tests\Rector\Architecture\DoctrineRepositoryAsService\Source\Entity\Post;
use Rector\NodeTypeResolver\FileSystem\CurrentFileInfoProvider;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\PostRector\Contract\Rector\PostRectorInterface;
use Rector\PostRector\Rector\UseAddingPostRector;
use Symplify\SmartFileSystem\SmartFileInfo;
final class PostFileProcessor extends NodeTraverser
@ -26,12 +26,21 @@ final class PostFileProcessor extends NodeTraverser
*/
private $currentFileInfoProvider;
/**
* @var UseAddingPostRector
*/
private $useAddingPostRector;
/**
* @param CommanderInterface[] $commanders
* @param PostRectorInterface[] $postRectors
*/
public function __construct(CurrentFileInfoProvider $currentFileInfoProvider, array $commanders, array $postRectors)
{
public function __construct(
CurrentFileInfoProvider $currentFileInfoProvider,
array $commanders,
array $postRectors,
UseAddingPostRector $useAddingPostRector
) {
// A. slowly remove...
$commanders = array_merge($commanders, $postRectors);
@ -44,6 +53,8 @@ final class PostFileProcessor extends NodeTraverser
$this->addVisitor($commander);
}
}
$this->useAddingPostRector = $useAddingPostRector;
}
/**
@ -68,7 +79,11 @@ final class PostFileProcessor extends NodeTraverser
}
// B. post rectors
return parent::traverse($nodes);
$nodes = parent::traverse($nodes);
// must run standalone, after NameImporitngPostRector, so it won't skip TraverseNodes()
return $this->useAddingPostRector->traverse($nodes);
}
/**

View File

@ -2,85 +2,60 @@
declare(strict_types=1);
namespace Rector\CodingStyle\Application;
namespace Rector\PostRector\Collector;
use Nette\Utils\Strings;
use PhpParser\Node;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\Namespace_;
use PHPStan\Type\ObjectType;
use Rector\CodingStyle\Imports\UsedImportsResolver;
use Rector\Core\Contract\PhpParser\Node\CommanderInterface;
use Rector\Core\Exception\ShouldNotHappenException;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
use Rector\NodeTypeResolver\FileSystem\CurrentFileInfoProvider;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
use Rector\PHPStan\Type\AliasedObjectType;
use Rector\PHPStan\Type\FullyQualifiedObjectType;
use Rector\PostRector\Contract\Collector\NodeCollectorInterface;
use Symplify\SmartFileSystem\SmartFileInfo;
final class UseAddingCommander implements CommanderInterface
final class UseNodesToAddCollector implements NodeCollectorInterface
{
/**
* @var FullyQualifiedObjectType[][]|AliasedObjectType[][]
* @var CurrentFileInfoProvider
*/
private $useImportTypesInFilePath = [];
/**
* @var FullyQualifiedObjectType[][]
*/
private $functionUseImportTypesInFilePath = [];
private $currentFileInfoProvider;
/**
* @todo use value object
* @var string[][]
*/
private $removedShortUsesInFilePath = [];
/**
* @var UseImportsAdder
* @todo use value object
* @var FullyQualifiedObjectType[][]|AliasedObjectType[][]
*/
private $useImportsAdder;
private $useImportTypesInFilePath = [];
/**
* @todo use value object
* @var FullyQualifiedObjectType[][]
*/
private $functionUseImportTypesInFilePath = [];
/**
* @var UsedImportsResolver
*/
private $usedImportsResolver;
/**
* @var BetterNodeFinder
*/
private $betterNodeFinder;
/**
* @var UseImportsRemover
*/
private $useImportsRemover;
/**
* @var TypeFactory
*/
private $typeFactory;
/**
* @var CurrentFileInfoProvider
*/
private $currentFileInfoProvider;
public function __construct(
UseImportsAdder $useImportsAdder,
UseImportsRemover $useImportsRemover,
UsedImportsResolver $usedImportsResolver,
BetterNodeFinder $betterNodeFinder,
TypeFactory $typeFactory,
CurrentFileInfoProvider $currentFileInfoProvider
CurrentFileInfoProvider $currentFileInfoProvider,
UsedImportsResolver $usedImportsResolver
) {
$this->useImportsAdder = $useImportsAdder;
$this->usedImportsResolver = $usedImportsResolver;
$this->betterNodeFinder = $betterNodeFinder;
$this->useImportsRemover = $useImportsRemover;
$this->typeFactory = $typeFactory;
$this->currentFileInfoProvider = $currentFileInfoProvider;
$this->usedImportsResolver = $usedImportsResolver;
}
public function isActive(): bool
{
return count($this->useImportTypesInFilePath) > 0 || count($this->functionUseImportTypesInFilePath) > 0;
}
/**
@ -101,6 +76,13 @@ final class UseAddingCommander implements CommanderInterface
$this->useImportTypesInFilePath[$fileInfo->getRealPath()][] = $objectType;
}
public function addFunctionUseImport(Node $node, FullyQualifiedObjectType $fullyQualifiedObjectType): void
{
/** @var SmartFileInfo $fileInfo */
$fileInfo = $node->getAttribute(AttributeKey::FILE_INFO);
$this->functionUseImportTypesInFilePath[$fileInfo->getRealPath()][] = $fullyQualifiedObjectType;
}
public function removeShortUse(Node $node, string $shortUse): void
{
/** @var SmartFileInfo|null $fileInfo */
@ -112,64 +94,48 @@ final class UseAddingCommander implements CommanderInterface
$this->removedShortUsesInFilePath[$fileInfo->getRealPath()][] = $shortUse;
}
public function addFunctionUseImport(Node $node, FullyQualifiedObjectType $fullyQualifiedObjectType): void
public function clear(string $filePath): void
{
/** @var SmartFileInfo $fileInfo */
$fileInfo = $node->getAttribute(AttributeKey::FILE_INFO);
$this->functionUseImportTypesInFilePath[$fileInfo->getRealPath()][] = $fullyQualifiedObjectType;
// clear applied imports, so isActive() doesn't return any false positives
unset($this->useImportTypesInFilePath[$filePath], $this->functionUseImportTypesInFilePath[$filePath]);
}
/**
* @param Stmt[] $nodes
* @return Stmt[]
* @return FullyQualifiedObjectType[]|AliasedObjectType[]
*/
public function traverseNodes(array $nodes): array
public function getUseImportTypesByNode(Node $node): array
{
// no nodes → just return
if (count($nodes) === 0) {
return $nodes;
}
$filePath = $this->getRealPathFromNode($node);
$filePath = $this->getRealPathFromNode($nodes[0]);
$useImportTypes = $this->useImportTypesInFilePath[$filePath] ?? [];
$functionUseImportTypes = $this->functionUseImportTypesInFilePath[$filePath] ?? [];
$removedShortUses = $this->removedShortUsesInFilePath[$filePath] ?? [];
// nothing to import or remove
if ($useImportTypes === [] && $functionUseImportTypes === [] && $removedShortUses === []) {
return $nodes;
}
/** @var FullyQualifiedObjectType[] $useImportTypes */
$useImportTypes = $this->typeFactory->uniquateTypes($useImportTypes);
// clear applied imports, so isActive() doesn't return any false positives
unset($this->useImportTypesInFilePath[$filePath], $this->functionUseImportTypesInFilePath[$filePath]);
// A. has namespace? add under it
$namespace = $this->betterNodeFinder->findFirstInstanceOf($nodes, Namespace_::class);
if ($namespace instanceof Namespace_) {
// first clean
$this->useImportsRemover->removeImportsFromNamespace($namespace, $removedShortUses);
// then add, to prevent adding + removing false positive of same short use
$this->useImportsAdder->addImportsToNamespace($namespace, $useImportTypes, $functionUseImportTypes);
return $nodes;
}
// B. no namespace? add in the top
// first clean
$nodes = $this->useImportsRemover->removeImportsFromStmts($nodes, $removedShortUses);
$useImportTypes = $this->filterOutNonNamespacedNames($useImportTypes);
// then add, to prevent adding + removing false positive of same short use
return $this->useImportsAdder->addImportsToStmts($nodes, $useImportTypes, $functionUseImportTypes);
return $this->useImportTypesInFilePath[$filePath] ?? [];
}
public function isActive(): bool
public function analyseFileInfoUseStatements(Node $node): void
{
return count($this->useImportTypesInFilePath) > 0 || count($this->functionUseImportTypesInFilePath) > 0;
$filePath = $this->getRealPathFromNode($node);
// already analysed
if (isset($this->useImportTypesInFilePath[$filePath])) {
return;
}
$usedImportTypes = $this->usedImportsResolver->resolveForNode($node);
foreach ($usedImportTypes as $usedImportType) {
$this->useImportTypesInFilePath[$filePath][] = $usedImportType;
}
}
public function hasImport(Node $node, FullyQualifiedObjectType $fullyQualifiedObjectType): bool
{
$useImports = $this->getUseImportTypesByNode($node);
foreach ($useImports as $useImport) {
if ($useImport->equals($fullyQualifiedObjectType)) {
return true;
}
}
return false;
}
public function isShortImported(Node $node, FullyQualifiedObjectType $fullyQualifiedObjectType): bool
@ -217,63 +183,28 @@ final class UseAddingCommander implements CommanderInterface
return false;
}
public function analyseFileInfoUseStatements(Node $node): void
/**
* @return FullyQualifiedObjectType[]|AliasedObjectType[]
*/
public function getUseImportTypes(string $filePath): array
{
$filePath = $this->getRealPathFromNode($node);
// already analysed
if (isset($this->useImportTypesInFilePath[$filePath])) {
return;
}
$usedImportTypes = $this->usedImportsResolver->resolveForNode($node);
foreach ($usedImportTypes as $usedImportType) {
$this->useImportTypesInFilePath[$filePath][] = $usedImportType;
}
}
public function hasImport(Node $node, FullyQualifiedObjectType $fullyQualifiedObjectType): bool
{
$useImports = $this->getUseImportTypesByNode($node);
foreach ($useImports as $useImport) {
if ($useImport->equals($fullyQualifiedObjectType)) {
return true;
}
}
return false;
return $this->useImportTypesInFilePath[$filePath] ?? [];
}
/**
* This prevents importing:
* - App\Some\Product
*
* if there is already:
* - use App\Another\Product
* @return FullyQualifiedObjectType[]
*/
public function canImportBeAdded(Node $node, FullyQualifiedObjectType $fullyQualifiedObjectType): bool
public function getFunctionUseImportTypesInFilePath(string $filePath): array
{
$useImportTypes = $this->getUseImportTypesByNode($node);
foreach ($useImportTypes as $useImportType) {
if (! $useImportType->equals($fullyQualifiedObjectType) &&
$useImportType->areShortNamesEqual($fullyQualifiedObjectType)
) {
return false;
}
if ($useImportType->equals($fullyQualifiedObjectType)) {
return true;
}
}
return true;
return $this->functionUseImportTypesInFilePath[$filePath] ?? [];
}
public function getPriority(): int
/**
* @return string[]
*/
public function getShortUsesInFilePath(string $filePath): array
{
return 500;
return $this->removedShortUsesInFilePath[$filePath] ?? [];
}
private function getRealPathFromNode(Node $node): ?string
@ -287,26 +218,6 @@ final class UseAddingCommander implements CommanderInterface
return $fileInfo->getRealPath();
}
/**
* Prevents
* @param FullyQualifiedObjectType[] $useImportTypes
* @return FullyQualifiedObjectType[]
*/
private function filterOutNonNamespacedNames(array $useImportTypes): array
{
$namespacedUseImportTypes = [];
foreach ($useImportTypes as $useImportType) {
if (! Strings::contains($useImportType->getClassName(), '\\')) {
continue;
}
$namespacedUseImportTypes[] = $useImportType;
}
return $namespacedUseImportTypes;
}
private function isShortClassImported(string $filePath, string $shortName): bool
{
$fileUseImports = $this->useImportTypesInFilePath[$filePath] ?? [];
@ -319,14 +230,4 @@ final class UseAddingCommander implements CommanderInterface
return false;
}
/**
* @return FullyQualifiedObjectType[]|AliasedObjectType[]
*/
private function getUseImportTypesByNode(Node $node): array
{
$filePath = $this->getRealPathFromNode($node);
return $this->useImportTypesInFilePath[$filePath] ?? [];
}
}

View File

@ -12,14 +12,13 @@ use PhpParser\Node\Stmt\ClassLike;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use Rector\ChangesReporting\Collector\RectorChangeCollector;
use Rector\CodingStyle\Application\NameImportingCommander;
use Rector\CodingStyle\Application\UseAddingCommander;
use Rector\Core\PhpParser\Node\Commander\NodeAddingCommander;
use Rector\Core\PhpParser\Node\Commander\NodeReplacingCommander;
use Rector\Core\PhpParser\Node\Commander\PropertyAddingCommander;
use Rector\PHPStan\Type\AliasedObjectType;
use Rector\PHPStan\Type\FullyQualifiedObjectType;
use Rector\PostRector\Collector\NodesToRemoveCollector;
use Rector\PostRector\Collector\UseNodesToAddCollector;
/**
* This could be part of @see AbstractRector, but decopuling to trait
@ -28,14 +27,9 @@ use Rector\PostRector\Collector\NodesToRemoveCollector;
trait NodeCommandersTrait
{
/**
* @var NameImportingCommander
* @var UseNodesToAddCollector
*/
protected $nameImportingCommander;
/**
* @var UseAddingCommander
*/
protected $useAddingCommander;
protected $useNodesToAddCollector;
/**
* @var NodesToRemoveCollector
@ -69,16 +63,14 @@ trait NodeCommandersTrait
NodesToRemoveCollector $nodesToRemoveCollector,
NodeAddingCommander $nodeAddingCommander,
PropertyAddingCommander $propertyAddingCommander,
UseAddingCommander $useAddingCommander,
NameImportingCommander $nameImportingCommander,
UseNodesToAddCollector $useNodesToAddCollector,
NodeReplacingCommander $nodeReplacingCommander,
RectorChangeCollector $rectorChangeCollector
): void {
$this->nodesToRemoveCollector = $nodesToRemoveCollector;
$this->nodeAddingCommander = $nodeAddingCommander;
$this->propertyAddingCommander = $propertyAddingCommander;
$this->useAddingCommander = $useAddingCommander;
$this->nameImportingCommander = $nameImportingCommander;
$this->useNodesToAddCollector = $useNodesToAddCollector;
$this->nodeReplacingCommander = $nodeReplacingCommander;
$this->rectorChangeCollector = $rectorChangeCollector;
}
@ -90,7 +82,7 @@ trait NodeCommandersTrait
{
assert($objectType instanceof FullyQualifiedObjectType || $objectType instanceof AliasedObjectType);
$this->useAddingCommander->addUseImport($positionNode, $objectType);
$this->useNodesToAddCollector->addUseImport($positionNode, $objectType);
}
protected function addNodeAfterNode(Node $newNode, Node $positionNode): void

View File

@ -0,0 +1,88 @@
<?php
declare(strict_types=1);
namespace Rector\PostRector\Rector;
use PhpParser\Node;
use PhpParser\Node\Name;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\CodingStyle\Node\NameImporter;
use Rector\Core\Configuration\Option;
use Rector\Core\RectorDefinition\RectorDefinition;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockNameImporter;
use Symplify\PackageBuilder\Parameter\ParameterProvider;
final class NameImportingRector extends AbstractPostRector
{
/**
* @var ParameterProvider
*/
private $parameterProvider;
/**
* @var NameImporter
*/
private $nameImporter;
/**
* @var bool
*/
private $importDocBlocks = false;
/**
* @var DocBlockNameImporter
*/
private $docBlockNameImporter;
public function __construct(
ParameterProvider $parameterProvider,
NameImporter $nameImporter,
DocBlockNameImporter $docBlockNameImporter,
bool $importDocBlocks
) {
$this->parameterProvider = $parameterProvider;
$this->nameImporter = $nameImporter;
$this->importDocBlocks = $importDocBlocks;
$this->docBlockNameImporter = $docBlockNameImporter;
}
public function refactor(Node $node): ?Node
{
if (! $this->parameterProvider->provideParameter(Option::AUTO_IMPORT_NAMES)) {
return null;
}
if ($node instanceof Name) {
return $this->nameImporter->importName($node);
}
if (! $this->importDocBlocks) {
return null;
}
/** @var PhpDocInfo|null $phpDocInfo */
$phpDocInfo = $node->getAttribute(AttributeKey::PHP_DOC_INFO);
if ($phpDocInfo === null) {
return null;
}
$hasChanged = $this->docBlockNameImporter->importNames($phpDocInfo, $node);
if (! $hasChanged) {
return null;
}
return $node;
}
public function getPriority(): int
{
return 600;
}
public function getDefinition(): RectorDefinition
{
return new RectorDefinition('Imports names');
}
}

View File

@ -0,0 +1,187 @@
<?php
declare(strict_types=1);
namespace Rector\PostRector\Rector;
use Nette\Utils\Strings;
use PhpParser\Node;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\Namespace_;
use PhpParser\NodeVisitorAbstract;
use Rector\CodingStyle\Application\UseImportsAdder;
use Rector\CodingStyle\Application\UseImportsRemover;
use Rector\Core\Contract\Rector\RectorInterface;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
use Rector\Core\RectorDefinition\RectorDefinition;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\PHPStan\Type\TypeFactory;
use Rector\PHPStan\Type\FullyQualifiedObjectType;
use Rector\PostRector\Collector\UseNodesToAddCollector;
use Symplify\SmartFileSystem\SmartFileInfo;
final class UseAddingPostRector extends NodeVisitorAbstract implements RectorInterface
{
/**
* @var UseImportsAdder
*/
private $useImportsAdder;
/**
* @var BetterNodeFinder
*/
private $betterNodeFinder;
/**
* @var UseImportsRemover
*/
private $useImportsRemover;
/**
* @var TypeFactory
*/
private $typeFactory;
/**
* @var UseNodesToAddCollector
*/
private $useNodesToAddCollector;
public function __construct(
UseImportsAdder $useImportsAdder,
UseImportsRemover $useImportsRemover,
BetterNodeFinder $betterNodeFinder,
TypeFactory $typeFactory,
UseNodesToAddCollector $useNodesToAddCollector
) {
$this->useImportsAdder = $useImportsAdder;
$this->betterNodeFinder = $betterNodeFinder;
$this->useImportsRemover = $useImportsRemover;
$this->typeFactory = $typeFactory;
$this->useNodesToAddCollector = $useNodesToAddCollector;
}
/**
* @param Stmt[] $nodes
* @return Stmt[]
*/
public function traverse(array $nodes): array
{
// no nodes → just return
if (count($nodes) === 0) {
return $nodes;
}
$filePath = $this->getRealPathFromNode($nodes[0]);
if ($filePath === null) {
return $nodes;
}
$useImportTypes = $this->useNodesToAddCollector->getUseImportTypes($filePath);
$functionUseImportTypes = $this->useNodesToAddCollector->getFunctionUseImportTypesInFilePath($filePath);
$removedShortUses = $this->useNodesToAddCollector->getShortUsesInFilePath($filePath);
// nothing to import or remove
if ($useImportTypes === [] && $functionUseImportTypes === [] && $removedShortUses === []) {
return $nodes;
}
/** @var FullyQualifiedObjectType[] $useImportTypes */
$useImportTypes = $this->typeFactory->uniquateTypes($useImportTypes);
$this->useNodesToAddCollector->clear($filePath);
// A. has namespace? add under it
$namespace = $this->betterNodeFinder->findFirstInstanceOf($nodes, Namespace_::class);
if ($namespace instanceof Namespace_) {
// first clean
$this->useImportsRemover->removeImportsFromNamespace($namespace, $removedShortUses);
// then add, to prevent adding + removing false positive of same short use
$this->useImportsAdder->addImportsToNamespace($namespace, $useImportTypes, $functionUseImportTypes);
return $nodes;
}
// B. no namespace? add in the top
// first clean
$nodes = $this->useImportsRemover->removeImportsFromStmts($nodes, $removedShortUses);
$useImportTypes = $this->filterOutNonNamespacedNames($useImportTypes);
// then add, to prevent adding + removing false positive of same short use
return $this->useImportsAdder->addImportsToStmts($nodes, $useImportTypes, $functionUseImportTypes);
}
/**
* This prevents importing:
* - App\Some\Product
*
* if there is already:
* - use App\Another\Product
*/
public function canImportBeAdded(Node $node, FullyQualifiedObjectType $fullyQualifiedObjectType): bool
{
$useImportTypes = $this->useNodesToAddCollector->getUseImportTypesByNode($node);
foreach ($useImportTypes as $useImportType) {
if (! $useImportType->equals($fullyQualifiedObjectType) &&
$useImportType->areShortNamesEqual($fullyQualifiedObjectType)
) {
return false;
}
if ($useImportType->equals($fullyQualifiedObjectType)) {
return true;
}
}
return true;
}
public function getPriority(): int
{
return 500;
}
public function getDefinition(): RectorDefinition
{
return new RectorDefinition('Post Rector that adds use statements');
}
public function refactor(Node $node): ?Node
{
// note: traverse() is used instead
return null;
}
/**
* Prevents
* @param FullyQualifiedObjectType[] $useImportTypes
* @return FullyQualifiedObjectType[]
*/
private function filterOutNonNamespacedNames(array $useImportTypes): array
{
$namespacedUseImportTypes = [];
foreach ($useImportTypes as $useImportType) {
if (! Strings::contains($useImportType->getClassName(), '\\')) {
continue;
}
$namespacedUseImportTypes[] = $useImportType;
}
return $namespacedUseImportTypes;
}
private function getRealPathFromNode(Node $node): ?string
{
/** @var SmartFileInfo|null $fileInfo */
$fileInfo = $node->getAttribute(AttributeKey::FILE_INFO);
if ($fileInfo === null) {
return null;
}
return $fileInfo->getRealPath();
}
}

View File

@ -1,100 +0,0 @@
<?php
declare(strict_types=1);
namespace Rector\CodingStyle\Application;
use PhpParser\Node;
use PhpParser\Node\Name;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\CodingStyle\Node\NameImporter;
use Rector\Core\Configuration\Option;
use Rector\Core\Contract\PhpParser\Node\CommanderInterface;
use Rector\Core\PhpParser\NodeTraverser\CallableNodeTraverser;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\NodeTypeResolver\PhpDoc\NodeAnalyzer\DocBlockNameImporter;
use Symplify\PackageBuilder\Parameter\ParameterProvider;
final class NameImportingCommander implements CommanderInterface
{
/**
* @var ParameterProvider
*/
private $parameterProvider;
/**
* @var NameImporter
*/
private $nameImporter;
/**
* @var CallableNodeTraverser
*/
private $callableNodeTraverser;
/**
* @var bool
*/
private $importDocBlocks = false;
/**
* @var DocBlockNameImporter
*/
private $docBlockNameImporter;
public function __construct(
ParameterProvider $parameterProvider,
NameImporter $nameImporter,
CallableNodeTraverser $callableNodeTraverser,
DocBlockNameImporter $docBlockNameImporter,
bool $importDocBlocks
) {
$this->parameterProvider = $parameterProvider;
$this->nameImporter = $nameImporter;
$this->callableNodeTraverser = $callableNodeTraverser;
$this->importDocBlocks = $importDocBlocks;
$this->docBlockNameImporter = $docBlockNameImporter;
}
public function isActive(): bool
{
return (bool) $this->parameterProvider->provideParameter(Option::AUTO_IMPORT_NAMES);
}
/**
* @param Node[] $nodes
* @return Node[]
*/
public function traverseNodes(array $nodes): array
{
$this->callableNodeTraverser->traverseNodesWithCallable($nodes, function (Node $node): ?Node {
if ($node instanceof Name) {
return $this->nameImporter->importName($node);
}
if ($this->importDocBlocks) {
/** @var PhpDocInfo|null $phpDocInfo */
$phpDocInfo = $node->getAttribute(AttributeKey::PHP_DOC_INFO);
if ($phpDocInfo === null) {
return null;
}
$hasChanged = $this->docBlockNameImporter->importNames($phpDocInfo, $node);
if (! $hasChanged) {
return null;
}
return $node;
}
return null;
});
return $nodes;
}
public function getPriority(): int
{
return 600;
}
}

View File

@ -6,15 +6,15 @@ namespace Rector\CodingStyle\Imports;
use Nette\Utils\Strings;
use PhpParser\Node;
use Rector\CodingStyle\Application\UseAddingCommander;
use Rector\PHPStan\Type\FullyQualifiedObjectType;
use Rector\PostRector\Rector\UseAddingPostRector;
final class ImportSkipper
{
/**
* @var UseAddingCommander
* @var UseAddingPostRector
*/
private $useAddingCommander;
private $useAddingPostRector;
/**
* @var AliasUsesResolver
@ -27,11 +27,11 @@ final class ImportSkipper
private $shortNameResolver;
public function __construct(
UseAddingCommander $useAddingCommander,
UseAddingPostRector $useAddingPostRector,
AliasUsesResolver $aliasUsesResolver,
ShortNameResolver $shortNameResolver
) {
$this->useAddingCommander = $useAddingCommander;
$this->useAddingPostRector = $useAddingPostRector;
$this->aliasUsesResolver = $aliasUsesResolver;
$this->shortNameResolver = $shortNameResolver;
}
@ -52,7 +52,7 @@ final class ImportSkipper
return true;
}
return ! $this->useAddingCommander->canImportBeAdded($node, $fullyQualifiedObjectType);
return ! $this->useAddingPostRector->canImportBeAdded($node, $fullyQualifiedObjectType);
}
private function isShortNameAlreadyUsedForDifferentFullyQualifiedName(

View File

@ -10,7 +10,6 @@ use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\Namespace_;
use PhpParser\Node\Stmt\UseUse;
use Rector\CodingStyle\Application\UseAddingCommander;
use Rector\CodingStyle\Imports\AliasUsesResolver;
use Rector\CodingStyle\Imports\ImportSkipper;
use Rector\Core\Configuration\Option;
@ -18,6 +17,7 @@ use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\ClassExistenceStaticHelper;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\PHPStan\Type\FullyQualifiedObjectType;
use Rector\PostRector\Collector\UseNodesToAddCollector;
use Rector\StaticTypeMapper\StaticTypeMapper;
use Symplify\PackageBuilder\Parameter\ParameterProvider;
@ -38,11 +38,6 @@ final class NameImporter
*/
private $aliasUsesResolver;
/**
* @var UseAddingCommander
*/
private $useAddingCommander;
/**
* @var ImportSkipper
*/
@ -58,20 +53,25 @@ final class NameImporter
*/
private $parameterProvider;
/**
* @var UseNodesToAddCollector
*/
private $useNodesToAddCollector;
public function __construct(
StaticTypeMapper $staticTypeMapper,
AliasUsesResolver $aliasUsesResolver,
UseAddingCommander $useAddingCommander,
ImportSkipper $importSkipper,
NodeNameResolver $nodeNameResolver,
ParameterProvider $parameterProvider
ParameterProvider $parameterProvider,
UseNodesToAddCollector $useNodesToAddCollector
) {
$this->staticTypeMapper = $staticTypeMapper;
$this->aliasUsesResolver = $aliasUsesResolver;
$this->useAddingCommander = $useAddingCommander;
$this->importSkipper = $importSkipper;
$this->nodeNameResolver = $nodeNameResolver;
$this->parameterProvider = $parameterProvider;
$this->useNodesToAddCollector = $useNodesToAddCollector;
}
public function importName(Name $name): ?Name
@ -134,8 +134,8 @@ final class NameImporter
return null;
}
if ($this->useAddingCommander->isShortImported($name, $fullyQualifiedObjectType)) {
if ($this->useAddingCommander->isImportShortable($name, $fullyQualifiedObjectType)) {
if ($this->useNodesToAddCollector->isShortImported($name, $fullyQualifiedObjectType)) {
if ($this->useNodesToAddCollector->isImportShortable($name, $fullyQualifiedObjectType)) {
return $fullyQualifiedObjectType->getShortNameNode();
}
@ -181,15 +181,15 @@ final class NameImporter
private function addUseImport(Name $name, FullyQualifiedObjectType $fullyQualifiedObjectType): void
{
if ($this->useAddingCommander->hasImport($name, $fullyQualifiedObjectType)) {
if ($this->useNodesToAddCollector->hasImport($name, $fullyQualifiedObjectType)) {
return;
}
$parentNode = $name->getAttribute(AttributeKey::PARENT_NODE);
if ($parentNode instanceof FuncCall) {
$this->useAddingCommander->addFunctionUseImport($name, $fullyQualifiedObjectType);
$this->useNodesToAddCollector->addFunctionUseImport($name, $fullyQualifiedObjectType);
} else {
$this->useAddingCommander->addUseImport($name, $fullyQualifiedObjectType);
$this->useNodesToAddCollector->addUseImport($name, $fullyQualifiedObjectType);
}
}

View File

@ -7,7 +7,6 @@ namespace Rector\CodingStyle\Rector\Namespace_;
use PhpParser\Node;
use PhpParser\Node\Name;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\CodingStyle\Application\NameImportingCommander;
use Rector\CodingStyle\Node\NameImporter;
use Rector\Core\Rector\AbstractRector;
use Rector\Core\RectorDefinition\CodeSample;
@ -99,12 +98,12 @@ PHP
{
// this file remains just for testing
// breaks other rectors by making name nodes short, FQN → short names
/** prevents duplicated run with @see NameImportingCommander */
/** prevents duplicated run with @see \Rector\PostRector\Application\\Rector\PostRector\Rector\NameImportingRector */
if (! PHPUnitEnvironment::isPHPUnitRun()) {
return null;
}
$this->useAddingCommander->analyseFileInfoUseStatements($node);
$this->useNodesToAddCollector->analyseFileInfoUseStatements($node);
if ($node instanceof Name) {
return $this->nameImporter->importName($node);

View File

@ -9,10 +9,10 @@ use PhpParser\Node\Stmt\ClassMethod;
use PHPStan\Type\ObjectType;
use Rector\BetterPhpDocParser\PhpDocInfo\PhpDocInfo;
use Rector\BetterPhpDocParser\PhpDocNode\Symfony\SymfonyRouteTagValueNode;
use Rector\CodingStyle\Application\UseAddingCommander;
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\PHPStan\Type\AliasedObjectType;
use Rector\PHPStan\Type\FullyQualifiedObjectType;
use Rector\PostRector\Collector\UseNodesToAddCollector;
final class ImplicitToExplicitRoutingAnnotationDecorator
{
@ -22,13 +22,13 @@ final class ImplicitToExplicitRoutingAnnotationDecorator
public const HAS_ROUTE_ANNOTATION = 'has_route_annotation';
/**
* @var UseAddingCommander
* @var UseNodesToAddCollector
*/
private $useAddingCommander;
private $useNodesToAddCollector;
public function __construct(UseAddingCommander $useAddingCommander)
public function __construct(UseNodesToAddCollector $useNodesToAddCollector)
{
$this->useAddingCommander = $useAddingCommander;
$this->useNodesToAddCollector = $useNodesToAddCollector;
}
public function decorateClassMethodWithRouteAnnotation(
@ -43,7 +43,7 @@ final class ImplicitToExplicitRoutingAnnotationDecorator
$this->addUseType($symfonyRouteUseObjectType, $classMethod);
// remove
$this->useAddingCommander->removeShortUse($classMethod, 'Route');
$this->useNodesToAddCollector->removeShortUse($classMethod, 'Route');
$classMethod->setAttribute(self::HAS_ROUTE_ANNOTATION, true);
}
@ -55,6 +55,6 @@ final class ImplicitToExplicitRoutingAnnotationDecorator
{
assert($objectType instanceof FullyQualifiedObjectType || $objectType instanceof AliasedObjectType);
$this->useAddingCommander->addUseImport($positionNode, $objectType);
$this->useNodesToAddCollector->addUseImport($positionNode, $objectType);
}
}

View File

@ -12,7 +12,7 @@ use Rector\Renaming\Tests\Rector\Class_\RenameClassRector\Source\NewClass;
use Rector\Renaming\Tests\Rector\Class_\RenameClassRector\Source\OldClass;
/**
* @see \Rector\CodingStyle\Application\NameImportingCommander
* @see \Rector\PostRector\Rector\NameImportingRector
*/
final class AutoImportNamesParameterTest extends AbstractRectorTestCase
{

View File

@ -11,7 +11,7 @@ use Rector\Renaming\Tests\Rector\Class_\RenameClassRector\Source\NewClass;
use Rector\Renaming\Tests\Rector\Class_\RenameClassRector\Source\OldClass;
/**
* @see \Rector\CodingStyle\Application\NameImportingCommander
* @see \Rector\PostRector\Rector\NameImportingRector
*/
final class FunctionAutoImportNamesParameterTest extends AbstractRectorTestCase
{