Skip to content

Commit 715242e

Browse files
committed
Generic//EmptyPHPStatement: reduce complexity/nesting levels
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.
1 parent c88c241 commit 715242e

File tree

1 file changed

+109
-79
lines changed

1 file changed

+109
-79
lines changed

src/Standards/Generic/Sniffs/CodeAnalysis/EmptyPHPStatementSniff.php

Lines changed: 109 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -48,106 +48,136 @@ public function process(File $phpcsFile, $stackPtr)
4848
{
4949
$tokens = $phpcsFile->getTokens();
5050

51-
switch ($tokens[$stackPtr]['type']) {
52-
// Detect `something();;`.
53-
case 'T_SEMICOLON':
54-
$prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
55-
56-
if ($tokens[$prevNonEmpty]['code'] !== T_SEMICOLON
57-
&& $tokens[$prevNonEmpty]['code'] !== T_OPEN_TAG
58-
&& $tokens[$prevNonEmpty]['code'] !== T_OPEN_TAG_WITH_ECHO
51+
if ($tokens[$stackPtr]['code'] === T_SEMICOLON) {
52+
$this->processSemicolon($phpcsFile, $stackPtr);
53+
} else {
54+
$this->processCloseTag($phpcsFile, $stackPtr);
55+
}
56+
57+
}//end process()
58+
59+
60+
/**
61+
* Detect `something();;`.
62+
*
63+
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
64+
* @param int $stackPtr The position of the current token
65+
* in the stack passed in $tokens.
66+
*
67+
* @return void
68+
*/
69+
private function processSemicolon(File $phpcsFile, $stackPtr)
70+
{
71+
$tokens = $phpcsFile->getTokens();
72+
73+
$prevNonEmpty = $phpcsFile->findPrevious(Tokens::$emptyTokens, ($stackPtr - 1), null, true);
74+
if ($tokens[$prevNonEmpty]['code'] !== T_SEMICOLON
75+
&& $tokens[$prevNonEmpty]['code'] !== T_OPEN_TAG
76+
&& $tokens[$prevNonEmpty]['code'] !== T_OPEN_TAG_WITH_ECHO
77+
) {
78+
if (isset($tokens[$prevNonEmpty]['scope_condition']) === false) {
79+
return;
80+
}
81+
82+
if ($tokens[$prevNonEmpty]['scope_opener'] !== $prevNonEmpty
83+
&& $tokens[$prevNonEmpty]['code'] !== T_CLOSE_CURLY_BRACKET
5984
) {
60-
if (isset($tokens[$prevNonEmpty]['scope_condition']) === false) {
61-
return;
62-
}
85+
return;
86+
}
6387

64-
if ($tokens[$prevNonEmpty]['scope_opener'] !== $prevNonEmpty
65-
&& $tokens[$prevNonEmpty]['code'] !== T_CLOSE_CURLY_BRACKET
66-
) {
67-
return;
68-
}
88+
$scopeOwner = $tokens[$tokens[$prevNonEmpty]['scope_condition']]['code'];
89+
if ($scopeOwner === T_CLOSURE || $scopeOwner === T_ANON_CLASS || $scopeOwner === T_MATCH) {
90+
return;
91+
}
6992

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

75-
// Else, it's something like `if (foo) {};` and the semicolon is not needed.
96+
if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
97+
$nested = $tokens[$stackPtr]['nested_parenthesis'];
98+
$lastCloser = array_pop($nested);
99+
if (isset($tokens[$lastCloser]['parenthesis_owner']) === true
100+
&& $tokens[$tokens[$lastCloser]['parenthesis_owner']]['code'] === T_FOR
101+
) {
102+
// Empty for() condition.
103+
return;
76104
}
105+
}
77106

78-
if (isset($tokens[$stackPtr]['nested_parenthesis']) === true) {
79-
$nested = $tokens[$stackPtr]['nested_parenthesis'];
80-
$lastCloser = array_pop($nested);
81-
if (isset($tokens[$lastCloser]['parenthesis_owner']) === true
82-
&& $tokens[$tokens[$lastCloser]['parenthesis_owner']]['code'] === T_FOR
83-
) {
84-
// Empty for() condition.
85-
return;
107+
$fix = $phpcsFile->addFixableWarning(
108+
'Empty PHP statement detected: superfluous semicolon.',
109+
$stackPtr,
110+
'SemicolonWithoutCodeDetected'
111+
);
112+
113+
if ($fix === true) {
114+
$phpcsFile->fixer->beginChangeset();
115+
116+
if ($tokens[$prevNonEmpty]['code'] === T_OPEN_TAG
117+
|| $tokens[$prevNonEmpty]['code'] === T_OPEN_TAG_WITH_ECHO
118+
) {
119+
// Check for superfluous whitespace after the semicolon which should be
120+
// removed as the `<?php ` open tag token already contains whitespace,
121+
// either a space or a new line.
122+
if ($tokens[($stackPtr + 1)]['code'] === T_WHITESPACE) {
123+
$replacement = str_replace(' ', '', $tokens[($stackPtr + 1)]['content']);
124+
$phpcsFile->fixer->replaceToken(($stackPtr + 1), $replacement);
86125
}
87126
}
88127

89-
$fix = $phpcsFile->addFixableWarning(
90-
'Empty PHP statement detected: superfluous semicolon.',
91-
$stackPtr,
92-
'SemicolonWithoutCodeDetected'
93-
);
94-
if ($fix === true) {
95-
$phpcsFile->fixer->beginChangeset();
96-
97-
if ($tokens[$prevNonEmpty]['code'] === T_OPEN_TAG
98-
|| $tokens[$prevNonEmpty]['code'] === T_OPEN_TAG_WITH_ECHO
128+
for ($i = $stackPtr; $i > $prevNonEmpty; $i--) {
129+
if ($tokens[$i]['code'] !== T_SEMICOLON
130+
&& $tokens[$i]['code'] !== T_WHITESPACE
99131
) {
100-
// Check for superfluous whitespace after the semicolon which will be
101-
// removed as the `<?php ` open tag token already contains whitespace,
102-
// either a space or a new line.
103-
if ($tokens[($stackPtr + 1)]['code'] === T_WHITESPACE) {
104-
$replacement = str_replace(' ', '', $tokens[($stackPtr + 1)]['content']);
105-
$phpcsFile->fixer->replaceToken(($stackPtr + 1), $replacement);
106-
}
132+
break;
107133
}
108134

109-
for ($i = $stackPtr; $i > $prevNonEmpty; $i--) {
110-
if ($tokens[$i]['code'] !== T_SEMICOLON
111-
&& $tokens[$i]['code'] !== T_WHITESPACE
112-
) {
113-
break;
114-
}
135+
$phpcsFile->fixer->replaceToken($i, '');
136+
}
115137

116-
$phpcsFile->fixer->replaceToken($i, '');
117-
}
138+
$phpcsFile->fixer->endChangeset();
139+
}//end if
118140

119-
$phpcsFile->fixer->endChangeset();
120-
}//end if
121-
break;
141+
}//end processSemicolon()
122142

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

127-
if ($tokens[$prevNonEmpty]['code'] !== T_OPEN_TAG
128-
&& $tokens[$prevNonEmpty]['code'] !== T_OPEN_TAG_WITH_ECHO
129-
) {
130-
return;
131-
}
144+
/**
145+
* Detect `<?php ? >`.
146+
*
147+
* @param \PHP_CodeSniffer\Files\File $phpcsFile The file being scanned.
148+
* @param int $stackPtr The position of the current token
149+
* in the stack passed in $tokens.
150+
*
151+
* @return void
152+
*/
153+
private function processCloseTag(File $phpcsFile, $stackPtr)
154+
{
155+
$tokens = $phpcsFile->getTokens();
132156

133-
$fix = $phpcsFile->addFixableWarning(
134-
'Empty PHP open/close tag combination detected.',
135-
$prevNonEmpty,
136-
'EmptyPHPOpenCloseTagsDetected'
137-
);
138-
if ($fix === true) {
139-
$phpcsFile->fixer->beginChangeset();
157+
$prevNonEmpty = $phpcsFile->findPrevious(T_WHITESPACE, ($stackPtr - 1), null, true);
158+
if ($tokens[$prevNonEmpty]['code'] !== T_OPEN_TAG
159+
&& $tokens[$prevNonEmpty]['code'] !== T_OPEN_TAG_WITH_ECHO
160+
) {
161+
return;
162+
}
140163

141-
for ($i = $prevNonEmpty; $i <= $stackPtr; $i++) {
142-
$phpcsFile->fixer->replaceToken($i, '');
143-
}
164+
$fix = $phpcsFile->addFixableWarning(
165+
'Empty PHP open/close tag combination detected.',
166+
$prevNonEmpty,
167+
'EmptyPHPOpenCloseTagsDetected'
168+
);
144169

145-
$phpcsFile->fixer->endChangeset();
170+
if ($fix === true) {
171+
$phpcsFile->fixer->beginChangeset();
172+
173+
for ($i = $prevNonEmpty; $i <= $stackPtr; $i++) {
174+
$phpcsFile->fixer->replaceToken($i, '');
146175
}
147-
break;
148-
}//end switch
149176

150-
}//end process()
177+
$phpcsFile->fixer->endChangeset();
178+
}
179+
180+
}//end processCloseTag()
151181

152182

153183
}//end class

0 commit comments

Comments
 (0)