mirror of
https://github.com/rectorphp/rector.git
synced 2025-02-20 16:15:31 +01:00
* remove doctrine/annotations * Refactor doctrine/annotation parser to static reflection with phpdoc-parser * remove doctirne-annotation-parser-syncer * remove annotation stubs * use nodes * almost there * [ci-review] Rector Rectify * skip temporary * phpstan: remove fixed messages Co-authored-by: kaizen-ci <info@kaizen-ci.org>
225 lines
6.5 KiB
PHP
225 lines
6.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Rector\CodingStyle\Node;
|
|
|
|
use Nette\Utils\Strings;
|
|
use PhpParser\Node;
|
|
use PhpParser\Node\Expr\ConstFetch;
|
|
use PhpParser\Node\Expr\FuncCall;
|
|
use PhpParser\Node\Name;
|
|
use PhpParser\Node\Stmt\Namespace_;
|
|
use PhpParser\Node\Stmt\UseUse;
|
|
use PHPStan\Reflection\ReflectionProvider;
|
|
use Rector\CodingStyle\ClassNameImport\AliasUsesResolver;
|
|
use Rector\CodingStyle\ClassNameImport\ClassNameImportSkipper;
|
|
use Rector\Core\Configuration\Option;
|
|
use Rector\NodeNameResolver\NodeNameResolver;
|
|
use Rector\NodeTypeResolver\Node\AttributeKey;
|
|
use Rector\PostRector\Collector\UseNodesToAddCollector;
|
|
use Rector\StaticTypeMapper\StaticTypeMapper;
|
|
use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType;
|
|
use Symplify\PackageBuilder\Parameter\ParameterProvider;
|
|
|
|
final class NameImporter
|
|
{
|
|
/**
|
|
* @var string[]
|
|
*/
|
|
private $aliasedUses = [];
|
|
|
|
/**
|
|
* @var StaticTypeMapper
|
|
*/
|
|
private $staticTypeMapper;
|
|
|
|
/**
|
|
* @var AliasUsesResolver
|
|
*/
|
|
private $aliasUsesResolver;
|
|
|
|
/**
|
|
* @var ClassNameImportSkipper
|
|
*/
|
|
private $classNameImportSkipper;
|
|
|
|
/**
|
|
* @var NodeNameResolver
|
|
*/
|
|
private $nodeNameResolver;
|
|
|
|
/**
|
|
* @var ParameterProvider
|
|
*/
|
|
private $parameterProvider;
|
|
|
|
/**
|
|
* @var UseNodesToAddCollector
|
|
*/
|
|
private $useNodesToAddCollector;
|
|
|
|
/**
|
|
* @var ReflectionProvider
|
|
*/
|
|
private $reflectionProvider;
|
|
|
|
public function __construct(
|
|
AliasUsesResolver $aliasUsesResolver,
|
|
ClassNameImportSkipper $classNameImportSkipper,
|
|
NodeNameResolver $nodeNameResolver,
|
|
ParameterProvider $parameterProvider,
|
|
StaticTypeMapper $staticTypeMapper,
|
|
UseNodesToAddCollector $useNodesToAddCollector,
|
|
ReflectionProvider $reflectionProvider
|
|
) {
|
|
$this->staticTypeMapper = $staticTypeMapper;
|
|
$this->aliasUsesResolver = $aliasUsesResolver;
|
|
$this->classNameImportSkipper = $classNameImportSkipper;
|
|
$this->nodeNameResolver = $nodeNameResolver;
|
|
$this->parameterProvider = $parameterProvider;
|
|
$this->useNodesToAddCollector = $useNodesToAddCollector;
|
|
$this->reflectionProvider = $reflectionProvider;
|
|
}
|
|
|
|
public function importName(Name $name): ?Name
|
|
{
|
|
if ($this->shouldSkipName($name)) {
|
|
return null;
|
|
}
|
|
|
|
if ($this->classNameImportSkipper->isShortNameInUseStatement($name)) {
|
|
return null;
|
|
}
|
|
|
|
$staticType = $this->staticTypeMapper->mapPhpParserNodePHPStanType($name);
|
|
if (! $staticType instanceof FullyQualifiedObjectType) {
|
|
return null;
|
|
}
|
|
|
|
$this->aliasedUses = $this->aliasUsesResolver->resolveForNode($name);
|
|
|
|
return $this->importNameAndCollectNewUseStatement($name, $staticType);
|
|
}
|
|
|
|
private function shouldSkipName(Name $name): bool
|
|
{
|
|
$virtualNode = $name->getAttribute(AttributeKey::VIRTUAL_NODE);
|
|
if ($virtualNode) {
|
|
return true;
|
|
}
|
|
|
|
// is scalar name?
|
|
if (in_array($name->toLowerString(), ['true', 'false', 'bool'], true)) {
|
|
return true;
|
|
}
|
|
|
|
// namespace <name>
|
|
// use <name>;
|
|
if ($this->isNamespaceOrUseImportName($name)) {
|
|
return true;
|
|
}
|
|
|
|
if ($this->isFunctionOrConstantImportWithSingleName($name)) {
|
|
return true;
|
|
}
|
|
|
|
// Importing root namespace classes (like \DateTime) is optional
|
|
$importShortClasses = $this->parameterProvider->provideParameter(Option::IMPORT_SHORT_CLASSES);
|
|
if (! $importShortClasses) {
|
|
$name = $this->nodeNameResolver->getName($name);
|
|
if ($name !== null && substr_count($name, '\\') === 0) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private function importNameAndCollectNewUseStatement(
|
|
Name $name,
|
|
FullyQualifiedObjectType $fullyQualifiedObjectType
|
|
): ?Name {
|
|
// the same end is already imported → skip
|
|
if ($this->classNameImportSkipper->shouldSkipNameForFullyQualifiedObjectType(
|
|
$name,
|
|
$fullyQualifiedObjectType
|
|
)) {
|
|
return null;
|
|
}
|
|
|
|
if ($this->useNodesToAddCollector->isShortImported($name, $fullyQualifiedObjectType)) {
|
|
if ($this->useNodesToAddCollector->isImportShortable($name, $fullyQualifiedObjectType)) {
|
|
return $fullyQualifiedObjectType->getShortNameNode();
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
$this->addUseImport($name, $fullyQualifiedObjectType);
|
|
|
|
// possibly aliased
|
|
foreach ($this->aliasedUses as $aliasedUse) {
|
|
if ($fullyQualifiedObjectType->getClassName() === $aliasedUse) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
return $fullyQualifiedObjectType->getShortNameNode();
|
|
}
|
|
|
|
/**
|
|
* Skip:
|
|
* - namespace name
|
|
* - use import name
|
|
*/
|
|
private function isNamespaceOrUseImportName(Name $name): bool
|
|
{
|
|
$parentNode = $name->getAttribute(AttributeKey::PARENT_NODE);
|
|
if ($parentNode instanceof Namespace_) {
|
|
return true;
|
|
}
|
|
|
|
return $parentNode instanceof UseUse;
|
|
}
|
|
|
|
private function isFunctionOrConstantImportWithSingleName(Name $name): bool
|
|
{
|
|
$parentNode = $name->getAttribute(AttributeKey::PARENT_NODE);
|
|
|
|
$fullName = $name->toString();
|
|
|
|
$autoImportNames = $this->parameterProvider->provideParameter(Option::AUTO_IMPORT_NAMES);
|
|
if ($autoImportNames && ! $parentNode instanceof Node && ! Strings::contains(
|
|
$fullName,
|
|
'\\'
|
|
) && $this->reflectionProvider->hasFunction(new Name($fullName), null)) {
|
|
return true;
|
|
}
|
|
|
|
if ($parentNode instanceof ConstFetch) {
|
|
return count($name->parts) === 1;
|
|
}
|
|
|
|
if ($parentNode instanceof FuncCall) {
|
|
return count($name->parts) === 1;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|