Skip to content

Commit

Permalink
UnusedUsesSniff - rewritten searching in annotations to simple fullte…
Browse files Browse the repository at this point in the history
…xt search
  • Loading branch information
ondrejmirtes committed Aug 8, 2016
1 parent 232ef22 commit 9b5f363
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 122 deletions.
64 changes: 4 additions & 60 deletions SlevomatCodingStandard/Helpers/ReferencedNameHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,13 @@ class ReferencedNameHelper
/**
* @param \PHP_CodeSniffer_File $phpcsFile
* @param integer $openTagPointer
* @param boolean $searchAnnotations
* @return \SlevomatCodingStandard\Helpers\ReferencedName[] referenced names
*/
public static function getAllReferencedNames(PHP_CodeSniffer_File $phpcsFile, $openTagPointer, $searchAnnotations = false)
public static function getAllReferencedNames(PHP_CodeSniffer_File $phpcsFile, $openTagPointer)
{
$cacheKey = $phpcsFile->getFilename() . '-' . $openTagPointer . ($searchAnnotations ? '-annotations' : '-no-annotations');
$cacheKey = $phpcsFile->getFilename() . '-' . $openTagPointer;
if (!isset(self::$allReferencedTypesCache[$cacheKey])) {
self::$allReferencedTypesCache[$cacheKey] = self::createAllReferencedNames($phpcsFile, $openTagPointer, $searchAnnotations);
self::$allReferencedTypesCache[$cacheKey] = self::createAllReferencedNames($phpcsFile, $openTagPointer);
}

return self::$allReferencedTypesCache[$cacheKey];
Expand All @@ -44,77 +43,22 @@ public static function getAllReferencedNames(PHP_CodeSniffer_File $phpcsFile, $o
/**
* @param \PHP_CodeSniffer_File $phpcsFile
* @param integer $openTagPointer
* @param boolean $searchAnnotations
* @return \SlevomatCodingStandard\Helpers\ReferencedName[] referenced names
*/
private static function createAllReferencedNames(PHP_CodeSniffer_File $phpcsFile, $openTagPointer, $searchAnnotations)
private static function createAllReferencedNames(PHP_CodeSniffer_File $phpcsFile, $openTagPointer)
{
$beginSearchAtPointer = $openTagPointer + 1;
$tokens = $phpcsFile->getTokens();
$phpDocTypes = [
T_DOC_COMMENT_STRING,
T_DOC_COMMENT_TAG,
];

$searchTypes = array_merge([T_RETURN_TYPE], TokenHelper::$nameTokenCodes);
if ($searchAnnotations) {
$searchTypes = array_merge($phpDocTypes, $searchTypes);
}

$types = [];

$matchTypesInAnnotation = function ($annotation, $nameStartPointer) use (&$types) {
$annotation = trim($annotation, '@ ');
if (preg_match('#([a-zA-Z0-9_|\[\]\\\]+)#', $annotation, $matches) > 0) {
$referencedNames = array_filter(array_map(function ($name) {
return trim($name, '[]');
}, explode('|', $matches[1])), function ($match) {
return !in_array($match, [
'null',
'self',
'static',
'mixed',
'array',
'string',
'int',
'integer',
'bool',
'boolean',
'callable',
], true);
});
foreach ($referencedNames as $name) {
$types[] = new ReferencedName($name, $nameStartPointer, ReferencedName::TYPE_DEFAULT);
}
}
};

while (true) {
$nameStartPointer = $phpcsFile->findNext($searchTypes, $beginSearchAtPointer);
if ($nameStartPointer === false) {
break;
}
$nameStartToken = $tokens[$nameStartPointer];
if (in_array($nameStartToken['code'], $phpDocTypes, true)) {
if ($nameStartToken['code'] === T_DOC_COMMENT_TAG) {
if (
!StringHelper::startsWith($nameStartToken['content'], '@var')
&& !StringHelper::startsWith($nameStartToken['content'], '@param')
&& !StringHelper::startsWith($nameStartToken['content'], '@return')
&& !StringHelper::startsWith($nameStartToken['content'], '@throws')
&& !StringHelper::startsWith($nameStartToken['content'], '@see')
&& !StringHelper::startsWith($nameStartToken['content'], '@link')
&& !StringHelper::startsWith($nameStartToken['content'], '@inherit')
) {
$matchTypesInAnnotation($nameStartToken['content'], $nameStartPointer);
}
} elseif ($nameStartToken['code'] === T_DOC_COMMENT_STRING) {
$matchTypesInAnnotation($nameStartToken['content'], $nameStartPointer);
}

$beginSearchAtPointer = $nameStartPointer + 1;
continue;
}
$nameEndPointer = self::findReferencedNameEndPointer($phpcsFile, $nameStartPointer);
if ($nameEndPointer === null) {
$beginSearchAtPointer = TokenHelper::findNextExcluding(
Expand Down
23 changes: 22 additions & 1 deletion SlevomatCodingStandard/Sniffs/Namespaces/UnusedUsesSniff.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public function register()
public function process(PHP_CodeSniffer_File $phpcsFile, $openTagPointer)
{
$unusedNames = UseStatementHelper::getUseStatements($phpcsFile, $openTagPointer);
$referencedNames = ReferencedNameHelper::getAllReferencedNames($phpcsFile, $openTagPointer, $this->searchAnnotations);
$referencedNames = ReferencedNameHelper::getAllReferencedNames($phpcsFile, $openTagPointer);

foreach ($referencedNames as $referencedName) {
$name = $referencedName->getNameAsReferencedInFile();
Expand All @@ -60,6 +60,27 @@ public function process(PHP_CodeSniffer_File $phpcsFile, $openTagPointer)
}
}

if ($this->searchAnnotations) {
$tokens = $phpcsFile->getTokens();
$searchAnnotationsPointer = $openTagPointer + 1;
while (true) {
$phpDocTokenPointer = $phpcsFile->findNext([T_DOC_COMMENT_TAG, T_DOC_COMMENT_STRING], $searchAnnotationsPointer);
if ($phpDocTokenPointer === false) {
break;
}

foreach ($unusedNames as $i => $useStatement) {
if (strpos($tokens[$phpDocTokenPointer]['content'], $useStatement->getNameAsReferencedInFile()) === false) {
continue;
}

unset($unusedNames[$i]);
}

$searchAnnotationsPointer = $phpDocTokenPointer + 1;
}
}

foreach ($unusedNames as $value) {
$fullName = $value->getFullyQualifiedTypeName();
if ($value->getNameAsReferencedInFile() !== $fullName) {
Expand Down
80 changes: 19 additions & 61 deletions tests/Helpers/ReferencedNameHelperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,72 +5,30 @@
class ReferencedNameHelperTest extends \SlevomatCodingStandard\Helpers\TestCase
{

public function dataGetAllReferencedNames()
{
return [
[
[
['\ExtendedClass', false, false],
['\ImplementedInterface', false, false],
['\FullyQualified\SomeOtherTrait', false, false],
['SomeTrait', false, false],
['TypehintedName', false, false],
['ClassInstance', false, false],
['StaticClass', false, false],
['\Foo\Bar\SpecificException', false, false],
['\Foo\Bar\Baz\SomeOtherException', false, false],
['callToFunction', true, false],
['FOO_CONSTANT', false, true],
['BAZ_CONSTANT', false, true],
['LoremClass', false, false],
['IpsumClass', false, false],
],
false,
],
[
[
['\ExtendedClass', false, false],
['\ImplementedInterface', false, false],
['\FullyQualified\SomeOtherTrait', false, false],
['SomeTrait', false, false],
['ORM\Column', false, false],
['Bar', false, false],
['Lorem', false, false],
['Ipsum', false, false],
['Rasmus', false, false],
['Lerdorf', false, false],
['\Foo\BarBaz', false, false],
['TypehintedName', false, false],
['AnotherTypehintedName', false, false],
['Returned_Typehinted_Underscored_Name', false, false],
['TypehintedName', false, false],
['ClassInstance', false, false],
['StaticClass', false, false],
['\Foo\Bar\SpecificException', false, false],
['\Foo\Bar\Baz\SomeOtherException', false, false],
['callToFunction', true, false],
['FOO_CONSTANT', false, true],
['BAZ_CONSTANT', false, true],
['LoremClass', false, false],
['IpsumClass', false, false],
],
true,
],
];
}

/**
* @dataProvider dataGetAllReferencedNames
* @param string[] $foundTypes
* @param boolean $searchAnnotations
*/
public function testGetAllReferencedNames(array $foundTypes, $searchAnnotations)
public function testGetAllReferencedNames()
{
$codeSnifferFile = $this->getCodeSnifferFile(
__DIR__ . '/data/lotsOfReferencedNames.php'
);

$names = ReferencedNameHelper::getAllReferencedNames($codeSnifferFile, 0, $searchAnnotations);
$foundTypes = [
['\ExtendedClass', false, false],
['\ImplementedInterface', false, false],
['\FullyQualified\SomeOtherTrait', false, false],
['SomeTrait', false, false],
['TypehintedName', false, false],
['ClassInstance', false, false],
['StaticClass', false, false],
['\Foo\Bar\SpecificException', false, false],
['\Foo\Bar\Baz\SomeOtherException', false, false],
['callToFunction', true, false],
['FOO_CONSTANT', false, true],
['BAZ_CONSTANT', false, true],
['LoremClass', false, false],
['IpsumClass', false, false],
];

$names = ReferencedNameHelper::getAllReferencedNames($codeSnifferFile, 0);
$this->assertCount(count($foundTypes), $names);
foreach ($names as $i => $referencedName) {
$this->assertSame($foundTypes[$i][0], $referencedName->getNameAsReferencedInFile());
Expand Down

0 comments on commit 9b5f363

Please sign in to comment.