From edbb408a16b70d5d6bb6f847b5b8dfe4cd3b070e Mon Sep 17 00:00:00 2001 From: jrfnl Date: Mon, 25 Nov 2024 03:36:48 +0100 Subject: [PATCH] Generic//EmptyPHPStatement: reduce complexity/nesting levels Follow up on #699 Commit 529bcba7a42ec35873c31459ae61bee67852e956 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. --- .../CodeAnalysis/EmptyPHPStatementSniff.php | 188 ++++++++++-------- 1 file changed, 109 insertions(+), 79 deletions(-) diff --git a/src/Standards/Generic/Sniffs/CodeAnalysis/EmptyPHPStatementSniff.php b/src/Standards/Generic/Sniffs/CodeAnalysis/EmptyPHPStatementSniff.php index eb7c50ce85..6fbfdc0b2b 100644 --- a/src/Standards/Generic/Sniffs/CodeAnalysis/EmptyPHPStatementSniff.php +++ b/src/Standards/Generic/Sniffs/CodeAnalysis/EmptyPHPStatementSniff.php @@ -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 `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 `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 ``. - 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 ``. + * + * @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