Skip to content

Commit

Permalink
UseSpacingSniff: Don't report errors for use statements with comments…
Browse files Browse the repository at this point in the history
… right before or after
  • Loading branch information
kukulich committed May 17, 2019
1 parent 2293ce5 commit 14fbbf5
Show file tree
Hide file tree
Showing 9 changed files with 259 additions and 28 deletions.
24 changes: 22 additions & 2 deletions SlevomatCodingStandard/Helpers/CommentHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
namespace SlevomatCodingStandard\Helpers;

use PHP_CodeSniffer\Files\File;
use const T_COMMENT;

/**
* @internal
Expand All @@ -17,7 +16,7 @@ public static function getMultilineCommentStartPointer(File $phpcsFile, int $com

$commentStartPointer = $commentEndPointer;
do {
$commentBefore = TokenHelper::findPrevious($phpcsFile, T_COMMENT, $commentStartPointer - 1);
$commentBefore = TokenHelper::findPrevious($phpcsFile, TokenHelper::$inlineCommentTokenCodes, $commentStartPointer - 1);
if ($commentBefore === null) {
break;
}
Expand All @@ -32,4 +31,25 @@ public static function getMultilineCommentStartPointer(File $phpcsFile, int $com
return $commentStartPointer;
}

public static function getMultilineCommentEndPointer(File $phpcsFile, int $commentStartPointer): int
{
$tokens = $phpcsFile->getTokens();

$commentEndPointer = $commentStartPointer;
do {
$commentAfter = TokenHelper::findNext($phpcsFile, TokenHelper::$inlineCommentTokenCodes, $commentEndPointer + 1);
if ($commentAfter === null) {
break;
}
if ($tokens[$commentAfter]['line'] - 1 !== $tokens[$commentEndPointer]['line']) {
break;
}

/** @var int $commentEndPointer */
$commentEndPointer = $commentAfter;
} while (true);

return $commentEndPointer;
}

}
98 changes: 72 additions & 26 deletions SlevomatCodingStandard/Sniffs/Namespaces/UseSpacingSniff.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@

use PHP_CodeSniffer\Files\File;
use PHP_CodeSniffer\Sniffs\Sniff;
use PHP_CodeSniffer\Util\Tokens;
use SlevomatCodingStandard\Helpers\CommentHelper;
use SlevomatCodingStandard\Helpers\SniffSettingsHelper;
use SlevomatCodingStandard\Helpers\TokenHelper;
use SlevomatCodingStandard\Helpers\UseStatement;
use SlevomatCodingStandard\Helpers\UseStatementHelper;
use function array_key_exists;
use function array_values;
use function count;
use function in_array;
use function sprintf;
use function strlen;
use function substr;
use function substr_count;
use const T_OPEN_TAG;
use const T_SEMICOLON;
use const T_WHITESPACE;
Expand Down Expand Up @@ -75,19 +76,21 @@ private function checkLinesBeforeFirstUse(File $phpcsFile, UseStatement $firstUs

/** @var int $pointerBeforeFirstUse */
$pointerBeforeFirstUse = TokenHelper::findPreviousExcluding($phpcsFile, T_WHITESPACE, $firstUse->getPointer() - 1);

$whitespaceBeforeFirstUse = '';

if ($tokens[$pointerBeforeFirstUse]['code'] === T_OPEN_TAG) {
$whitespaceBeforeFirstUse .= substr($tokens[$pointerBeforeFirstUse]['content'], strlen('<?php'));
}

if ($pointerBeforeFirstUse + 1 !== $firstUse->getPointer()) {
$whitespaceBeforeFirstUse .= TokenHelper::getContent($phpcsFile, $pointerBeforeFirstUse + 1, $firstUse->getPointer() - 1);
$useStartPointer = $firstUse->getPointer();

if (
in_array($tokens[$pointerBeforeFirstUse]['code'], Tokens::$commentTokens, true)
&& $tokens[$pointerBeforeFirstUse]['line'] + 1 === $tokens[$useStartPointer]['line']
) {
$useStartPointer = array_key_exists('comment_opener', $tokens[$pointerBeforeFirstUse])
? $tokens[$pointerBeforeFirstUse]['comment_opener']
: CommentHelper::getMultilineCommentStartPointer($phpcsFile, $pointerBeforeFirstUse);
/** @var int $pointerBeforeFirstUse */
$pointerBeforeFirstUse = TokenHelper::findPreviousExcluding($phpcsFile, T_WHITESPACE, $useStartPointer - 1);
}

$requiredLinesCountBeforeFirstUse = SniffSettingsHelper::normalizeInteger($this->linesCountBeforeFirstUse);
$actualLinesCountBeforeFirstUse = substr_count($whitespaceBeforeFirstUse, $phpcsFile->eolChar) - 1;
$actualLinesCountBeforeFirstUse = $tokens[$useStartPointer]['line'] - $tokens[$pointerBeforeFirstUse]['line'] - 1;

if ($actualLinesCountBeforeFirstUse === $requiredLinesCountBeforeFirstUse) {
return;
Expand All @@ -113,7 +116,7 @@ private function checkLinesBeforeFirstUse(File $phpcsFile, UseStatement $firstUs
$phpcsFile->fixer->replaceToken($pointerBeforeFirstUse, '<?php');
}

for ($i = $pointerBeforeFirstUse + 1; $i < $firstUse->getPointer(); $i++) {
for ($i = $pointerBeforeFirstUse + 1; $i < $useStartPointer; $i++) {
$phpcsFile->fixer->replaceToken($i, '');
}
for ($i = 0; $i <= $requiredLinesCountBeforeFirstUse; $i++) {
Expand All @@ -124,18 +127,29 @@ private function checkLinesBeforeFirstUse(File $phpcsFile, UseStatement $firstUs

private function checkLinesAfterLastUse(File $phpcsFile, UseStatement $lastUse): void
{
/** @var int $lastUseSemicolonPointer */
$lastUseSemicolonPointer = TokenHelper::findNextLocal($phpcsFile, T_SEMICOLON, $lastUse->getPointer() + 1);
$tokens = $phpcsFile->getTokens();

$pointerAfterWhitespaceEnd = TokenHelper::findNextExcluding($phpcsFile, T_WHITESPACE, $lastUseSemicolonPointer + 1);
/** @var int $useEndPointer */
$useEndPointer = TokenHelper::findNextLocal($phpcsFile, T_SEMICOLON, $lastUse->getPointer() + 1);

$pointerAfterWhitespaceEnd = TokenHelper::findNextExcluding($phpcsFile, T_WHITESPACE, $useEndPointer + 1);
if ($pointerAfterWhitespaceEnd === null) {
return;
}

$whitespaceAfterLastUse = TokenHelper::getContent($phpcsFile, $lastUseSemicolonPointer + 1, $pointerAfterWhitespaceEnd - 1);
if (
in_array($tokens[$pointerAfterWhitespaceEnd]['code'], Tokens::$commentTokens, true)
&& $tokens[$useEndPointer]['line'] + 1 === $tokens[$pointerAfterWhitespaceEnd]['line']
) {
$useEndPointer = array_key_exists('comment_closer', $tokens[$pointerAfterWhitespaceEnd])
? $tokens[$pointerAfterWhitespaceEnd]['comment_closer']
: CommentHelper::getMultilineCommentEndPointer($phpcsFile, $pointerAfterWhitespaceEnd);
/** @var int $pointerAfterWhitespaceEnd */
$pointerAfterWhitespaceEnd = TokenHelper::findNextExcluding($phpcsFile, T_WHITESPACE, $useEndPointer + 1);
}

$requiredLinesCountAfterLastUse = SniffSettingsHelper::normalizeInteger($this->linesCountAfterLastUse);
$actualLinesCountAfterLastUse = substr_count($whitespaceAfterLastUse, $phpcsFile->eolChar) - 1;
$actualLinesCountAfterLastUse = $tokens[$pointerAfterWhitespaceEnd]['line'] - $tokens[$useEndPointer]['line'] - 1;

if ($actualLinesCountAfterLastUse === $requiredLinesCountAfterLastUse) {
return;
Expand All @@ -156,11 +170,17 @@ private function checkLinesAfterLastUse(File $phpcsFile, UseStatement $lastUse):
}

$phpcsFile->fixer->beginChangeset();
for ($i = $lastUseSemicolonPointer + 1; $i < $pointerAfterWhitespaceEnd; $i++) {
for ($i = $useEndPointer + 1; $i < $pointerAfterWhitespaceEnd; $i++) {
$phpcsFile->fixer->replaceToken($i, '');
}
for ($i = 0; $i <= $requiredLinesCountAfterLastUse; $i++) {
$phpcsFile->fixer->addNewline($lastUseSemicolonPointer);

$linesToAdd = $requiredLinesCountAfterLastUse;
if (in_array($tokens[$useEndPointer]['code'], TokenHelper::$inlineCommentTokenCodes, true)) {
$linesToAdd--;
}

for ($i = 0; $i <= $linesToAdd; $i++) {
$phpcsFile->fixer->addNewline($useEndPointer);
}
$phpcsFile->fixer->endChangeset();
}
Expand Down Expand Up @@ -191,7 +211,20 @@ private function checkLinesBetweenSameTypesOfUse(File $phpcsFile, array $useStat
continue;
}

$actualLinesCountAfterPreviousUse = $tokens[$use->getPointer()]['line'] - $tokens[$previousUse->getPointer()]['line'] - 1;
/** @var int $pointerBeforeUse */
$pointerBeforeUse = TokenHelper::findPreviousExcluding($phpcsFile, T_WHITESPACE, $use->getPointer() - 1);
$useStartPointer = $use->getPointer();

if (
in_array($tokens[$pointerBeforeUse]['code'], Tokens::$commentTokens, true)
&& $tokens[$pointerBeforeUse]['line'] + 1 === $tokens[$useStartPointer]['line']
) {
$useStartPointer = array_key_exists('comment_opener', $tokens[$pointerBeforeUse])
? $tokens[$pointerBeforeUse]['comment_opener']
: CommentHelper::getMultilineCommentStartPointer($phpcsFile, $pointerBeforeUse);
}

$actualLinesCountAfterPreviousUse = $tokens[$useStartPointer]['line'] - $tokens[$previousUse->getPointer()]['line'] - 1;

if ($actualLinesCountAfterPreviousUse === $requiredLinesCountBetweenUses) {
$previousUse = $use;
Expand All @@ -217,7 +250,7 @@ private function checkLinesBetweenSameTypesOfUse(File $phpcsFile, array $useStat
$previousUseSemicolonPointer = TokenHelper::findNextLocal($phpcsFile, T_SEMICOLON, $previousUse->getPointer() + 1);

$phpcsFile->fixer->beginChangeset();
for ($i = $previousUseSemicolonPointer + 1; $i < $use->getPointer(); $i++) {
for ($i = $previousUseSemicolonPointer + 1; $i < $useStartPointer; $i++) {
$phpcsFile->fixer->replaceToken($i, '');
}
$phpcsFile->fixer->addNewline($previousUseSemicolonPointer);
Expand Down Expand Up @@ -253,7 +286,20 @@ private function checkLinesBetweenDifferentTypesOfUse(File $phpcsFile, array $us
continue;
}

$actualLinesCountAfterPreviousUse = $tokens[$use->getPointer()]['line'] - $tokens[$previousUse->getPointer()]['line'] - 1;
/** @var int $pointerBeforeUse */
$pointerBeforeUse = TokenHelper::findPreviousExcluding($phpcsFile, T_WHITESPACE, $use->getPointer() - 1);
$useStartPointer = $use->getPointer();

if (
in_array($tokens[$pointerBeforeUse]['code'], Tokens::$commentTokens, true)
&& $tokens[$pointerBeforeUse]['line'] + 1 === $tokens[$useStartPointer]['line']
) {
$useStartPointer = array_key_exists('comment_opener', $tokens[$pointerBeforeUse])
? $tokens[$pointerBeforeUse]['comment_opener']
: CommentHelper::getMultilineCommentStartPointer($phpcsFile, $pointerBeforeUse);
}

$actualLinesCountAfterPreviousUse = $tokens[$useStartPointer]['line'] - $tokens[$previousUse->getPointer()]['line'] - 1;

if ($actualLinesCountAfterPreviousUse === $requiredLinesCountBetweenUseTypes) {
$previousUse = $use;
Expand All @@ -279,7 +325,7 @@ private function checkLinesBetweenDifferentTypesOfUse(File $phpcsFile, array $us
$previousUseSemicolonPointer = TokenHelper::findNextLocal($phpcsFile, T_SEMICOLON, $previousUse->getPointer() + 1);

$phpcsFile->fixer->beginChangeset();
for ($i = $previousUseSemicolonPointer + 1; $i < $use->getPointer(); $i++) {
for ($i = $previousUseSemicolonPointer + 1; $i < $useStartPointer; $i++) {
$phpcsFile->fixer->replaceToken($i, '');
}
for ($i = 0; $i <= $requiredLinesCountBetweenUseTypes; $i++) {
Expand Down
40 changes: 40 additions & 0 deletions tests/Sniffs/Namespaces/UseSpacingSniffTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,46 @@ public function testDefaultSettingsNoErrors(): void
self::assertNoSniffErrorInFile($report);
}

public function testDefaultSettingsWithCommentsNoErrors(): void
{
$report = self::checkFile(__DIR__ . '/data/useSpacingWithDefaultSettingsWithCommentsNoErrors.php');
self::assertNoSniffErrorInFile($report);
}

public function testDefaultSettingsWithCommentsErrors(): void
{
$report = self::checkFile(__DIR__ . '/data/useSpacingWithDefaultSettingsWithCommentsErrors.php');

self::assertSame(4, $report->getErrorCount());

self::assertSniffError($report, 7, UseSpacingSniff::CODE_INCORRECT_LINES_COUNT_BEFORE_FIRST_USE);
self::assertSniffError($report, 12, UseSpacingSniff::CODE_INCORRECT_LINES_COUNT_BETWEEN_DIFFERENT_TYPES_OF_USE);
self::assertSniffError($report, 18, UseSpacingSniff::CODE_INCORRECT_LINES_COUNT_BETWEEN_SAME_TYPES_OF_USE);
self::assertSniffError($report, 18, UseSpacingSniff::CODE_INCORRECT_LINES_COUNT_AFTER_LAST_USE);

self::assertAllFixedInFile($report);
}

public function testDefaultSettingsWithInlineCommentsNoErrors(): void
{
$report = self::checkFile(__DIR__ . '/data/useSpacingWithDefaultSettingsWithInlineCommentsNoErrors.php');
self::assertNoSniffErrorInFile($report);
}

public function testDefaultSettingsWithInlineCommentsErrors(): void
{
$report = self::checkFile(__DIR__ . '/data/useSpacingWithDefaultSettingsWithInlineCommentsErrors.php');

self::assertSame(4, $report->getErrorCount());

self::assertSniffError($report, 5, UseSpacingSniff::CODE_INCORRECT_LINES_COUNT_BEFORE_FIRST_USE);
self::assertSniffError($report, 8, UseSpacingSniff::CODE_INCORRECT_LINES_COUNT_BETWEEN_DIFFERENT_TYPES_OF_USE);
self::assertSniffError($report, 12, UseSpacingSniff::CODE_INCORRECT_LINES_COUNT_BETWEEN_SAME_TYPES_OF_USE);
self::assertSniffError($report, 12, UseSpacingSniff::CODE_INCORRECT_LINES_COUNT_AFTER_LAST_USE);

self::assertAllFixedInFile($report);
}

public function testDefaultSettingsErrors(): void
{
$report = self::checkFile(__DIR__ . '/data/useSpacingWithDefaultSettingsErrors.php');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php declare(strict_types = 1);

/**
* Something
*/
use DateTimeImmutable;
/**
* Whatever
*/
use function phpversion;
use const PHP_VERSION;
/**
* Anything
*/
use const PHP_VERSION_ID;
/**
* Nothing
*/

class Whatever
{

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php declare(strict_types = 1);


/**
* Something
*/
use DateTimeImmutable;

/**
* Whatever
*/
use function phpversion;
use const PHP_VERSION;

/**
* Anything
*/
use const PHP_VERSION_ID;
/**
* Nothing
*/


class Whatever
{

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php declare(strict_types = 1);

/**
* Something
*/
use DateTimeImmutable;
/**
* Whatever
*/
use function phpversion;
use const PHP_VERSION;
/**
* Anything
*/
use const PHP_VERSION_ID;
/**
* Nothing
*/

class Whatever
{

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php declare(strict_types = 1);

// Something
use DateTimeImmutable;
// phpcs:disable Something
use function phpversion;
use const PHP_VERSION;
// Anything
use const PHP_VERSION_ID;
// phpcs:enable

class Whatever
{

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php declare(strict_types = 1);


// Something
use DateTimeImmutable;

// phpcs:disable Something
use function phpversion;
use const PHP_VERSION;

// Anything
use const PHP_VERSION_ID;
// phpcs:enable


class Whatever
{

}

0 comments on commit 14fbbf5

Please sign in to comment.