rector/rules/coding-style/src/Node/NameImporter.php

228 lines
6.7 KiB
PHP
Raw Normal View History

2019-10-13 07:59:52 +02:00
<?php
declare(strict_types=1);
2019-09-28 14:08:08 +02:00
namespace Rector\CodingStyle\Node;
2019-10-03 08:53:23 +02:00
use PhpParser\Node\Expr\ConstFetch;
2019-09-28 14:08:08 +02:00
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
2019-09-28 14:08:08 +02:00
use PhpParser\Node\Stmt\Namespace_;
use PhpParser\Node\Stmt\UseUse;
use Rector\CodingStyle\Imports\AliasUsesResolver;
use Rector\CodingStyle\Imports\ImportSkipper;
use Rector\Core\Configuration\Option;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\NodeTypeResolver\ClassExistenceStaticHelper;
2019-09-28 14:08:08 +02:00
use Rector\NodeTypeResolver\Node\AttributeKey;
use Rector\PHPStan\Type\FullyQualifiedObjectType;
2020-03-31 19:32:54 +02:00
use Rector\PostRector\Collector\UseNodesToAddCollector;
2020-06-19 11:11:25 +02:00
use Rector\PSR4\Collector\RenamedClassesCollector;
use Rector\StaticTypeMapper\StaticTypeMapper;
use Symplify\PackageBuilder\Parameter\ParameterProvider;
2019-09-28 14:08:08 +02:00
final class NameImporter
{
2020-02-01 17:04:38 +01:00
/**
* @var string[]
*/
private $aliasedUses = [];
2019-09-28 14:08:08 +02:00
/**
* @var StaticTypeMapper
*/
private $staticTypeMapper;
/**
* @var AliasUsesResolver
*/
private $aliasUsesResolver;
/**
* @var ImportSkipper
*/
private $importSkipper;
/**
* @var NodeNameResolver
*/
private $nodeNameResolver;
/**
* @var ParameterProvider
*/
private $parameterProvider;
2020-03-31 19:32:54 +02:00
/**
* @var UseNodesToAddCollector
*/
private $useNodesToAddCollector;
2020-06-19 11:11:25 +02:00
/**
* @var RenamedClassesCollector
*/
private $renamedClassesCollector;
2019-09-28 14:08:08 +02:00
public function __construct(
AliasUsesResolver $aliasUsesResolver,
ImportSkipper $importSkipper,
NodeNameResolver $nodeNameResolver,
2020-03-31 19:32:54 +02:00
ParameterProvider $parameterProvider,
2020-07-26 09:49:22 +02:00
RenamedClassesCollector $renamedClassesCollector,
StaticTypeMapper $staticTypeMapper,
UseNodesToAddCollector $useNodesToAddCollector
2019-09-28 14:08:08 +02:00
) {
$this->staticTypeMapper = $staticTypeMapper;
$this->aliasUsesResolver = $aliasUsesResolver;
$this->importSkipper = $importSkipper;
$this->nodeNameResolver = $nodeNameResolver;
$this->parameterProvider = $parameterProvider;
2020-03-31 19:32:54 +02:00
$this->useNodesToAddCollector = $useNodesToAddCollector;
2020-06-19 11:11:25 +02:00
$this->renamedClassesCollector = $renamedClassesCollector;
2019-09-28 14:08:08 +02:00
}
public function importName(Name $name): ?Name
{
2019-11-08 22:29:15 +01:00
if ($this->shouldSkipName($name)) {
return null;
}
2019-09-28 14:08:08 +02:00
$staticType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($name);
if (! $staticType instanceof FullyQualifiedObjectType) {
return null;
}
$this->aliasedUses = $this->aliasUsesResolver->resolveForNode($name);
return $this->importNameAndCollectNewUseStatement($name, $staticType);
}
2020-02-01 17:04:38 +01:00
private function shouldSkipName(Name $name): bool
2019-09-28 14:08:08 +02:00
{
if ($name->getAttribute(AttributeKey::VIRTUAL_NODE)) {
2019-09-28 14:08:08 +02:00
return true;
}
2020-02-01 17:04:38 +01:00
// is scalar name?
if (in_array($name->toLowerString(), ['true', 'false', 'bool'], true)) {
2020-02-01 17:04:38 +01:00
return true;
}
2019-09-28 14:08:08 +02:00
// namespace <name>
// use <name>;
2020-02-01 17:04:38 +01:00
if ($this->isNamespaceOrUseImportName($name)) {
return true;
}
2020-02-01 17:04:38 +01:00
if ($this->isFunctionOrConstantImportWithSingleName($name)) {
return true;
}
2020-03-27 16:42:58 +01:00
if ($this->isNonExistingClassLikeAndFunctionFullyQualifiedName($name)) {
return true;
}
2020-02-01 17:04:38 +01:00
// Importing root namespace classes (like \DateTime) is optional
$importShortClasses = $this->parameterProvider->provideParameter(Option::IMPORT_SHORT_CLASSES);
2020-02-01 17:04:38 +01:00
if (! $importShortClasses) {
$name = $this->nodeNameResolver->getName($name);
2020-02-01 17:04:38 +01:00
if ($name !== null && substr_count($name, '\\') === 0) {
return true;
}
}
return false;
}
2019-09-28 14:08:08 +02:00
private function importNameAndCollectNewUseStatement(
Name $name,
FullyQualifiedObjectType $fullyQualifiedObjectType
): ?Name {
// the same end is already imported → skip
2019-11-08 14:38:30 +01:00
if ($this->importSkipper->shouldSkipNameForFullyQualifiedObjectType($name, $fullyQualifiedObjectType)) {
2019-09-28 14:08:08 +02:00
return null;
}
2020-03-31 19:32:54 +02:00
if ($this->useNodesToAddCollector->isShortImported($name, $fullyQualifiedObjectType)) {
if ($this->useNodesToAddCollector->isImportShortable($name, $fullyQualifiedObjectType)) {
2019-09-28 14:08:08 +02:00
return $fullyQualifiedObjectType->getShortNameNode();
}
return null;
}
$this->addUseImport($name, $fullyQualifiedObjectType);
// possibly aliased
foreach ($this->aliasedUses as $aliasUse) {
if ($fullyQualifiedObjectType->getClassName() === $aliasUse) {
return null;
}
}
return $fullyQualifiedObjectType->getShortNameNode();
}
2020-02-01 17:04:38 +01:00
/**
* Skip:
* - namespace name
* - use import name
*/
private function isNamespaceOrUseImportName(Name $name): bool
2019-09-28 14:08:08 +02:00
{
$parentNode = $name->getAttribute(AttributeKey::PARENT_NODE);
2020-02-01 17:04:38 +01:00
if ($parentNode instanceof Namespace_) {
return true;
2019-09-28 14:08:08 +02:00
}
2020-02-01 17:04:38 +01:00
return $parentNode instanceof UseUse;
2019-09-28 14:08:08 +02:00
}
2019-11-08 14:38:30 +01:00
2020-02-01 17:04:38 +01:00
private function isFunctionOrConstantImportWithSingleName(Name $name): bool
2019-11-08 14:38:30 +01:00
{
2020-02-01 17:04:38 +01:00
$parentNode = $name->getAttribute(AttributeKey::PARENT_NODE);
if (! $parentNode instanceof ConstFetch && ! $parentNode instanceof FuncCall) {
return false;
}
2020-02-01 17:04:38 +01:00
return count($name->parts) === 1;
}
2019-11-08 14:38:30 +01:00
private function isNonExistingClassLikeAndFunctionFullyQualifiedName(Name $name): bool
{
if (! $name instanceof FullyQualified) {
return false;
}
2020-06-19 11:11:25 +02:00
// can be also in to be renamed classes
$classOrFunctionName = $name->toString();
2020-07-19 20:52:42 +02:00
$oldToNewClasses = $this->renamedClassesCollector->getOldToNewClasses();
if (in_array($classOrFunctionName, $oldToNewClasses, true)) {
2020-06-19 11:11:25 +02:00
return false;
}
// skip-non existing class-likes and functions
2020-06-19 11:11:25 +02:00
if (ClassExistenceStaticHelper::doesClassLikeExist($classOrFunctionName)) {
return false;
}
2020-06-19 11:11:25 +02:00
return ! function_exists($classOrFunctionName);
}
private function addUseImport(Name $name, FullyQualifiedObjectType $fullyQualifiedObjectType): void
{
if ($this->useNodesToAddCollector->hasImport($name, $fullyQualifiedObjectType)) {
return;
}
$parentNode = $name->getAttribute(AttributeKey::PARENT_NODE);
if ($parentNode instanceof FuncCall) {
$this->useNodesToAddCollector->addFunctionUseImport($name, $fullyQualifiedObjectType);
} else {
$this->useNodesToAddCollector->addUseImport($name, $fullyQualifiedObjectType);
}
}
2019-09-28 14:08:08 +02:00
}