diff --git a/.gitignore b/.gitignore index 2a76e0ea..b453ada9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ vendor/ +.phpunit.result.cache phpcs.xml composer.lock tests/input2 diff --git a/.travis.yml b/.travis.yml index 9d68b004..d5626ddd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,6 +21,7 @@ install: travis_retry composer update --prefer-dist script: - vendor/bin/phpcs - vendor/bin/phpcs $(find tests/input/* | sort) --report=summary --report-file=phpcs.log; diff tests/expected_report.txt phpcs.log + - vendor/bin/phpunit stages: - Validate against schema diff --git a/composer.json b/composer.json index f195a9aa..04283f5c 100644 --- a/composer.json +++ b/composer.json @@ -25,12 +25,20 @@ "Doctrine\\Sniffs\\": "lib/Doctrine/Sniffs" } }, + "autoload-dev": { + "psr-4": { + "Doctrine\\Tests\\": "tests/" + } + }, "require": { "php": "^7.2", "dealerdirect/phpcodesniffer-composer-installer": "^0.5.0", "slevomat/coding-standard": "dev-master#63a8186b129ee96d1277e68c80cf87c8cdb356d1", "squizlabs/php_codesniffer": "^3.5.0" }, + "require-dev": { + "phpunit/phpunit": "^8.1" + }, "config": { "sort-packages": true }, diff --git a/lib/Doctrine/Sniffs/Operators/NegationOperatorSpacingSniff.php b/lib/Doctrine/Sniffs/Operators/NegationOperatorSpacingSniff.php new file mode 100644 index 00000000..c6c1c227 --- /dev/null +++ b/lib/Doctrine/Sniffs/Operators/NegationOperatorSpacingSniff.php @@ -0,0 +1,114 @@ +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] + ); + } +} diff --git a/lib/Doctrine/Sniffs/Operators/OperatorSpacingSniff.php b/lib/Doctrine/Sniffs/Operators/OperatorSpacingSniff.php new file mode 100644 index 00000000..3ab35deb --- /dev/null +++ b/lib/Doctrine/Sniffs/Operators/OperatorSpacingSniff.php @@ -0,0 +1,78 @@ +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; + } +} diff --git a/lib/Doctrine/ruleset.xml b/lib/Doctrine/ruleset.xml index cc7b04a8..7823be15 100644 --- a/lib/Doctrine/ruleset.xml +++ b/lib/Doctrine/ruleset.xml @@ -16,6 +16,12 @@ + + + + + + diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 00000000..75c01700 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,20 @@ + + + + tests + + + + src/ + + + diff --git a/tests/Sniffs/Operators/NegationOperatorSpacingSniffTest.php b/tests/Sniffs/Operators/NegationOperatorSpacingSniffTest.php new file mode 100644 index 00000000..9bc7f09f --- /dev/null +++ b/tests/Sniffs/Operators/NegationOperatorSpacingSniffTest.php @@ -0,0 +1,27 @@ + true] + ); + + self::assertSame(97, $file->getErrorCount()); + + self::assertAllFixedInFile($file); + } +} diff --git a/tests/Sniffs/Operators/OperatorSpacingSniffTest.php b/tests/Sniffs/Operators/OperatorSpacingSniffTest.php new file mode 100644 index 00000000..07812b87 --- /dev/null +++ b/tests/Sniffs/Operators/OperatorSpacingSniffTest.php @@ -0,0 +1,32 @@ + false] + ) + ); + } + + public function testErrors() : void + { + $file = self::checkFile( + __DIR__ . '/data/OperatorSpacingSniffErrors.php', + ['ignoreSpacingBeforeAssignments' => false] + ); + + self::assertSame(67, $file->getErrorCount()); + + self::assertAllFixedInFile($file); + } +} diff --git a/tests/Sniffs/Operators/data/NegationOperatorSpacingSniffNoErrors.php b/tests/Sniffs/Operators/data/NegationOperatorSpacingSniffNoErrors.php new file mode 100644 index 00000000..0dd6a23f --- /dev/null +++ b/tests/Sniffs/Operators/data/NegationOperatorSpacingSniffNoErrors.php @@ -0,0 +1,108 @@ + -16 || $a > -$b || $a > -self::CONSTANT) { + +} + + +yield -1; +echo -1; +$a = -1; +func(-1); +$a = [-1]; +return -1; +print -1; +$a &= -1; +switch ($a) { + case -1: +} +$a = $a ?? -1; +$a .= -1; +$a /= -1; +$a = [1 => -1]; +$a = $a == -1; +$a = $a >= -1; +$a = $a === -1; +$a = $a != -1; +$a = $a !== -1; +$a = $a <= -1; +$a = $a <=> -1; +$a = $a and -1; +$a = $a or -1; +$a = $a xor -1; +$a -= -1; +$a %= -1; +$a *= -1; +$a |= -1; +$a += -1; +$a = $a ** -1; +$a **= -1; +$a = $a << -1; +$a <<= -1; +$a = $a >> -1; +$a >>= -1; +$a = (string) -1; +$a = (array) -1; +$a = (bool) -1; +$a = (object) -1; +$a = (unset) -1; +$a = (float) -1; +$a = (int) -1; +$a ^= -1; +$a = [$a, -1]; +$a = $a[-1]; +$a = $a ? -1 : -1; + + +yield -$b; +echo -$b; +$a = -$b; +func(-$b); +$a = [-$b]; +return -$b; +print -$b; +$a &= -$b; +switch ($a) { + case -$b: +} +$a = $a ?? -$b; +$a .= -$b; +$a /= -$b; +$a = [1 => -$b]; +$a = $a == -$b; +$a = $a >= -$b; +$a = $a === -$b; +$a = $a != -$b; +$a = $a !== -$b; +$a = $a <= -$b; +$a = $a <=> -$b; +$a = $a and -$b; +$a = $a or -$b; +$a = $a xor -$b; +$a -= -$b; +$a %= -$b; +$a *= -$b; +$a |= -$b; +$a += -$b; +$a = $a ** -$b; +$a **= -$b; +$a = $a << -$b; +$a <<= -$b; +$a = $a >> -$b; +$a >>= -$b; +$a = (string) -$b; +$a = (array) -$b; +$a = (bool) -$b; +$a = (object) -$b; +$a = (unset) -$b; +$a = (float) -$b; +$a = (int) -$b; +$a ^= -$b; +$a = [$a, -$b]; +$a = $a[-$b]; +$a = $a ? -$b : -$b; diff --git a/tests/Sniffs/Operators/data/NegationOperatorSpacingSniffRequireSpaceErrors.fixed.php b/tests/Sniffs/Operators/data/NegationOperatorSpacingSniffRequireSpaceErrors.fixed.php new file mode 100644 index 00000000..991f175f --- /dev/null +++ b/tests/Sniffs/Operators/data/NegationOperatorSpacingSniffRequireSpaceErrors.fixed.php @@ -0,0 +1,108 @@ + - 16 || $a > - $b || $a > - self::CONSTANT) { + +} + + +yield - 1; +echo - 1; +$a = - 1; +func(- 1); +$a = [- 1]; +return - 1; +print - 1; +$a &= - 1; +switch ($a) { + case - 1: +} +$a = $a ?? - 1; +$a .= - 1; +$a /= - 1; +$a = [1 => - 1]; +$a = $a == - 1; +$a = $a >= - 1; +$a = $a === - 1; +$a = $a != - 1; +$a = $a !== - 1; +$a = $a <= - 1; +$a = $a <=> - 1; +$a = $a and - 1; +$a = $a or - 1; +$a = $a xor - 1; +$a -= - 1; +$a %= - 1; +$a *= - 1; +$a |= - 1; +$a += - 1; +$a = $a ** - 1; +$a **= - 1; +$a = $a << - 1; +$a <<= - 1; +$a = $a >> - 1; +$a >>= - 1; +$a = (string) - 1; +$a = (array) - 1; +$a = (bool) - 1; +$a = (object) - 1; +$a = (unset) - 1; +$a = (float) - 1; +$a = (int) - 1; +$a ^= - 1; +$a = [$a, - 1]; +$a = $a[- 1]; +$a = $a ? - 1 : - 1; + + +yield - $b; +echo - $b; +$a = - $b; +func(- $b); +$a = [- $b]; +return - $b; +print - $b; +$a &= - $b; +switch ($a) { + case - $b: +} +$a = $a ?? - $b; +$a .= - $b; +$a /= - $b; +$a = [1 => - $b]; +$a = $a == - $b; +$a = $a >= - $b; +$a = $a === - $b; +$a = $a != - $b; +$a = $a !== - $b; +$a = $a <= - $b; +$a = $a <=> - $b; +$a = $a and - $b; +$a = $a or - $b; +$a = $a xor - $b; +$a -= - $b; +$a %= - $b; +$a *= - $b; +$a |= - $b; +$a += - $b; +$a = $a ** - $b; +$a **= - $b; +$a = $a << - $b; +$a <<= - $b; +$a = $a >> - $b; +$a >>= - $b; +$a = (string) - $b; +$a = (array) - $b; +$a = (bool) - $b; +$a = (object) - $b; +$a = (unset) - $b; +$a = (float) - $b; +$a = (int) - $b; +$a ^= - $b; +$a = [$a, - $b]; +$a = $a[- $b]; +$a = $a ? - $b : - $b; diff --git a/tests/Sniffs/Operators/data/NegationOperatorSpacingSniffRequireSpaceErrors.php b/tests/Sniffs/Operators/data/NegationOperatorSpacingSniffRequireSpaceErrors.php new file mode 100644 index 00000000..0dd6a23f --- /dev/null +++ b/tests/Sniffs/Operators/data/NegationOperatorSpacingSniffRequireSpaceErrors.php @@ -0,0 +1,108 @@ + -16 || $a > -$b || $a > -self::CONSTANT) { + +} + + +yield -1; +echo -1; +$a = -1; +func(-1); +$a = [-1]; +return -1; +print -1; +$a &= -1; +switch ($a) { + case -1: +} +$a = $a ?? -1; +$a .= -1; +$a /= -1; +$a = [1 => -1]; +$a = $a == -1; +$a = $a >= -1; +$a = $a === -1; +$a = $a != -1; +$a = $a !== -1; +$a = $a <= -1; +$a = $a <=> -1; +$a = $a and -1; +$a = $a or -1; +$a = $a xor -1; +$a -= -1; +$a %= -1; +$a *= -1; +$a |= -1; +$a += -1; +$a = $a ** -1; +$a **= -1; +$a = $a << -1; +$a <<= -1; +$a = $a >> -1; +$a >>= -1; +$a = (string) -1; +$a = (array) -1; +$a = (bool) -1; +$a = (object) -1; +$a = (unset) -1; +$a = (float) -1; +$a = (int) -1; +$a ^= -1; +$a = [$a, -1]; +$a = $a[-1]; +$a = $a ? -1 : -1; + + +yield -$b; +echo -$b; +$a = -$b; +func(-$b); +$a = [-$b]; +return -$b; +print -$b; +$a &= -$b; +switch ($a) { + case -$b: +} +$a = $a ?? -$b; +$a .= -$b; +$a /= -$b; +$a = [1 => -$b]; +$a = $a == -$b; +$a = $a >= -$b; +$a = $a === -$b; +$a = $a != -$b; +$a = $a !== -$b; +$a = $a <= -$b; +$a = $a <=> -$b; +$a = $a and -$b; +$a = $a or -$b; +$a = $a xor -$b; +$a -= -$b; +$a %= -$b; +$a *= -$b; +$a |= -$b; +$a += -$b; +$a = $a ** -$b; +$a **= -$b; +$a = $a << -$b; +$a <<= -$b; +$a = $a >> -$b; +$a >>= -$b; +$a = (string) -$b; +$a = (array) -$b; +$a = (bool) -$b; +$a = (object) -$b; +$a = (unset) -$b; +$a = (float) -$b; +$a = (int) -$b; +$a ^= -$b; +$a = [$a, -$b]; +$a = $a[-$b]; +$a = $a ? -$b : -$b; diff --git a/tests/Sniffs/Operators/data/OperatorSpacingSniffErrors.fixed.php b/tests/Sniffs/Operators/data/OperatorSpacingSniffErrors.fixed.php new file mode 100644 index 00000000..fe71eb67 --- /dev/null +++ b/tests/Sniffs/Operators/data/OperatorSpacingSniffErrors.fixed.php @@ -0,0 +1,42 @@ + $b; +$a >= $b; +$a < $b; +$a <= $b; +$a != $b; + +$a = $b; +$a === $b; +$a > $b; +$a >= $b; +$a < $b; +$a <= $b; +$a != $b; + +$a = $a + $b; +$a = $a - $b; +$a = $a / $b; +$a = $a * $b; +$a = $a + $b; +$a = $a ^ $b; + +$a = $a + $b; +$a = $a - $b; +$a = $a / $b; +$a = $a * $b; +$a = $a + $b; +$a = $a ^ $b; + +$a = self::CONST - $a; + +$a += $b; +$a -= $b; + +$a += $b; +$a -= $b; + +$a = ($a) instanceof $b; +$a = $a instanceof $b; diff --git a/tests/Sniffs/Operators/data/OperatorSpacingSniffErrors.php b/tests/Sniffs/Operators/data/OperatorSpacingSniffErrors.php new file mode 100644 index 00000000..774a056c --- /dev/null +++ b/tests/Sniffs/Operators/data/OperatorSpacingSniffErrors.php @@ -0,0 +1,42 @@ +$b; +$a>=$b; +$a<$b; +$a<=$b; +$a!=$b; + +$a = $b; +$a === $b; +$a > $b; +$a >= $b; +$a < $b; +$a <= $b; +$a != $b; + +$a = $a+$b; +$a = $a-$b; +$a = $a/$b; +$a = $a*$b; +$a = $a+$b; +$a = $a^$b; + +$a = $a + $b; +$a = $a - $b; +$a = $a / $b; +$a = $a * $b; +$a = $a + $b; +$a = $a ^ $b; + +$a = self::CONST- $a; + +$a+=$b; +$a-=$b; + +$a += $b; +$a -= $b; + +$a = ($a)instanceof$b; +$a = $a instanceof $b; diff --git a/tests/Sniffs/Operators/data/OperatorSpacingSniffNoErrors.php b/tests/Sniffs/Operators/data/OperatorSpacingSniffNoErrors.php new file mode 100644 index 00000000..61a110d0 --- /dev/null +++ b/tests/Sniffs/Operators/data/OperatorSpacingSniffNoErrors.php @@ -0,0 +1,194 @@ + -1]; +$a = $a == -1; +$a = $a >= -1; +$a = $a === -1; +$a = $a != -1; +$a = $a !== -1; +$a = $a <= -1; +$a = $a <=> -1; +$a = $a and -1; +$a = $a or -1; +$a = $a xor -1; +$a -= -1; +$a %= -1; +$a *= -1; +$a |= -1; +$a += -1; +$a = $a ** -1; +$a **= -1; +$a = $a << -1; +$a <<= -1; +$a = $a >> -1; +$a >>= -1; +$a = (string) -1; +$a = (array) -1; +$a = (bool) -1; +$a = (object) -1; +$a = (unset) -1; +$a = (float) -1; +$a = (int) -1; +$a ^= -1; +$a = [$a, -1]; +$a = $a[-1]; +$a = $a ? -1 : -1; + +yield - 1; +echo - 1; +$a = - 1; +func(- 1); +$a = [- 1]; +return - 1; +print - 1; +$a &= - 1; +switch ($a) { + case - 1: +} +$a = $a ?? - 1; +$a .= - 1; +$a /= - 1; +$a = [1 => - 1]; +$a = $a == - 1; +$a = $a >= - 1; +$a = $a === - 1; +$a = $a != - 1; +$a = $a !== - 1; +$a = $a <= - 1; +$a = $a <=> - 1; +$a = $a and - 1; +$a = $a or - 1; +$a = $a xor - 1; +$a -= - 1; +$a %= - 1; +$a *= - 1; +$a |= - 1; +$a += - 1; +$a = $a ** - 1; +$a **= - 1; +$a = $a << - 1; +$a <<= - 1; +$a = $a >> - 1; +$a >>= - 1; +$a = (string) - 1; +$a = (array) - 1; +$a = (bool) - 1; +$a = (object) - 1; +$a = (unset) - 1; +$a = (float) - 1; +$a = (int) - 1; +$a ^= - 1; +$a = [$a, - 1]; +$a = $a[- 1]; +$a = $a ? - 1 : - 1; + + +yield -$b; +echo -$b; +$a = -$b; +func(-$b); +$a = [-$b]; +return -$b; +print -$b; +$a &= -$b; +switch ($a) { + case -$b: +} +$a = $a ?? -$b; +$a .= -$b; +$a /= -$b; +$a = [1 => -$b]; +$a = $a == -$b; +$a = $a >= -$b; +$a = $a === -$b; +$a = $a != -$b; +$a = $a !== -$b; +$a = $a <= -$b; +$a = $a <=> -$b; +$a = $a and -$b; +$a = $a or -$b; +$a = $a xor -$b; +$a -= -$b; +$a %= -$b; +$a *= -$b; +$a |= -$b; +$a += -$b; +$a = $a ** -$b; +$a **= -$b; +$a = $a << -$b; +$a <<= -$b; +$a = $a >> -$b; +$a >>= -$b; +$a = (string) -$b; +$a = (array) -$b; +$a = (bool) -$b; +$a = (object) -$b; +$a = (unset) -$b; +$a = (float) -$b; +$a = (int) -$b; +$a ^= -$b; +$a = [$a, -$b]; +$a = $a[-$b]; +$a = $a ? -$b : -$b; + +yield - $b; +echo - $b; +$a = - $b; +func(- $b); +$a = [- $b]; +return - $b; +print - $b; +$a &= - $b; +switch ($a) { + case - $b: +} +$a = $a ?? - $b; +$a .= - $b; +$a /= - $b; +$a = [1 => - $b]; +$a = $a == - $b; +$a = $a >= - $b; +$a = $a === - $b; +$a = $a != - $b; +$a = $a !== - $b; +$a = $a <= - $b; +$a = $a <=> - $b; +$a = $a and - $b; +$a = $a or - $b; +$a = $a xor - $b; +$a -= - $b; +$a %= - $b; +$a *= - $b; +$a |= - $b; +$a += - $b; +$a = $a ** - $b; +$a **= - $b; +$a = $a << - $b; +$a <<= - $b; +$a = $a >> - $b; +$a >>= - $b; +$a = (string) - $b; +$a = (array) - $b; +$a = (bool) - $b; +$a = (object) - $b; +$a = (unset) - $b; +$a = (float) - $b; +$a = (int) - $b; +$a ^= - $b; +$a = [$a, - $b]; +$a = $a[- $b]; +$a = $a ? - $b : - $b; diff --git a/tests/TestCase.php b/tests/TestCase.php new file mode 100644 index 00000000..adb2dc28 --- /dev/null +++ b/tests/TestCase.php @@ -0,0 +1,18 @@ + 'value', 'key2' => 'value', ]; diff --git a/tests/input/trailing_comma_on_array.php b/tests/input/trailing_comma_on_array.php index aeb1a729..3b39e357 100644 --- a/tests/input/trailing_comma_on_array.php +++ b/tests/input/trailing_comma_on_array.php @@ -2,7 +2,7 @@ declare(strict_types=1); -$array = [ +$array = [ 'key1' => 'value', 'key2' => 'value' ];