Skip to content

Commit dc6ebd9

Browse files
authored
Merge pull request #125 from kubawerlos/add-comment-surrounded-by-spaces-fixer
Add NoDuplicatedImportsFixer
2 parents 3d60d1d + 66e850a commit dc6ebd9

File tree

4 files changed

+218
-1
lines changed

4 files changed

+218
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## [Unreleased]
44
- Add CommentSurroundedBySpacesFixer
5+
- Add NoDuplicatedImportsFixer
56

67
## v1.14.0 - *2019-07-25*
78
- Add DataProviderNameFixer

README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
[![Build status](https://img.shields.io/travis/kubawerlos/php-cs-fixer-custom-fixers/master.svg)](https://travis-ci.org/kubawerlos/php-cs-fixer-custom-fixers)
1010
[![Code coverage](https://img.shields.io/coveralls/github/kubawerlos/php-cs-fixer-custom-fixers/master.svg)](https://coveralls.io/github/kubawerlos/php-cs-fixer-custom-fixers?branch=master)
11-
![Tests](https://img.shields.io/badge/tests-1030-brightgreen.svg)
11+
![Tests](https://img.shields.io/badge/tests-1051-brightgreen.svg)
1212
[![Mutation testing badge](https://badge.stryker-mutator.io/github.com/kubawerlos/php-cs-fixer-custom-fixers/master)](https://stryker-mutator.github.io)
1313

1414
A set of custom fixers for [PHP CS Fixer](https://github.com/FriendsOfPHP/PHP-CS-Fixer).
@@ -121,6 +121,16 @@ There must be no comment generated by Doctrine Migrations.
121121
}
122122
```
123123

124+
#### NoDuplicatedImportsFixer
125+
Duplicated `use` statements must be removed.
126+
```diff
127+
<?php
128+
namespace FooBar;
129+
use Foo;
130+
-use Foo;
131+
use Bar;
132+
```
133+
124134
#### NoImportFromGlobalNamespaceFixer
125135
There must be no import from global namespace.
126136
```diff
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
namespace PhpCsFixerCustomFixers\Fixer;
6+
7+
use PhpCsFixer\Fixer\Import\NoUnusedImportsFixer;
8+
use PhpCsFixer\FixerDefinition\CodeSample;
9+
use PhpCsFixer\FixerDefinition\FixerDefinition;
10+
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
11+
use PhpCsFixer\Tokenizer\Analyzer\Analysis\NamespaceUseAnalysis;
12+
use PhpCsFixer\Tokenizer\Analyzer\NamespacesAnalyzer;
13+
use PhpCsFixer\Tokenizer\Analyzer\NamespaceUsesAnalyzer;
14+
use PhpCsFixer\Tokenizer\Tokens;
15+
16+
final class NoDuplicatedImportsFixer extends AbstractFixer
17+
{
18+
public function getDefinition(): FixerDefinitionInterface
19+
{
20+
return new FixerDefinition(
21+
'Duplicated `use` statements must be removed.',
22+
[new CodeSample('<?php
23+
namespace FooBar;
24+
use Foo;
25+
use Foo;
26+
use Bar;
27+
')]
28+
);
29+
}
30+
31+
public function isCandidate(Tokens $tokens): bool
32+
{
33+
return $tokens->isTokenKindFound(T_USE);
34+
}
35+
36+
public function isRisky(): bool
37+
{
38+
return false;
39+
}
40+
41+
public function fix(\SplFileInfo $file, Tokens $tokens): void
42+
{
43+
$useDeclarations = (new NamespaceUsesAnalyzer())->getDeclarationsFromTokens($tokens);
44+
45+
foreach ((new NamespacesAnalyzer())->getDeclarations($tokens) as $namespace) {
46+
$currentNamespaceUseDeclarations = \array_filter(
47+
$useDeclarations,
48+
static function (NamespaceUseAnalysis $useDeclaration) use ($namespace) {
49+
return $useDeclaration->getStartIndex() >= $namespace->getScopeStartIndex()
50+
&& $useDeclaration->getEndIndex() <= $namespace->getScopeEndIndex();
51+
}
52+
);
53+
54+
$used = [];
55+
56+
foreach ($currentNamespaceUseDeclarations as $useDeclaration) {
57+
if (isset($used[$useDeclaration->getFullName()])) {
58+
$this->removeUseDeclaration($tokens, $useDeclaration);
59+
}
60+
$used[$useDeclaration->getFullName()] = true;
61+
}
62+
}
63+
}
64+
65+
public function getPriority(): int
66+
{
67+
return 0;
68+
}
69+
70+
private function removeUseDeclaration(Tokens $tokens, NamespaceUseAnalysis $useDeclaration): void
71+
{
72+
static $noUnusedImportsFixer, $reflectionMethod;
73+
74+
if ($reflectionMethod === null) {
75+
$noUnusedImportsFixer = new NoUnusedImportsFixer();
76+
77+
$reflectionMethod = new \ReflectionMethod($noUnusedImportsFixer, 'removeUseDeclaration');
78+
$reflectionMethod->setAccessible(true);
79+
}
80+
81+
$reflectionMethod->invoke($noUnusedImportsFixer, $tokens, $useDeclaration);
82+
}
83+
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
namespace Tests\Fixer;
6+
7+
/**
8+
* @internal
9+
*
10+
* @covers \PhpCsFixerCustomFixers\Fixer\NoDuplicatedImportsFixer
11+
*/
12+
final class NoDuplicatedImportsFixerTest extends AbstractFixerTestCase
13+
{
14+
public function testPriority(): void
15+
{
16+
static::assertSame(0, $this->fixer->getPriority());
17+
}
18+
19+
public function testIsRisky(): void
20+
{
21+
static::assertFalse($this->fixer->isRisky());
22+
}
23+
24+
/**
25+
* @param string $expected
26+
* @param null|string $input
27+
*
28+
* @dataProvider provideFixCases
29+
*/
30+
public function testFix(string $expected, ?string $input = null): void
31+
{
32+
$this->doTest($expected, $input);
33+
}
34+
35+
public function provideFixCases(): iterable
36+
{
37+
yield [
38+
'<?php
39+
namespace FooBar;
40+
use Foo;
41+
use Bar;
42+
',
43+
'<?php
44+
namespace FooBar;
45+
use Foo;
46+
use Foo;
47+
use Bar;
48+
',
49+
];
50+
51+
yield [
52+
'<?php
53+
namespace FooBar;
54+
use Foo;
55+
use Bar;
56+
',
57+
'<?php
58+
namespace FooBar;
59+
use Foo;
60+
use Bar;
61+
use Foo;
62+
',
63+
];
64+
65+
yield [
66+
'<?php
67+
namespace FooBar;
68+
use Vendor\Project\Foo;
69+
use Bar;
70+
use Baz;
71+
',
72+
'<?php
73+
namespace FooBar;
74+
use Vendor\Project\Foo;
75+
use Bar;
76+
use Vendor\Project\Foo;
77+
use Baz;
78+
',
79+
];
80+
81+
yield [
82+
'<?php
83+
namespace FooBar;
84+
use Vendor\Project\Duplicated\Foo;
85+
use Vendor\Foo;
86+
use Vendor\Project\Duplicated\Bar;
87+
use Vendor\Bar;
88+
',
89+
'<?php
90+
namespace FooBar;
91+
use Vendor\Project\Duplicated\Foo;
92+
use Vendor\Foo;
93+
use Vendor\Project\Duplicated\Bar;
94+
use Vendor\Project\Duplicated\Foo;
95+
use Vendor\Project\Duplicated\Bar;
96+
use Vendor\Bar;
97+
use Vendor\Project\Duplicated\Foo;
98+
use Vendor\Project\Duplicated\Foo;
99+
',
100+
];
101+
102+
yield [
103+
'<?php
104+
namespace Foo;
105+
use Vendor\Class1;
106+
use Vendor\Class2;
107+
namespace Bar;
108+
use Vendor\Class1;
109+
use Vendor\Class2;
110+
',
111+
'<?php
112+
namespace Foo;
113+
use Vendor\Class1;
114+
use Vendor\Class1;
115+
use Vendor\Class2;
116+
namespace Bar;
117+
use Vendor\Class1;
118+
use Vendor\Class2;
119+
use Vendor\Class2;
120+
',
121+
];
122+
}
123+
}

0 commit comments

Comments
 (0)