-
Notifications
You must be signed in to change notification settings - Fork 51
Add custom sniffs to address spacing of operators #134
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
vendor/ | ||
.phpunit.result.cache | ||
phpcs.xml | ||
composer.lock | ||
tests/input2 | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Doctrine\Sniffs\Operators; | ||
|
||
use PHP_CodeSniffer\Files\File; | ||
use PHP_CodeSniffer\Sniffs\Sniff; | ||
use SlevomatCodingStandard\Helpers\TokenHelper; | ||
use function in_array; | ||
use function strlen; | ||
use const T_CLOSE_PARENTHESIS; | ||
use const T_CLOSE_SHORT_ARRAY; | ||
use const T_CLOSE_SQUARE_BRACKET; | ||
use const T_CONSTANT_ENCAPSED_STRING; | ||
use const T_DNUMBER; | ||
use const T_ENCAPSED_AND_WHITESPACE; | ||
use const T_LNUMBER; | ||
use const T_MINUS; | ||
use const T_NUM_STRING; | ||
use const T_STRING; | ||
use const T_VARIABLE; | ||
use const T_WHITESPACE; | ||
|
||
final class NegationOperatorSpacingSniff implements Sniff | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't we need to add this one to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sniffs from the directory of the standards ruleset.xml are automatically included as far as I know, at least that's how it is with our coding standard: https://github.com/cdn77/coding-standard/tree/master/src/Cdn77CodingStandard |
||
{ | ||
public const CODE_INVALID_SPACE_AFTER_MINUS = 'InvalidSpaceAfterMinus'; | ||
|
||
/** @var bool */ | ||
public $requireSpace = false; | ||
|
||
/** | ||
* {@inheritdoc} | ||
*/ | ||
public function register() : array | ||
{ | ||
return [T_MINUS]; | ||
} | ||
|
||
/** | ||
* @param int $pointer | ||
* | ||
* @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingParameterTypeHint | ||
*/ | ||
public function process(File $file, $pointer) : void | ||
{ | ||
$tokens = $file->getTokens(); | ||
|
||
$previousEffective = TokenHelper::findPreviousEffective($file, $pointer - 1); | ||
|
||
$possibleOperandTypes = [ | ||
T_CONSTANT_ENCAPSED_STRING, | ||
T_CLOSE_PARENTHESIS, | ||
T_CLOSE_SHORT_ARRAY, | ||
T_CLOSE_SQUARE_BRACKET, | ||
T_DNUMBER, | ||
T_ENCAPSED_AND_WHITESPACE, | ||
T_LNUMBER, | ||
T_NUM_STRING, | ||
T_STRING, | ||
T_VARIABLE, | ||
]; | ||
|
||
if (in_array($tokens[$previousEffective]['code'], $possibleOperandTypes, true)) { | ||
return; | ||
} | ||
|
||
$whitespacePointer = $pointer + 1; | ||
if (! isset($tokens[$whitespacePointer])) { | ||
return; | ||
} | ||
|
||
$numberOfSpaces = $this->numberOfSpaces($tokens[$whitespacePointer]); | ||
$expectedSpaces = $this->requireSpace ? 1 : 0; | ||
|
||
if ($numberOfSpaces === $expectedSpaces | ||
|| ! $this->recordError($file, $pointer, $tokens[$pointer], $expectedSpaces, $numberOfSpaces)) { | ||
return; | ||
} | ||
|
||
if ($expectedSpaces > $numberOfSpaces) { | ||
$file->fixer->addContent($pointer, ' '); | ||
|
||
return; | ||
} | ||
|
||
$file->fixer->replaceToken($whitespacePointer, ''); | ||
} | ||
|
||
/** | ||
* @param mixed[] $token | ||
*/ | ||
private function numberOfSpaces(array $token) : int | ||
{ | ||
if ($token['code'] !== T_WHITESPACE) { | ||
return 0; | ||
} | ||
|
||
return strlen($token['content']); | ||
} | ||
|
||
/** | ||
* @param mixed[] $token | ||
*/ | ||
private function recordError(File $file, int $pointer, array $token, int $expected, int $found) : bool | ||
{ | ||
return $file->addFixableError( | ||
'Expected exactly %d space after "%s"; %d found', | ||
$pointer, | ||
self::CODE_INVALID_SPACE_AFTER_MINUS, | ||
[$expected, $token['content'], $found] | ||
); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Doctrine\Sniffs\Operators; | ||
|
||
use PHP_CodeSniffer\Files\File; | ||
use PHP_CodeSniffer\Standards\Squiz\Sniffs\WhiteSpace\OperatorSpacingSniff as SquizOperatorSpacingSniff; | ||
use PHP_CodeSniffer\Util\Tokens; | ||
use function in_array; | ||
use const T_ARRAY_CAST; | ||
use const T_BOOL_CAST; | ||
use const T_DOUBLE_CAST; | ||
use const T_ECHO; | ||
use const T_INT_CAST; | ||
use const T_MINUS; | ||
use const T_OBJECT_CAST; | ||
use const T_PLUS; | ||
use const T_PRINT; | ||
use const T_STRING_CAST; | ||
use const T_UNSET_CAST; | ||
use const T_YIELD; | ||
|
||
/** | ||
* We need this sniff until Squiz accepts fix for unary operands detection | ||
* https://github.com/squizlabs/PHP_CodeSniffer/pull/2640 | ||
*/ | ||
final class OperatorSpacingSniff extends SquizOperatorSpacingSniff | ||
{ | ||
private const NON_OPERAND_TOKENS = [ | ||
T_ARRAY_CAST, | ||
T_BOOL_CAST, | ||
T_DOUBLE_CAST, | ||
T_ECHO, | ||
T_INT_CAST, | ||
T_OBJECT_CAST, | ||
T_PRINT, | ||
T_STRING_CAST, | ||
T_UNSET_CAST, | ||
T_YIELD, | ||
]; | ||
|
||
/** | ||
* @param int $pointer | ||
* | ||
* @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingParameterTypeHint | ||
*/ | ||
public function process(File $file, $pointer) : void | ||
{ | ||
if (! $this->isOperator($file, $pointer)) { | ||
return; | ||
} | ||
|
||
parent::process($file, $pointer); | ||
} | ||
|
||
/** | ||
* @param int $pointer | ||
* | ||
* @phpcsSuppress SlevomatCodingStandard.TypeHints.TypeHintDeclaration.MissingParameterTypeHint | ||
*/ | ||
protected function isOperator(File $file, $pointer) : bool | ||
{ | ||
if (! parent::isOperator($file, $pointer)) { | ||
return false; | ||
} | ||
|
||
$tokens = $file->getTokens(); | ||
if ($tokens[$pointer]['code'] === T_MINUS || $tokens[$pointer]['code'] === T_PLUS) { | ||
$prev = $file->findPrevious(Tokens::$emptyTokens, $pointer - 1, null, true); | ||
if (in_array($tokens[$prev]['code'], self::NON_OPERAND_TOKENS, true)) { | ||
return false; | ||
} | ||
} | ||
|
||
return true; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<?xml version="1.0"?> | ||
<phpunit | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd" | ||
beStrictAboutChangesToGlobalState="true" | ||
beStrictAboutOutputDuringTests="true" | ||
beStrictAboutTodoAnnotatedTests="true" | ||
colors="true" | ||
bootstrap="tests/bootstrap.php" | ||
executionOrder="random" | ||
> | ||
grongor marked this conversation as resolved.
Show resolved
Hide resolved
|
||
<testsuite name="Doctrine Coding Standard"> | ||
<directory>tests</directory> | ||
</testsuite> | ||
<filter> | ||
<whitelist> | ||
<directory suffix=".php">src/</directory> | ||
</whitelist> | ||
</filter> | ||
</phpunit> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Doctrine\Tests\Sniffs\Operators; | ||
|
||
use Doctrine\Tests\TestCase; | ||
|
||
class NegationOperatorSpacingSniffTest extends TestCase | ||
{ | ||
public function testNoErrors() : void | ||
{ | ||
self::assertNoSniffErrorInFile(self::checkFile(__DIR__ . '/data/NegationOperatorSpacingSniffNoErrors.php')); | ||
} | ||
|
||
public function testErrors() : void | ||
{ | ||
$file = self::checkFile( | ||
__DIR__ . '/data/NegationOperatorSpacingSniffRequireSpaceErrors.php', | ||
['requireSpace' => true] | ||
); | ||
|
||
self::assertSame(97, $file->getErrorCount()); | ||
|
||
self::assertAllFixedInFile($file); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Doctrine\Tests\Sniffs\Operators; | ||
|
||
use Doctrine\Tests\TestCase; | ||
|
||
class OperatorSpacingSniffTest extends TestCase | ||
{ | ||
public function testNoErrors() : void | ||
{ | ||
self::assertNoSniffErrorInFile( | ||
self::checkFile( | ||
__DIR__ . '/data/OperatorSpacingSniffNoErrors.php', | ||
['ignoreSpacingBeforeAssignments' => false] | ||
) | ||
); | ||
} | ||
|
||
public function testErrors() : void | ||
{ | ||
$file = self::checkFile( | ||
__DIR__ . '/data/OperatorSpacingSniffErrors.php', | ||
['ignoreSpacingBeforeAssignments' => false] | ||
); | ||
|
||
self::assertSame(67, $file->getErrorCount()); | ||
|
||
self::assertAllFixedInFile($file); | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.