Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add NoUnneededImportAliasFixer #4498

Closed
wants to merge 22 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
d3f5d81
Add NoUnneededAliasFixer
JakeFr Aug 9, 2019
f25e471
Make NoUnneededAliasFixer final
JakeFr Aug 9, 2019
fe4dabd
Rename to NoUnneededImportAliasFixer, supports comments, minor fixes …
JakeFr Aug 12, 2019
c230285
Add support for multiple use statement in NamespaceUsesAnalyzer
JakeFr Aug 12, 2019
0fb2227
Improve isCandidate
JakeFr Aug 13, 2019
04203fe
Merge branch 'master' into nounneededalias
keradus Aug 24, 2019
3dcd82d
Add in PhpCsFixer rules sets, add code sample for function and const …
JakeFr Sep 24, 2019
c2619ae
Merge branch 'master' into nounneededalias
JakeFr Sep 24, 2019
b694388
Code sample is not specific
JakeFr Sep 25, 2019
0dcf935
Apply php-cs-fixer
JakeFr Sep 25, 2019
1bc7abd
Fix test with close tags
JakeFr Sep 25, 2019
d23a138
Add NoUnneededAliasFixer
JakeFr Aug 9, 2019
9145bcd
Make NoUnneededAliasFixer final
JakeFr Aug 9, 2019
660e566
Rename to NoUnneededImportAliasFixer, supports comments, minor fixes …
JakeFr Aug 12, 2019
f74a41b
Add support for multiple use statement in NamespaceUsesAnalyzer
JakeFr Aug 12, 2019
7565700
Improve isCandidate
JakeFr Aug 13, 2019
af14d5a
Add in PhpCsFixer rules sets, add code sample for function and const …
JakeFr Sep 24, 2019
a374577
Code sample is not specific
JakeFr Sep 25, 2019
6a0ab72
Apply php-cs-fixer
JakeFr Sep 25, 2019
0547f98
Fix test with close tags
JakeFr Sep 25, 2019
c5d6da9
Merge branch 'nounneededalias' of https://github.com/JakeFr/PHP-CS-Fi…
JakeFr Oct 9, 2019
df50327
Merge branch 'master' into nounneededalias
JakeFr Oct 9, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.rst
Expand Up @@ -1170,6 +1170,10 @@ Choose from the list of available rules:

A final class must not have final methods.

* **no_unneeded_import_alias** [@PhpCsFixer]

Remove unneeded alias in ``use`` clauses.

* **no_unreachable_default_argument_value** [@PhpCsFixer:risky]

In function arguments there must not be arguments with default values
Expand Down
100 changes: 100 additions & 0 deletions src/Fixer/Import/NoUnneededImportAliasFixer.php
@@ -0,0 +1,100 @@
<?php

/*
* This file is part of PHP CS Fixer.
*
* (c) Fabien Potencier <fabien@symfony.com>
* Dariusz Rumiński <dariusz.ruminski@gmail.com>
*
* This source file is subject to the MIT license that is bundled
* with this source code in the file LICENSE.
*/

namespace PhpCsFixer\Fixer\Import;

use PhpCsFixer\AbstractFixer;
use PhpCsFixer\FixerDefinition\CodeSample;
use PhpCsFixer\FixerDefinition\FixerDefinition;
use PhpCsFixer\Tokenizer\Analyzer\Analysis\NamespaceUseAnalysis;
use PhpCsFixer\Tokenizer\Analyzer\NamespaceUsesAnalyzer;
use PhpCsFixer\Tokenizer\Tokens;

final class NoUnneededImportAliasFixer extends AbstractFixer
{
/**
* {@inheritdoc}
*/
public function getDefinition()
{
return new FixerDefinition(
'Remove unneeded alias in `use` clauses.',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reading this sentence, i don't know what unneeded means.
is baz as baz only case? then, audience of this rule is very limited

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's unneeded when the alias is equal to the last part of the fqcn
same with function and const imports
I found some import in big legacy project
not found in recent projects with modern IDE

[
new CodeSample(
'<?php
namespace Foo;

use Bar\Baz as Baz;

use const some\a\ConstA as ConstA;
use function some\a\fn_b as fn_b;
'
),
]
);
}

/**
* {@inheritdoc}
*/
public function getPriority()
{
// should be run after the NoUnusedImportsFixer (just for save performance)
return -15;
}

/**
* {@inheritdoc}
*/
public function isCandidate(Tokens $tokens)
{
return $tokens->isAllTokenKindsFound([T_USE, T_AS]);
}

/**
* {@inheritdoc}
*/
protected function applyFix(\SplFileInfo $file, Tokens $tokens)
{
$useDeclarations = (new NamespaceUsesAnalyzer())->getDeclarationsFromTokens($tokens, false);

foreach ($useDeclarations as $declaration) {
if (!$declaration->isAliased()) {
continue;
}
$shortNameStartPos = strrpos($declaration->getFullName(), '\\');
$shortName = false === $shortNameStartPos ? $declaration->getFullName() : substr($declaration->getFullName(), $shortNameStartPos + 1);

if ($declaration->getShortName() !== $shortName) {
continue;
}

$this->removeAlias($tokens, $declaration);
}
}

private function removeAlias(Tokens $tokens, NamespaceUseAnalysis $declaration)
{
$asIndex = $tokens->getNextTokenOfKind($declaration->getStartIndex(), [[T_AS]]);
if (null === $asIndex || $asIndex > $declaration->getEndIndex()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is only called if the import uses an alias so this should never happen.

return;
}

for ($i = $tokens->getPrevMeaningfulToken($asIndex) + 1; $i <= $declaration->getEndIndex() - 1; ++$i) {
if ($tokens[$i]->isWhitespace() && !($tokens[$i - 1]->isComment() || $tokens[$i + 1]->isComment() || $tokens[$i + 1]->isGivenKind([T_CLOSE_TAG]))) {
$tokens->clearAt($i);
} elseif ($tokens[$i]->isGivenKind([T_AS, T_STRING])) {
$tokens->clearAt($i);
}
}
}
}
1 change: 1 addition & 0 deletions src/RuleSet.php
Expand Up @@ -238,6 +238,7 @@ final class RuleSet implements RuleSetInterface
'no_superfluous_elseif' => true,
'no_unneeded_curly_braces' => true,
'no_unneeded_final_method' => true,
'no_unneeded_import_alias' => true,
'no_unset_cast' => true,
'no_useless_else' => true,
'no_useless_return' => true,
Expand Down
42 changes: 31 additions & 11 deletions src/Tokenizer/Analyzer/NamespaceUsesAnalyzer.php
Expand Up @@ -24,32 +24,34 @@ final class NamespaceUsesAnalyzer
{
/**
* @param Tokens $tokens
* @param bool $skipMultipleUseStatement
*
* @return NamespaceUseAnalysis[]
*/
public function getDeclarationsFromTokens(Tokens $tokens)
public function getDeclarationsFromTokens(Tokens $tokens, $skipMultipleUseStatement = true)
{
$tokenAnalyzer = new TokensAnalyzer($tokens);
$useIndexes = $tokenAnalyzer->getImportUseIndexes();

return $this->getDeclarations($tokens, $useIndexes);
return $this->getDeclarations($tokens, $useIndexes, $skipMultipleUseStatement);
}

/**
* @param Tokens $tokens
* @param array $useIndexes
* @param bool $skipMultipleUseStatement
*
* @return NamespaceUseAnalysis[]
*/
private function getDeclarations(Tokens $tokens, array $useIndexes)
private function getDeclarations(Tokens $tokens, array $useIndexes, $skipMultipleUseStatement = true)
{
$uses = [];

foreach ($useIndexes as $index) {
$endIndex = $tokens->getNextTokenOfKind($index, [';', [T_CLOSE_TAG]]);
$analysis = $this->parseDeclaration($tokens, $index, $endIndex);
if ($analysis) {
$uses[] = $analysis;
$analyses = $this->parseDeclaration($tokens, $index, $endIndex, $skipMultipleUseStatement);
if ($analyses) {
$uses = array_merge($uses, $analyses);
}
}

Expand All @@ -60,20 +62,36 @@ private function getDeclarations(Tokens $tokens, array $useIndexes)
* @param Tokens $tokens
* @param int $startIndex
* @param int $endIndex
* @param bool $skipMultipleUseStatement
*
* @return null|NamespaceUseAnalysis
* @return null|NamespaceUseAnalysis[]
*/
private function parseDeclaration(Tokens $tokens, $startIndex, $endIndex)
private function parseDeclaration(Tokens $tokens, $startIndex, $endIndex, $skipMultipleUseStatement = true)
{
$namespaceUseAnalyses = [];
$fullName = $shortName = '';
$aliased = false;

$type = NamespaceUseAnalysis::TYPE_CLASS;
for ($i = $startIndex; $i <= $endIndex; ++$i) {
$token = $tokens[$i];
if ($token->equals(',') || $token->isGivenKind(CT::T_GROUP_IMPORT_BRACE_CLOSE)) {
if ($token->equals(',')) {
if ($skipMultipleUseStatement) {
return null;
}
$namespaceUseAnalyses[] = new NamespaceUseAnalysis(
trim($fullName),
$shortName,
$aliased,
$startIndex,
$i,
$type
);
$fullName = $shortName = '';
$aliased = false;
$startIndex = $i + 1;
} elseif ($token->isGivenKind(CT::T_GROUP_IMPORT_BRACE_CLOSE)) {
// do not touch group use declarations until the logic of this is added (for example: `use some\a\{ClassD};`)
// ignore multiple use statements that should be split into few separate statements (for example: `use BarB, BarC as C;`)
return null;
}

Expand All @@ -99,13 +117,15 @@ private function parseDeclaration(Tokens $tokens, $startIndex, $endIndex)
}
}

return new NamespaceUseAnalysis(
$namespaceUseAnalyses[] = new NamespaceUseAnalysis(
trim($fullName),
$shortName,
$aliased,
$startIndex,
$endIndex,
$type
);

return $namespaceUseAnalyses;
}
}