Skip to content

Commit

Permalink
Generic//EmptyPHPStatement: reduce complexity/nesting levels
Browse files Browse the repository at this point in the history
Follow up on #699

Commit 529bcba removed the unreachable `default` case from the `switch`, but even when it is not explicitly there, this still leaves an unreachable branch in the code flow.

This commit takes the previous change one step further and removes the `switch` completely in favour of two separate `private` functions which each handle one specific token.

N.B.: this commit will be easier to review while ignoring whitespace changes.
  • Loading branch information
jrfnl committed Nov 26, 2024
1 parent e60140d commit edbb408
Showing 1 changed file with 109 additions and 79 deletions.
188 changes: 109 additions & 79 deletions src/Standards/Generic/Sniffs/CodeAnalysis/EmptyPHPStatementSniff.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,106 +48,136 @@ public function process(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();

switch ($tokens[$stackPtr]['type']) {
// Detect `something();;`.
case 'T_SEMICOLON':
$prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);

if ($tokens[$prevNonEmpty]['code'] !== T_SEMICOLON
&& $tokens[$prevNonEmpty]['code'] !== T_OPEN_TAG
&& $tokens[$prevNonEmpty]['code'] !== T_OPEN_TAG_WITH_ECHO
if ($tokens[$stackPtr]['code'] === T_SEMICOLON) {
$this->processSemicolon($phpcsFile, $stackPtr);
} else {
$this->processCloseTag($phpcsFile, $stackPtr);
}

}//end process()


/**
* Detect `something();;`.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
private function processSemicolon(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();

$prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
if ($tokens[$prevNonEmpty]['code'] !== T_SEMICOLON
&& $tokens[$prevNonEmpty]['code'] !== T_OPEN_TAG
&& $tokens[$prevNonEmpty]['code'] !== T_OPEN_TAG_WITH_ECHO
) {
if (isset($tokens[$prevNonEmpty]['scope_condition']) === false) {
return;
}

if ($tokens[$prevNonEmpty]['scope_opener'] !== $prevNonEmpty
&& $tokens[$prevNonEmpty]['code'] !== T_CLOSE_CURLY_BRACKET
) {
if (isset($tokens[$prevNonEmpty]['scope_condition']) === false) {
return;
}
return;
}

if ($tokens[$prevNonEmpty]['scope_opener'] !== $prevNonEmpty
&& $tokens[$prevNonEmpty]['code'] !== T_CLOSE_CURLY_BRACKET
) {
return;
}
$scopeOwner = $tokens[$tokens[$prevNonEmpty]['scope_condition']]['code'];
if ($scopeOwner === T_CLOSURE || $scopeOwner === T_ANON_CLASS || $scopeOwner === T_MATCH) {
return;
}

$scopeOwner = $tokens[$tokens[$prevNonEmpty]['scope_condition']]['code'];
if ($scopeOwner === T_CLOSURE || $scopeOwner === T_ANON_CLASS || $scopeOwner === T_MATCH) {
return;
}
// Else, it's something like `if (foo) {};` and the semicolon is not needed.
}

// Else, it's something like `if (foo) {};` and the semicolon is not needed.
if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
$nested = $tokens[$stackPtr]['nested_parenthesis'];
$lastCloser = array_pop($nested);
if (isset($tokens[$lastCloser]['parenthesis_owner']) === true
&& $tokens[$tokens[$lastCloser]['parenthesis_owner']]['code'] === T_FOR
) {
// Empty for() condition.
return;
}
}

if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
$nested = $tokens[$stackPtr]['nested_parenthesis'];
$lastCloser = array_pop($nested);
if (isset($tokens[$lastCloser]['parenthesis_owner']) === true
&& $tokens[$tokens[$lastCloser]['parenthesis_owner']]['code'] === T_FOR
) {
// Empty for() condition.
return;
$fix = $phpcsFile->addFixableWarning(
'Empty PHP statement detected: superfluous semicolon.',
$stackPtr,
'SemicolonWithoutCodeDetected'
);

if ($fix === true) {
$phpcsFile->fixer->beginChangeset();

if ($tokens[$prevNonEmpty]['code'] === T_OPEN_TAG
|| $tokens[$prevNonEmpty]['code'] === T_OPEN_TAG_WITH_ECHO
) {
// Check for superfluous whitespace after the semicolon which should be
// removed as the `<?php ` open tag token already contains whitespace,
// either a space or a new line.
if ($tokens[($stackPtr + 1)]['code'] === T_WHITESPACE) {
$replacement = str_replace(' ', '', $tokens[($stackPtr + 1)]['content']);
$phpcsFile->fixer->replaceToken(($stackPtr + 1), $replacement);
}
}

$fix = $phpcsFile->addFixableWarning(
'Empty PHP statement detected: superfluous semicolon.',
$stackPtr,
'SemicolonWithoutCodeDetected'
);
if ($fix === true) {
$phpcsFile->fixer->beginChangeset();

if ($tokens[$prevNonEmpty]['code'] === T_OPEN_TAG
|| $tokens[$prevNonEmpty]['code'] === T_OPEN_TAG_WITH_ECHO
for ($i = $stackPtr; $i > $prevNonEmpty; $i--) {
if ($tokens[$i]['code'] !== T_SEMICOLON
&& $tokens[$i]['code'] !== T_WHITESPACE
) {
// Check for superfluous whitespace after the semicolon which will be
// removed as the `<?php ` open tag token already contains whitespace,
// either a space or a new line.
if ($tokens[($stackPtr + 1)]['code'] === T_WHITESPACE) {
$replacement = str_replace(' ', '', $tokens[($stackPtr + 1)]['content']);
$phpcsFile->fixer->replaceToken(($stackPtr + 1), $replacement);
}
break;
}

for ($i = $stackPtr; $i > $prevNonEmpty; $i--) {
if ($tokens[$i]['code'] !== T_SEMICOLON
&& $tokens[$i]['code'] !== T_WHITESPACE
) {
break;
}
$phpcsFile->fixer->replaceToken($i, '');
}

$phpcsFile->fixer->replaceToken($i, '');
}
$phpcsFile->fixer->endChangeset();
}//end if

$phpcsFile->fixer->endChangeset();
}//end if
break;
}//end processSemicolon()

// Detect `<?php ? >`.
case 'T_CLOSE_TAG':
$prevNonEmpty = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);

if ($tokens[$prevNonEmpty]['code'] !== T_OPEN_TAG
&& $tokens[$prevNonEmpty]['code'] !== T_OPEN_TAG_WITH_ECHO
) {
return;
}
/**
* Detect `<?php ? >`.
*
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
* @param int $stackPtr The position of the current token
* in the stack passed in $tokens.
*
* @return void
*/
private function processCloseTag(File $phpcsFile, $stackPtr)
{
$tokens = $phpcsFile->getTokens();

$fix = $phpcsFile->addFixableWarning(
'Empty PHP open/close tag combination detected.',
$prevNonEmpty,
'EmptyPHPOpenCloseTagsDetected'
);
if ($fix === true) {
$phpcsFile->fixer->beginChangeset();
$prevNonEmpty = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
if ($tokens[$prevNonEmpty]['code'] !== T_OPEN_TAG
&& $tokens[$prevNonEmpty]['code'] !== T_OPEN_TAG_WITH_ECHO
) {
return;
}

for ($i = $prevNonEmpty; $i <= $stackPtr; $i++) {
$phpcsFile->fixer->replaceToken($i, '');
}
$fix = $phpcsFile->addFixableWarning(
'Empty PHP open/close tag combination detected.',
$prevNonEmpty,
'EmptyPHPOpenCloseTagsDetected'
);

$phpcsFile->fixer->endChangeset();
if ($fix === true) {
$phpcsFile->fixer->beginChangeset();

for ($i = $prevNonEmpty; $i <= $stackPtr; $i++) {
$phpcsFile->fixer->replaceToken($i, '');
}
break;
}//end switch

}//end process()
$phpcsFile->fixer->endChangeset();
}

}//end processCloseTag()


}//end class

0 comments on commit edbb408

Please sign in to comment.