Skip to content

Commit

Permalink
EarlyExitSniff: Fixed fixer for heredoc/nowdoc
Browse files Browse the repository at this point in the history
  • Loading branch information
kukulich committed Feb 3, 2020
1 parent baacdb6 commit 0d57613
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 26 deletions.
58 changes: 42 additions & 16 deletions SlevomatCodingStandard/Helpers/IndentationHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@
namespace SlevomatCodingStandard\Helpers;

use PHP_CodeSniffer\Files\File;
use function array_map;
use function explode;
use function implode;
use function in_array;
use function ltrim;
use function rtrim;
use function strlen;
use function substr;
use const T_END_HEREDOC;
use const T_END_NOWDOC;
use const T_START_HEREDOC;
use const T_START_NOWDOC;
use const T_WHITESPACE;

/**
Expand Down Expand Up @@ -40,26 +43,49 @@ public static function addIndentation(string $identation): string
return $identation . ($identation[0] === self::TAB_INDENT ? self::TAB_INDENT : self::SPACES_INDENT);
}

public static function fixIndentation(string $code, string $eolChar, string $defaultIndentation): string
/**
* @param File $phpcsFile
* @param int[] $codePointers
* @param string $defaultIndentation
* @return string
*/
public static function fixIndentation(File $phpcsFile, array $codePointers, string $defaultIndentation): string
{
/** @var string[] $lines */
$lines = explode($eolChar, rtrim($code));
$tokens = $phpcsFile->getTokens();

return implode($eolChar, array_map(static function (string $line) use ($defaultIndentation): string {
if ($line === '') {
return $line;
}
$eolLength = strlen($phpcsFile->eolChar);

$code = '';
$inHeredoc = false;

foreach ($codePointers as $no => $codePointer) {
$content = $tokens[$codePointer]['content'];

if ($line[0] === self::TAB_INDENT) {
return substr($line, 1);
if (
!$inHeredoc
&& ($no === 0 || substr($tokens[$codePointer - 1]['content'], -$eolLength) === $phpcsFile->eolChar)
) {
if ($content === $phpcsFile->eolChar) {
// Nothing
} elseif ($content[0] === self::TAB_INDENT) {
$content = substr($content, 1);
} elseif (substr($content, 0, 4) === self::SPACES_INDENT) {
$content = substr($content, 4);
} else {
$content = $defaultIndentation . ltrim($content);
}
}

if (substr($line, 0, 4) === self::SPACES_INDENT) {
return substr($line, 4);
if (in_array($tokens[$codePointer]['code'], [T_START_HEREDOC, T_START_NOWDOC], true)) {
$inHeredoc = true;
} elseif (in_array($tokens[$codePointer]['code'], [T_END_HEREDOC, T_END_NOWDOC], true)) {
$inHeredoc = false;
}

return $defaultIndentation . ltrim($line);
}, $lines));
$code .= $content;
}

return rtrim($code);
}

}
24 changes: 18 additions & 6 deletions SlevomatCodingStandard/Sniffs/ControlStructures/EarlyExitSniff.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use Throwable;
use function array_key_exists;
use function in_array;
use function range;
use function sort;
use function sprintf;
use const T_CLOSE_CURLY_BRACKET;
Expand Down Expand Up @@ -133,10 +134,10 @@ private function processElse(File $phpcsFile, int $elsePointer): void
$phpcsFile->fixer->replaceToken($i, '');
}

$ifCode = $this->getScopeCode($phpcsFile, $ifPointer);
$ifCodePointers = $this->getScopeCodePointers($phpcsFile, $ifPointer);
$elseCode = $this->getScopeCode($phpcsFile, $elsePointer);
$negativeIfCondition = ConditionHelper::getNegativeCondition($phpcsFile, $tokens[$ifPointer]['parenthesis_opener'], $tokens[$ifPointer]['parenthesis_closer']);
$afterIfCode = IndentationHelper::fixIndentation($ifCode, $phpcsFile->eolChar, IndentationHelper::getIndentation($phpcsFile, $ifPointer));
$afterIfCode = IndentationHelper::fixIndentation($phpcsFile, $ifCodePointers, IndentationHelper::getIndentation($phpcsFile, $ifPointer));

$phpcsFile->fixer->addContent(
$ifPointer,
Expand Down Expand Up @@ -183,8 +184,8 @@ private function processElse(File $phpcsFile, int $elsePointer): void
$phpcsFile->fixer->replaceToken($i, '');
}

$elseCode = $this->getScopeCode($phpcsFile, $elsePointer);
$afterIfCode = IndentationHelper::fixIndentation($elseCode, $phpcsFile->eolChar, IndentationHelper::addIndentation(IndentationHelper::getIndentation($phpcsFile, $previousConditionPointer)));
$elseCodePointers = $this->getScopeCodePointers($phpcsFile, $elsePointer);
$afterIfCode = IndentationHelper::fixIndentation($phpcsFile, $elseCodePointers, IndentationHelper::addIndentation(IndentationHelper::getIndentation($phpcsFile, $previousConditionPointer)));

$phpcsFile->fixer->addContent(
$tokens[$elsePointer]['scope_closer'],
Expand Down Expand Up @@ -301,7 +302,7 @@ private function processIf(File $phpcsFile, int $ifPointer): void
return;
}

$ifCode = $this->getScopeCode($phpcsFile, $ifPointer);
$ifCodePointers = $this->getScopeCodePointers($phpcsFile, $ifPointer);
$ifIndentation = IndentationHelper::getIndentation($phpcsFile, $ifPointer);
$earlyExitCode = $this->getEarlyExitCode($tokens[$scopePointer]['code']);
$earlyExitCodeIndentation = IndentationHelper::addIndentation($ifIndentation);
Expand All @@ -314,7 +315,7 @@ private function processIf(File $phpcsFile, int $ifPointer): void
$phpcsFile->fixer->replaceToken($i, '');
}

$afterIfCode = IndentationHelper::fixIndentation($ifCode, $phpcsFile->eolChar, $ifIndentation);
$afterIfCode = IndentationHelper::fixIndentation($phpcsFile, $ifCodePointers, $ifIndentation);

$phpcsFile->fixer->addContent(
$ifPointer,
Expand All @@ -340,6 +341,17 @@ private function getScopeCode(File $phpcsFile, int $scopePointer): string
return TokenHelper::getContent($phpcsFile, $tokens[$scopePointer]['scope_opener'] + 1, $tokens[$scopePointer]['scope_closer'] - 1);
}

/**
* @param File $phpcsFile
* @param int $scopePointer
* @return int[]
*/
private function getScopeCodePointers(File $phpcsFile, int $scopePointer): array
{
$tokens = $phpcsFile->getTokens();
return range($tokens[$scopePointer]['scope_opener'] + 1, $tokens[$scopePointer]['scope_closer'] - 1);
}

/**
* @param string|int $code
* @return string
Expand Down
8 changes: 4 additions & 4 deletions tests/Sniffs/ControlStructures/EarlyExitSniffTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,21 @@ public function testErrors(): void
{
$report = self::checkFile(__DIR__ . '/data/earlyExitErrors.php');

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

foreach ([6, 15, 24, 33, 42, 50, 58, 66, 74, 82, 90, 98, 108, 149, 157, 165, 191, 199, 207, 376] as $line) {
self::assertSniffError($report, $line, EarlyExitSniff::CODE_EARLY_EXIT_NOT_USED, 'Use early exit instead of else.');
}

foreach ([115, 122, 129, 135, 141, 213, 222, 229, 235, 241, 247, 256, 262, 271, 287, 305, 361, 368, 388] as $line) {
foreach ([115, 122, 129, 135, 141, 213, 222, 229, 235, 241, 247, 256, 262, 271, 287, 305, 361, 368, 388, 415, 425] as $line) {
self::assertSniffError($report, $line, EarlyExitSniff::CODE_EARLY_EXIT_NOT_USED, 'Use early exit to reduce code nesting.');
}

foreach ([173, 182, 328, 353, 398] as $line) {
foreach ([173, 182, 328, 353, 398, 440] as $line) {
self::assertSniffError($report, $line, EarlyExitSniff::CODE_USELESS_ELSE, 'Remove useless else to reduce code nesting.');
}

foreach ([322, 324, 326, 336, 338, 340, 351, 396, 406] as $line) {
foreach ([322, 324, 326, 336, 338, 340, 351, 396, 406, 436] as $line) {
self::assertSniffError($report, $line, EarlyExitSniff::CODE_USELESS_ELSEIF, 'Use if instead of elseif.');
}

Expand Down
40 changes: 40 additions & 0 deletions tests/Sniffs/ControlStructures/data/earlyExitErrors.fixed.php
Original file line number Diff line number Diff line change
Expand Up @@ -467,3 +467,43 @@ function uselessElseIf() {
doSomethingElse();
}
}

function heredoc() {
foreach ([] as $f) {
if (!file_exists($f)) {
continue;
}

echo <<<EOF
XYZ
EOF;
}
}

function nowdoc() {
foreach ([] as $f) {
if (!file_exists($f)) {
continue;
}

echo <<<'EOF'
XYZ
EOF;
}
}

function uselessElseWithHeredoc() {
if (true) {
return;
}

if (false) {
return <<<EOF
XYZ
EOF;
}

echo <<<EOF
XYZ
EOF;
}
34 changes: 34 additions & 0 deletions tests/Sniffs/ControlStructures/data/earlyExitErrors.php
Original file line number Diff line number Diff line change
Expand Up @@ -409,3 +409,37 @@ function uselessElseIf() {
doSomethingElse();
}
}

function heredoc() {
foreach ([] as $f) {
if (file_exists($f)) {
echo <<<EOF
XYZ
EOF;
}
}
}

function nowdoc() {
foreach ([] as $f) {
if (file_exists($f)) {
echo <<<'EOF'
XYZ
EOF;
}
}
}

function uselessElseWithHeredoc() {
if (true) {
return;
} elseif (false) {
return <<<EOF
XYZ
EOF;
} else {
echo <<<EOF
XYZ
EOF;
}
}

0 comments on commit 0d57613

Please sign in to comment.