Skip to content

Commit

Permalink
Refactor use import resolving (#4998)
Browse files Browse the repository at this point in the history
  • Loading branch information
staabm committed Sep 12, 2023
1 parent cbc632c commit aeba96a
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 86 deletions.
5 changes: 5 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -580,3 +580,8 @@ parameters:
- '#Call to deprecated method markAsChanged\(\) of class Rector\\BetterPhpDocParser\\PhpDocInfo\\PhpDocInfo#'
- '#Method "(importNames|renamePhpDocType)\(\)" returns bool type, so the name should start with is/has/was#'
- '#Call to an undefined method Rector\\BetterPhpDocParser\\PhpDocInfo\\PhpDocInfo\:\:markAsChanged\(\)#'

# ignore until PHPParser returns more precise types
-
message: '#Parameter \#1 \$useType of callable callable\(0\|1\|2\|3, PhpParser\\Node\\Stmt\\UseUse, string\): void expects 0\|1\|2\|3, int given.#'
path: rules/CodingStyle/ClassNameImport/UseImportsTraverser.php
14 changes: 8 additions & 6 deletions rules/CodingStyle/Application/UseImportsAdder.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,10 @@ public function addImportsToStmts(
array $constantUseImportTypes,
array $functionUseImportTypes
): array {
$existingUseImportTypes = $this->usedImportsResolver->resolveForStmts($stmts);
$existingConstantUseImports = $this->usedImportsResolver->resolveConstantImportsForStmts($stmts);
$existingFunctionUseImports = $this->usedImportsResolver->resolveFunctionImportsForStmts($stmts);
$usedImports = $this->usedImportsResolver->resolveForStmts($stmts);
$existingUseImportTypes = $usedImports->getUseImports();
$existingConstantUseImports = $usedImports->getConstantImports();
$existingFunctionUseImports = $usedImports->getFunctionImports();

$useImportTypes = $this->diffFullyQualifiedObjectTypes($useImportTypes, $existingUseImportTypes);
$constantUseImportTypes = $this->diffFullyQualifiedObjectTypes(
Expand Down Expand Up @@ -103,9 +104,10 @@ public function addImportsToNamespace(
): void {
$namespaceName = $this->getNamespaceName($namespace);

$existingUseImportTypes = $this->usedImportsResolver->resolveForStmts($namespace->stmts);
$existingConstantUseImportTypes = $this->usedImportsResolver->resolveConstantImportsForStmts($namespace->stmts);
$existingFunctionUseImportTypes = $this->usedImportsResolver->resolveFunctionImportsForStmts($namespace->stmts);
$existingUsedImports = $this->usedImportsResolver->resolveForStmts($namespace->stmts);
$existingUseImportTypes = $existingUsedImports->getUseImports();
$existingConstantUseImportTypes = $existingUsedImports->getConstantImports();
$existingFunctionUseImportTypes = $existingUsedImports->getFunctionImports();

$existingUseImportTypes = $this->typeFactory->uniquateTypes($existingUseImportTypes);

Expand Down
7 changes: 7 additions & 0 deletions rules/CodingStyle/ClassNameImport/AliasUsesResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Rector\CodingStyle\ClassNameImport;

use PhpParser\Node\Stmt\Use_;
use PhpParser\Node;
use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt;
Expand Down Expand Up @@ -44,10 +45,16 @@ public function resolveFromStmts(array $stmts): array
{
$aliasedUses = [];

/** @param Use_::TYPE_* $useType */
$this->useImportsTraverser->traverserStmts($stmts, static function (
int $useType,
UseUse $useUse,
string $name
) use (&$aliasedUses): void {
if ($useType !== Use_::TYPE_NORMAL) {
return;
}

if (! $useUse->alias instanceof Identifier) {
return;
}
Expand Down
46 changes: 7 additions & 39 deletions rules/CodingStyle/ClassNameImport/UseImportsTraverser.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,67 +25,39 @@ public function __construct(

/**
* @param Stmt[] $stmts
* @param callable(UseUse $useUse, string $name): void $callable
* @param callable(Use_::TYPE_* $useType, UseUse $useUse, string $name): void $callable
*/
public function traverserStmts(array $stmts, callable $callable): void
{
$this->traverseForType($stmts, $callable, Use_::TYPE_NORMAL);
}

/**
* @param Stmt[] $stmts
* @param callable(UseUse $useUse, string $name): void $callable
*/
public function traverserStmtsForConstants(array $stmts, callable $callable): void
{
$this->traverseForType($stmts, $callable, Use_::TYPE_CONSTANT);
}

/**
* @param Stmt[] $stmts
* @param callable(UseUse $useUse, string $name): void $callable
*/
public function traverserStmtsForFunctions(array $stmts, callable $callable): void
{
$this->traverseForType($stmts, $callable, Use_::TYPE_FUNCTION);
}

/**
* @param callable(UseUse $useUse, string $name): void $callable
* @param Stmt[] $stmts
*/
private function traverseForType(array $stmts, callable $callable, int $desiredType): void
{
$this->simpleCallableNodeTraverser->traverseNodesWithCallable($stmts, function (Node $node) use (
$callable,
$desiredType
): ?int {
if ($node instanceof Namespace_ || $node instanceof FileWithoutNamespace) {
// traverse into namespaces
return null;
}

if ($node instanceof Use_ && $node->type === $desiredType) {
if ($node instanceof Use_) {
foreach ($node->uses as $useUse) {
$name = $this->nodeNameResolver->getName($useUse);
if ($name === null) {
continue;
}

$callable($useUse, $name);
$callable($node->type, $useUse, $name);
}
} elseif ($node instanceof GroupUse) {
$this->processGroupUse($node, $desiredType, $callable);
$this->processGroupUse($node, $callable);
}

return NodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN;
});
}

/**
* @param callable(UseUse $useUse, string $name): void $callable
* @param callable(Use_::TYPE_* $useType, UseUse $useUse, string $name): void $callable
*/
private function processGroupUse(GroupUse $groupUse, int $desiredType, callable $callable): void
private function processGroupUse(GroupUse $groupUse, callable $callable): void
{
if ($groupUse->type !== Use_::TYPE_UNKNOWN) {
return;
Expand All @@ -94,12 +66,8 @@ private function processGroupUse(GroupUse $groupUse, int $desiredType, callable
$prefixName = $groupUse->prefix->toString();

foreach ($groupUse->uses as $useUse) {
if ($useUse->type !== $desiredType) {
continue;
}

$name = $prefixName . '\\' . $this->nodeNameResolver->getName($useUse);
$callable($useUse, $name);
$callable($useUse->type, $useUse, $name);
}
}
}
62 changes: 21 additions & 41 deletions rules/CodingStyle/ClassNameImport/UsedImportsResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@

namespace Rector\CodingStyle\ClassNameImport;

use PhpParser\Node\Stmt\Use_;
use PhpParser\Node\Identifier;
use PhpParser\Node\Stmt;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\UseUse;
use Rector\CodingStyle\ClassNameImport\ValueObject\UsedImports;
use Rector\Core\PhpParser\Node\BetterNodeFinder;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\StaticTypeMapper\ValueObject\Type\AliasedObjectType;
Expand All @@ -24,9 +26,8 @@ public function __construct(

/**
* @param Stmt[] $stmts
* @return array<FullyQualifiedObjectType|AliasedObjectType>
*/
public function resolveForStmts(array $stmts): array
public function resolveForStmts(array $stmts): UsedImports
{
$usedImports = [];

Expand All @@ -40,53 +41,32 @@ public function resolveForStmts(array $stmts): array
$usedImports[] = new FullyQualifiedObjectType($className);
}

$usedConstImports = [];
$usedFunctionImports = [];
/** @param Use_::TYPE_* $useType */
$this->useImportsTraverser->traverserStmts($stmts, static function (
int $useType,
UseUse $useUse,
string $name
) use (&$usedImports): void {
if ($useUse->alias instanceof Identifier) {
$usedImports[] = new AliasedObjectType($useUse->alias->toString(), $name);
} else {
$usedImports[] = new FullyQualifiedObjectType($name);
) use (&$usedImports, &$usedFunctionImports, &$usedConstImports): void {
if ($useType === Use_::TYPE_NORMAL) {
if ($useUse->alias instanceof Identifier) {
$usedImports[] = new AliasedObjectType($useUse->alias->toString(), $name);
} else {
$usedImports[] = new FullyQualifiedObjectType($name);
}
}
});

return $usedImports;
}

/**
* @param Stmt[] $stmts
* @return FullyQualifiedObjectType[]
*/
public function resolveConstantImportsForStmts(array $stmts): array
{
$usedConstImports = [];
if ($useType === Use_::TYPE_FUNCTION) {
$usedFunctionImports[] = new FullyQualifiedObjectType($name);
}

$this->useImportsTraverser->traverserStmtsForConstants($stmts, static function (
UseUse $useUse,
string $name
) use (&$usedConstImports): void {
$usedConstImports[] = new FullyQualifiedObjectType($name);
if ($useType === Use_::TYPE_CONSTANT) {
$usedConstImports[] = new FullyQualifiedObjectType($name);
}
});

return $usedConstImports;
return new UsedImports($usedImports, $usedFunctionImports, $usedConstImports);
}

/**
* @param Stmt[] $stmts
* @return FullyQualifiedObjectType[]
*/
public function resolveFunctionImportsForStmts(array $stmts): array
{
$usedFunctionImports = [];

$this->useImportsTraverser->traverserStmtsForFunctions($stmts, static function (
UseUse $useUse,
string $name
) use (&$usedFunctionImports): void {
$usedFunctionImports[] = new FullyQualifiedObjectType($name);
});

return $usedFunctionImports;
}
}
43 changes: 43 additions & 0 deletions rules/CodingStyle/ClassNameImport/ValueObject/UsedImports.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

declare(strict_types=1);

namespace Rector\CodingStyle\ClassNameImport\ValueObject;

use Rector\StaticTypeMapper\ValueObject\Type\AliasedObjectType;
use Rector\StaticTypeMapper\ValueObject\Type\FullyQualifiedObjectType;

final class UsedImports {
/**
* @param array<FullyQualifiedObjectType|AliasedObjectType> $useImports
* @param FullyQualifiedObjectType[] $functionImports
* @param FullyQualifiedObjectType[] $constantImports
*/
public function __construct(private readonly array $useImports, private readonly array $functionImports, private readonly array $constantImports)
{
}

/**
* @return array<FullyQualifiedObjectType|AliasedObjectType>
*/
public function getUseImports() : array
{
return $this->useImports;
}

/**
* @return FullyQualifiedObjectType[]
*/
public function getFunctionImports() : array
{
return $this->functionImports;
}

/**
* @return FullyQualifiedObjectType[]
*/
public function getConstantImports() : array
{
return $this->constantImports;
}
}

0 comments on commit aeba96a

Please sign in to comment.