[Experiment][Naming] Keep GroupUse Stmt on UseImportsResolver::resolveForNode() (#2349)

Co-authored-by: GitHub Action <action@github.com>
This commit is contained in:
Abdul Malik Ikhsan 2022-05-23 03:57:59 +07:00 committed by GitHub
parent f8c204adba
commit 3fe07aa6a1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 109 additions and 93 deletions

View File

@ -6,6 +6,7 @@ namespace Rector\BetterPhpDocParser\PhpDocParser;
use PhpParser\Node;
use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt\GroupUse;
use PhpParser\Node\Stmt\Use_;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\ReflectionProvider;
@ -48,7 +49,7 @@ final class ClassAnnotationMatcher
}
/**
* @param Use_[] $uses
* @param Use_[]|GroupUse[] $uses
*/
private function resolveFullyQualifiedClass(array $uses, Node $node, string $tag): string
{
@ -72,29 +73,25 @@ final class ClassAnnotationMatcher
}
/**
* @param Use_[] $uses
* @param Use_[]|GroupUse[] $uses
*/
private function resolveAsAliased(array $uses, string $tag): string
{
foreach ($uses as $use) {
$prefix = $use instanceof GroupUse
? $use->prefix . '\\'
: '';
$useUses = $use->uses;
// skip group uses or empty
if (count($useUses) !== 1) {
continue;
}
// uses already removed
if (! isset($useUses[0])) {
continue;
}
foreach ($useUses as $useUse) {
if (! $useUse->alias instanceof Identifier) {
continue;
}
if (! $useUses[0]->alias instanceof Identifier) {
continue;
}
$alias = $useUses[0]->alias;
if ($alias->toString() === $tag) {
return $useUses[0]->name->toString();
$alias = $useUse->alias;
if ($alias->toString() === $tag) {
return $prefix . $useUse->name->toString();
}
}
}

View File

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Rector\PostRector\Collector;
use PhpParser\Node;
use PhpParser\Node\Stmt\GroupUse;
use Rector\Core\Provider\CurrentFileProvider;
use Rector\Core\ValueObject\Application\File;
use Rector\Naming\Naming\UseImportsResolver;
@ -63,11 +64,14 @@ final class UseNodesToAddCollector implements NodeCollectorInterface
$uses = $this->useImportsResolver->resolveForNode($node);
foreach ($uses as $use) {
$prefix = $use instanceof GroupUse
? $use->prefix . '\\'
: '';
foreach ($use->uses as $useUse) {
if ($useUse->alias !== null) {
$objectTypes[] = new AliasedObjectType($useUse->alias->toString(), (string) $useUse->name);
$objectTypes[] = new AliasedObjectType($useUse->alias->toString(), $prefix . $useUse->name);
} else {
$objectTypes[] = new FullyQualifiedObjectType((string) $useUse->name);
$objectTypes[] = new FullyQualifiedObjectType($prefix . $useUse->name);
}
}
}

View File

@ -6,6 +6,7 @@ namespace Rector\StaticTypeMapper\Naming;
use PhpParser\Node;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\GroupUse;
use PhpParser\Node\Stmt\Use_;
use PhpParser\Node\Stmt\UseUse;
use PHPStan\Analyser\NameScope;
@ -81,7 +82,7 @@ final class NameScopeFactory
}
/**
* @param Use_[] $useNodes
* @param Use_[]|GroupUse[] $useNodes
* @return array<string, string>
*/
private function resolveUseNamesByAlias(array $useNodes): array
@ -89,6 +90,9 @@ final class NameScopeFactory
$useNamesByAlias = [];
foreach ($useNodes as $useNode) {
$prefix = $useNode instanceof GroupUse
? $useNode->prefix . '\\'
: '';
foreach ($useNode->uses as $useUse) {
/** @var UseUse $useUse */
$aliasName = $useUse->getAlias()
@ -97,7 +101,7 @@ final class NameScopeFactory
// uses must be lowercase, as PHPStan lowercases it
$lowercasedAliasName = strtolower($aliasName);
$useNamesByAlias[$lowercasedAliasName] = $useUse->name->toString();
$useNamesByAlias[$lowercasedAliasName] = $prefix . $useUse->name->toString();
}
}

View File

@ -6,6 +6,7 @@ namespace Rector\Tests\Naming\Naming\UseImportsResolver;
use Iterator;
use PhpParser\Node\Stmt\Property;
use PhpParser\Node\Stmt\Use_;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
use Rector\Naming\Naming\UseImportsResolver;
use Rector\Testing\PHPUnit\AbstractTestCase;
@ -45,7 +46,9 @@ final class UseImportsResolverTest extends AbstractTestCase
foreach ($resolvedUses as $resolvedUse) {
foreach ($resolvedUse->uses as $useUse) {
$stringUses[] = $useUse->name->tostring();
$stringUses[] = $resolvedUse instanceof Use_
? $useUse->name->tostring()
: $resolvedUse->prefix->toString() . '\\' . $useUse->name->toString();
}
}

View File

@ -6,6 +6,7 @@ namespace Rector\CodingStyle\NodeAnalyzer;
use Nette\Utils\Strings;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\GroupUse;
use PhpParser\Node\Stmt\Use_;
use PhpParser\Node\Stmt\UseUse;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
@ -37,35 +38,38 @@ final class UseImportNameMatcher
}
/**
* @param Use_[] $uses
* @param Use_[]|GroupUse[] $uses
*/
public function matchNameWithUses(string $tag, array $uses): ?string
{
foreach ($uses as $use) {
$prefix = $use instanceof GroupUse
? $use->prefix . '\\'
: '';
foreach ($use->uses as $useUse) {
if (! $this->isUseMatchingName($tag, $useUse)) {
continue;
}
return $this->resolveName($tag, $useUse);
return $this->resolveName($prefix, $tag, $useUse);
}
}
return null;
}
public function resolveName(string $tag, UseUse $useUse): string
public function resolveName(string $prefix, string $tag, UseUse $useUse): string
{
if ($useUse->alias === null) {
return $useUse->name->toString();
return $prefix . $useUse->name->toString();
}
$unaliasedShortClass = Strings::substring($tag, Strings::length($useUse->alias->toString()));
if (\str_starts_with($unaliasedShortClass, '\\')) {
return $useUse->name . $unaliasedShortClass;
return $prefix . $useUse->name . $unaliasedShortClass;
}
return $useUse->name . '\\' . $unaliasedShortClass;
return $prefix . $useUse->name . '\\' . $unaliasedShortClass;
}
private function isUseMatchingName(string $tag, UseUse $useUse): bool

View File

@ -9,6 +9,7 @@ use PhpParser\Node\Expr\ConstFetch;
use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\GroupUse;
use PhpParser\Node\Stmt\Use_;
use Rector\Core\Rector\AbstractRector;
use Rector\Naming\Naming\UseImportsResolver;
@ -101,7 +102,7 @@ CODE_SAMPLE
}
/**
* @param Use_[] $useNodes
* @param Use_[]|GroupUse[] $useNodes
*/
private function getFullyQualifiedName(array $useNodes, ConstFetch|FuncCall $node): ?string
{
@ -113,22 +114,31 @@ CODE_SAMPLE
$typeFilter = $node instanceof ConstFetch ? Use_::TYPE_CONSTANT : Use_::TYPE_FUNCTION;
foreach ($useNodes as $useNode) {
$prefix = $this->resolvePrefix($useNode);
if ($useNode->type !== $typeFilter) {
continue;
}
foreach ($useNode->uses as $useUse) {
if ($name === $useUse->name->toLowerString()) {
return $useUse->name->toString();
if ($name === $prefix . $useUse->name->toLowerString()) {
return $prefix . $useUse->name->toString();
}
$alias = $useUse->getAlias();
if ($name === $alias->toLowerString()) {
return $useUse->name->toString();
return $prefix . $useUse->name->toString();
}
}
}
return null;
}
private function resolvePrefix(Use_|GroupUse $useNode): string
{
return $useNode instanceof GroupUse
? $useNode->prefix . '\\'
: '';
}
}

View File

@ -6,12 +6,11 @@ namespace Rector\Naming\Naming;
use PhpParser\Node\Identifier;
use PhpParser\Node\Name;
use Rector\NodeNameResolver\NodeNameResolver;
use PhpParser\Node\Stmt\GroupUse;
final class AliasNameResolver
{
public function __construct(
private readonly NodeNameResolver $nodeNameResolver,
private readonly UseImportsResolver $useImportsResolver,
) {
}
@ -22,25 +21,22 @@ final class AliasNameResolver
$nameString = $name->toString();
foreach ($uses as $use) {
$prefix = $use instanceof GroupUse
? $use->prefix . '\\'
: '';
$useUses = $use->uses;
if (count($useUses) > 1) {
continue;
}
foreach ($useUses as $useUse) {
if (! $useUse->alias instanceof Identifier) {
continue;
}
if (! isset($useUses[0])) {
continue;
}
$name = $prefix . $useUse->name->toString();
if ($name !== $nameString) {
continue;
}
$useUse = $useUses[0];
if (! $useUse->alias instanceof Identifier) {
continue;
return (string) $useUse->getAlias();
}
if (! $this->nodeNameResolver->isName($useUse->name, $nameString)) {
continue;
}
return (string) $useUse->getAlias();
}
return null;

View File

@ -5,11 +5,10 @@ declare(strict_types=1);
namespace Rector\Naming\Naming;
use PhpParser\Node;
use PhpParser\Node\Name;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\GroupUse;
use PhpParser\Node\Stmt\Namespace_;
use PhpParser\Node\Stmt\Use_;
use PhpParser\Node\Stmt\UseUse;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
use Rector\Core\PhpParser\Node\CustomNode\FileWithoutNamespace;
@ -21,7 +20,7 @@ final class UseImportsResolver
}
/**
* @return Use_[]
* @return Use_[]|GroupUse[]
*/
public function resolveForNode(Node $node): array
{
@ -33,29 +32,9 @@ final class UseImportsResolver
return [];
}
$collectedUses = [];
foreach ($namespace->stmts as $stmt) {
if ($stmt instanceof Use_) {
$collectedUses[] = $stmt;
continue;
}
if ($stmt instanceof GroupUse) {
$groupUseUses = [];
foreach ($stmt->uses as $useUse) {
$groupUseUses[] = new UseUse(
new Name($stmt->prefix . '\\' . $useUse->name),
$useUse->alias,
$useUse->type,
$useUse->getAttributes()
);
}
$collectedUses[] = new Use_($groupUseUses, $stmt->type, $stmt->getAttributes());
}
}
return $collectedUses;
return array_filter(
$namespace->stmts,
fn (Stmt $stmt): bool => $stmt instanceof Use_ || $stmt instanceof GroupUse
);
}
}

View File

@ -14,6 +14,7 @@ use PhpParser\Node\Name;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassLike;
use PhpParser\Node\Stmt\GroupUse;
use PhpParser\Node\Stmt\Namespace_;
use PhpParser\Node\Stmt\Use_;
use PhpParser\Node\Stmt\UseUse;
@ -420,9 +421,14 @@ final class ClassRenamer
}
foreach ($uses as $use) {
if ($this->nodeNameResolver->isName($use, $newName)) {
// name already exists
return false;
$prefix = $use instanceof GroupUse
? $use->prefix . '\\'
: '';
foreach ($use->uses as $useUse) {
if ($prefix . $useUse->name->toString() === $newName) {
// name already exists
return false;
}
}
}

View File

@ -8,6 +8,7 @@ use Nette\Utils\Strings;
use PhpParser\Node;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Stmt\GroupUse;
use PhpParser\Node\Stmt\Use_;
use PhpParser\Node\Stmt\UseUse;
use PHPStan\Analyser\Scope;
@ -90,7 +91,7 @@ final class ObjectTypeSpecifier
}
/**
* @param Use_[] $uses
* @param Use_[]|GroupUse[] $uses
*/
private function matchAliasedObjectType(Node $node, ObjectType $objectType, array $uses): ?AliasedObjectType
{
@ -102,14 +103,17 @@ final class ObjectTypeSpecifier
$parent = $node->getAttribute(AttributeKey::PARENT_NODE);
foreach ($uses as $use) {
$prefix = $use instanceof GroupUse
? $use->prefix . '\\'
: '';
foreach ($use->uses as $useUse) {
if ($useUse->alias === null) {
continue;
}
$useName = $useUse->name->toString();
$useName = $prefix . $useUse->name->toString();
$alias = $useUse->alias->toString();
$fullyQualifiedName = $useUse->name->toString();
$fullyQualifiedName = $prefix . $useUse->name->toString();
$processAliasedObject = $this->processAliasedObject(
$alias,
@ -154,7 +158,7 @@ final class ObjectTypeSpecifier
}
/**
* @param Use_[] $uses
* @param Use_[]|GroupUse[] $uses
*/
private function matchShortenedObjectType(
ObjectType $objectType,
@ -165,17 +169,20 @@ final class ObjectTypeSpecifier
}
foreach ($uses as $use) {
$prefix = $use instanceof GroupUse
? $use->prefix . '\\'
: '';
foreach ($use->uses as $useUse) {
if ($useUse->alias !== null) {
continue;
}
$partialNamespaceObjectType = $this->matchPartialNamespaceObjectType($objectType, $useUse);
$partialNamespaceObjectType = $this->matchPartialNamespaceObjectType($prefix, $objectType, $useUse);
if ($partialNamespaceObjectType !== null) {
return $partialNamespaceObjectType;
}
$partialNamespaceObjectType = $this->matchClassWithLastUseImportPart($objectType, $useUse);
$partialNamespaceObjectType = $this->matchClassWithLastUseImportPart($prefix, $objectType, $useUse);
if ($partialNamespaceObjectType instanceof FullyQualifiedObjectType) {
// keep Generic items
if ($objectType instanceof GenericObjectType) {
@ -198,8 +205,11 @@ final class ObjectTypeSpecifier
return null;
}
private function matchPartialNamespaceObjectType(ObjectType $objectType, UseUse $useUse): ?ShortenedObjectType
{
private function matchPartialNamespaceObjectType(
string $prefix,
ObjectType $objectType,
UseUse $useUse
): ?ShortenedObjectType {
// partial namespace
if (! \str_starts_with($objectType->getClassName(), $useUse->name->getLast() . '\\')) {
return null;
@ -207,7 +217,7 @@ final class ObjectTypeSpecifier
$classNameWithoutLastUsePart = Strings::after($objectType->getClassName(), '\\', 1);
$connectedClassName = $useUse->name->toString() . '\\' . $classNameWithoutLastUsePart;
$connectedClassName = $prefix . $useUse->name->toString() . '\\' . $classNameWithoutLastUsePart;
if (! $this->reflectionProvider->hasClass($connectedClassName)) {
return null;
}
@ -222,20 +232,23 @@ final class ObjectTypeSpecifier
/**
* @return FullyQualifiedObjectType|ShortenedObjectType|null
*/
private function matchClassWithLastUseImportPart(ObjectType $objectType, UseUse $useUse): ?ObjectType
{
private function matchClassWithLastUseImportPart(
string $prefix,
ObjectType $objectType,
UseUse $useUse
): ?ObjectType {
if ($useUse->name->getLast() !== $objectType->getClassName()) {
return null;
}
if (! $this->reflectionProvider->hasClass($useUse->name->toString())) {
if (! $this->reflectionProvider->hasClass($prefix . $useUse->name->toString())) {
return null;
}
if ($objectType->getClassName() === $useUse->name->toString()) {
if ($objectType->getClassName() === $prefix . $useUse->name->toString()) {
return new FullyQualifiedObjectType($objectType->getClassName());
}
return new ShortenedObjectType($objectType->getClassName(), $useUse->name->toString());
return new ShortenedObjectType($objectType->getClassName(), $prefix . $useUse->name->toString());
}
}