Skip to content

Commit

Permalink
Fixable ReferenceThrowableOnlySniff
Browse files Browse the repository at this point in the history
  • Loading branch information
kukulich committed Feb 4, 2017
1 parent 946a6d1 commit 1b1b75d
Show file tree
Hide file tree
Showing 11 changed files with 196 additions and 19 deletions.
19 changes: 14 additions & 5 deletions SlevomatCodingStandard/Helpers/ReferencedName.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,19 @@ class ReferencedName
private $nameAsReferencedInFile;

/** @var int */
private $pointer;
private $startPointer;

/** @var int */
private $endPointer;

/** @var string */
private $type;

public function __construct(string $nameAsReferencedInFile, int $pointer, string $type)
public function __construct(string $nameAsReferencedInFile, int $startPointer, int $endPointer, string $type)
{
$this->nameAsReferencedInFile = $nameAsReferencedInFile;
$this->pointer = $pointer;
$this->startPointer = $startPointer;
$this->endPointer = $endPointer;
$this->type = $type;
}

Expand All @@ -30,9 +34,14 @@ public function getNameAsReferencedInFile(): string
return $this->nameAsReferencedInFile;
}

public function getPointer(): int
public function getStartPointer(): int
{
return $this->startPointer;
}

public function getEndPointer(): int
{
return $this->pointer;
return $this->endPointer;
}

public function isConstant(): bool
Expand Down
2 changes: 1 addition & 1 deletion SlevomatCodingStandard/Helpers/ReferencedNameHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ private static function createAllReferencedNames(\PHP_CodeSniffer_File $phpcsFil
}
}
}
$types[] = new ReferencedName(TokenHelper::getContent($phpcsFile, $nameStartPointer, $nameEndPointer), $nameStartPointer, $type);
$types[] = new ReferencedName(TokenHelper::getContent($phpcsFile, $nameStartPointer, $nameEndPointer), $nameStartPointer, $nameEndPointer, $type);
$beginSearchAtPointer = $nameEndPointer + 1;
}
return $types;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,12 @@ public function process(\PHP_CodeSniffer_File $phpcsFile, $openTagPointer)
$phpcsFile,
$referencedName->getNameAsReferencedInFile(),
$useStatements,
$referencedName->getPointer()
$referencedName->getStartPointer()
);
if ($resolvedName !== '\\Exception') {
continue;
}
$previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $referencedName->getPointer() - 1);
$previousPointer = TokenHelper::findPreviousEffective($phpcsFile, $referencedName->getStartPointer() - 1);
if (in_array($tokens[$previousPointer]['code'], [T_EXTENDS, T_NEW, T_INSTANCEOF], true)) {
continue; // allow \Exception in extends and instantiating it
}
Expand All @@ -65,7 +65,7 @@ public function process(\PHP_CodeSniffer_File $phpcsFile, $openTagPointer)
}
$phpcsFile->addError(
$message,
$referencedName->getPointer(),
$referencedName->getStartPointer(),
self::CODE_REFERENCED_GENERAL_EXCEPTION
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public function process(\PHP_CodeSniffer_File $phpcsFile, $openTagPointer)
$referencedNames = ReferencedNameHelper::getAllReferencedNames($phpcsFile, $openTagPointer);
$useStatements = UseStatementHelper::getUseStatements($phpcsFile, $openTagPointer);
foreach ($referencedNames as $referencedName) {
$pointer = $referencedName->getPointer();
$pointer = $referencedName->getStartPointer();
$name = $referencedName->getNameAsReferencedInFile();
$normalizedName = UseStatement::normalizedNameAsReferencedInFile($name);
if (isset($useStatements[$normalizedName]) && $referencedName->hasSameUseStatementType($useStatements[$normalizedName])) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use SlevomatCodingStandard\Helpers\SniffSettingsHelper;
use SlevomatCodingStandard\Helpers\StringHelper;
use SlevomatCodingStandard\Helpers\TokenHelper;
use SlevomatCodingStandard\Helpers\UseStatementHelper;

class ReferenceUsedNamesOnlySniff implements \PHP_CodeSniffer_Sniff
{
Expand Down Expand Up @@ -122,10 +123,11 @@ private function getFullyQualifiedKeywords(): array
public function process(\PHP_CodeSniffer_File $phpcsFile, $openTagPointer)
{
$tokens = $phpcsFile->getTokens();

$referencedNames = ReferencedNameHelper::getAllReferencedNames($phpcsFile, $openTagPointer);
foreach ($referencedNames as $referencedName) {
$name = $referencedName->getNameAsReferencedInFile();
$pointer = $referencedName->getPointer();
$nameStartPointer = $referencedName->getStartPointer();
$canonicalName = NamespaceHelper::normalizeToCanonicalName($name);
if (NamespaceHelper::isFullyQualifiedName($name)) {
$isExceptionByName = StringHelper::endsWith($name, 'Exception')
Expand All @@ -144,23 +146,61 @@ public function process(\PHP_CodeSniffer_File $phpcsFile, $openTagPointer)
$previousKeywordPointer = TokenHelper::findPreviousExcluding($phpcsFile, array_merge(
TokenHelper::$nameTokenCodes,
[T_WHITESPACE, T_COMMA]
), $pointer - 1);
), $nameStartPointer - 1);
if (
!in_array($tokens[$previousKeywordPointer]['code'], $this->getFullyQualifiedKeywords(), true)
) {
if (
!NamespaceHelper::hasNamespace($name)
&& NamespaceHelper::findCurrentNamespaceName($phpcsFile, $pointer) === null
&& NamespaceHelper::findCurrentNamespaceName($phpcsFile, $nameStartPointer) === null
) {
$phpcsFile->addError(sprintf(
$fix = $phpcsFile->addFixableError(sprintf(
'Type %s should not be referenced via a fully qualified name, but via an unqualified name without the leading \\, because the file does not have a namespace and the type cannot be put in a use statement',
$name
), $pointer, self::CODE_REFERENCE_VIA_FULLY_QUALIFIED_NAME_WITHOUT_NAMESPACE);
), $nameStartPointer, self::CODE_REFERENCE_VIA_FULLY_QUALIFIED_NAME_WITHOUT_NAMESPACE);
if ($fix) {
$phpcsFile->fixer->beginChangeset();
$phpcsFile->fixer->replaceToken($nameStartPointer, substr($tokens[$nameStartPointer]['content'], 1));
$phpcsFile->fixer->endChangeset();
}
} else {
$phpcsFile->addError(sprintf(
$fix = $phpcsFile->addFixableError(sprintf(
'Type %s should not be referenced via a fully qualified name, but via a use statement',
$name
), $pointer, self::CODE_REFERENCE_VIA_FULLY_QUALIFIED_NAME);
), $nameStartPointer, self::CODE_REFERENCE_VIA_FULLY_QUALIFIED_NAME);
if ($fix) {
$useStatements = UseStatementHelper::getUseStatements($phpcsFile, $openTagPointer);
if (count($useStatements) === 0) {
$namespacePointer = $phpcsFile->findNext(T_NAMESPACE, $openTagPointer);
$useStatementPlacePointer = $phpcsFile->findNext([T_SEMICOLON, T_OPEN_CURLY_BRACKET], $namespacePointer + 1);
} else {
$lastUseStatement = array_values($useStatements)[count($useStatements) - 1];
$useStatementPlacePointer = $phpcsFile->findNext(T_SEMICOLON, $lastUseStatement->getPointer() + 1);
}

$phpcsFile->fixer->beginChangeset();

for ($i = $referencedName->getStartPointer(); $i <= $referencedName->getEndPointer(); $i++) {
$phpcsFile->fixer->replaceToken($i, '');
}

$phpcsFile->fixer->addContent($referencedName->getStartPointer(), NamespaceHelper::getUnqualifiedNameFromFullyQualifiedName($name));

$alreadyUsed = false;
foreach ($useStatements as $useStatement) {
if ($useStatement->getFullyQualifiedTypeName() === $canonicalName) {
$alreadyUsed = true;
break;
}
}

if (!$alreadyUsed) {
$phpcsFile->fixer->addNewline($useStatementPlacePointer);
$phpcsFile->fixer->addContent($useStatementPlacePointer, sprintf('use %s;', $canonicalName));
}

$phpcsFile->fixer->endChangeset();
}
}
}
}
Expand All @@ -169,7 +209,7 @@ public function process(\PHP_CodeSniffer_File $phpcsFile, $openTagPointer)
$phpcsFile->addError(sprintf(
'Partial use statements are not allowed, but referencing %s found',
$name
), $pointer, self::CODE_PARTIAL_USE);
), $nameStartPointer, self::CODE_PARTIAL_USE);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public function process(\PHP_CodeSniffer_File $phpcsFile, $openTagPointer)

foreach ($referencedNames as $referencedName) {
$name = $referencedName->getNameAsReferencedInFile();
$pointer = $referencedName->getPointer();
$pointer = $referencedName->getStartPointer();
$nameParts = NamespaceHelper::getNameParts($name);
$nameAsReferencedInFile = $nameParts[0];
$normalizedNameAsReferencedInFile = UseStatement::normalizedNameAsReferencedInFile($nameAsReferencedInFile);
Expand Down
22 changes: 22 additions & 0 deletions tests/Sniffs/Namespaces/ReferenceUsedNamesOnlySniffTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -529,4 +529,26 @@ public function testIgnoredNamesWithAllowFullyQualifiedExceptionsInNamespace()
$this->assertNoSniffErrorInFile($report);
}


public function testFixableReferenceViaFullyQualifiedName()
{
$report = $this->checkFile(__DIR__ . '/data/fixableReferenceViaFullyQualifiedName.php', [
'fullyQualifiedKeywords' => ['T_EXTENDS'],
'allowFullyQualifiedExceptions' => true,
], [ReferenceUsedNamesOnlySniff::CODE_REFERENCE_VIA_FULLY_QUALIFIED_NAME]);
$this->assertAllFixedInFile($report);
}

public function testFixableReferenceViaFullyQualifiedNameWithoutNamespace()
{
$report = $this->checkFile(__DIR__ . '/data/fixableReferenceViaFullyQualifiedNameWithoutNamespace.php', [
'fullyQualifiedKeywords' => ['T_IMPLEMENTS'],
'allowFullyQualifiedExceptions' => false,
'specialExceptionNames' => [
'BarErrorX',
],
], [ReferenceUsedNamesOnlySniff::CODE_REFERENCE_VIA_FULLY_QUALIFIED_NAME_WITHOUT_NAMESPACE]);
$this->assertAllFixedInFile($report);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

namespace Foo\Test\Bla;
use Iterator;
use Some\ConstantClass;
use Foo\SomeError;
use Nette\Object;

class Bar extends \Object implements Iterator
{

public function bar()
{
new Lorem();
$constant = ConstantClass::CONSTANT;
new SomeError();
new \Some\CommonException();
new \Exception();
new Object();
}

public function foo(): Object
{
return new Object();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace Foo\Test\Bla;

class Bar extends \Object implements \Iterator
{

public function bar()
{
new Lorem();
$constant = \Some\ConstantClass::CONSTANT;
new \Foo\SomeError();
new \Some\CommonException();
new \Exception();
new \Nette\Object();
}

public function foo(): \Nette\Object
{
return new \Nette\Object();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

function bar(\Some\Exception $e)
{
try {
throw new \Some\Other\Exception();
} catch (\Some\Other\DifferentException $ex) {

} catch (Throwable $ex) {

} catch (Exception $ex) {

} catch (TypeError $ex) {

} catch (BarErrorX $ex) {

}
}

class Lorem implements \Dolor, \Amet
{

}

class Ipsum extends Bar
{

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

function bar(\Some\Exception $e)
{
try {
throw new \Some\Other\Exception();
} catch (\Some\Other\DifferentException $ex) {

} catch (\Throwable $ex) {

} catch (\Exception $ex) {

} catch (\TypeError $ex) {

} catch (\BarErrorX $ex) {

}
}

class Lorem implements \Dolor, \Amet
{

}

class Ipsum extends \Bar
{

}

0 comments on commit 1b1b75d

Please sign in to comment.