Skip to content

Commit 5a0a897

Browse files
committed
EarlyExitSniff: fixed false positive
1 parent d91220b commit 5a0a897

File tree

3 files changed

+30
-15
lines changed

3 files changed

+30
-15
lines changed

SlevomatCodingStandard/Helpers/TokenHelper.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,16 @@ class TokenHelper
4141
T_CALLABLE,
4242
];
4343

44+
/** @var mixed[] */
45+
public static $earlyExitTokenCodes = [
46+
T_RETURN,
47+
T_CONTINUE,
48+
T_BREAK,
49+
T_THROW,
50+
T_YIELD,
51+
T_EXIT,
52+
];
53+
4454
/**
4555
* @param \PHP_CodeSniffer\Files\File $phpcsFile
4656
* @param mixed|mixed[] $types

SlevomatCodingStandard/Sniffs/ControlStructures/EarlyExitSniff.php

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,7 @@ private function processElse(\PHP_CodeSniffer\Files\File $phpcsFile, int $elsePo
4444
{
4545
$tokens = $phpcsFile->getTokens();
4646

47-
$earlyExitTokenCodes = [T_RETURN, T_CONTINUE, T_BREAK, T_THROW, T_YIELD, T_EXIT];
48-
49-
$lastSemicolonInElseScopePointer = TokenHelper::findPreviousEffective($phpcsFile, $tokens[$elsePointer]['scope_closer'] - 1);
50-
if ($tokens[$lastSemicolonInElseScopePointer]['code'] !== T_SEMICOLON) {
51-
return;
52-
}
53-
54-
$earlyExitInElseScopePointer = TokenHelper::findPreviousLocal($phpcsFile, $earlyExitTokenCodes, $lastSemicolonInElseScopePointer - 1, $tokens[$elsePointer]['scope_opener']);
55-
if ($earlyExitInElseScopePointer === null) {
47+
if (!$this->isEarlyExitInScope($phpcsFile, $tokens[$elsePointer]['scope_opener'], $tokens[$elsePointer]['scope_closer'])) {
5648
return;
5749
}
5850

@@ -65,12 +57,7 @@ private function processElse(\PHP_CodeSniffer\Files\File $phpcsFile, int $elsePo
6557

6658
$ifPointer = $previousConditionPointer;
6759

68-
$lastSemicolonInIfScopePointer = TokenHelper::findPreviousEffective($phpcsFile, $tokens[$ifPointer]['scope_closer'] - 1);
69-
$isEarlyExitInIfScope = $tokens[$lastSemicolonInIfScopePointer]['code'] === T_SEMICOLON
70-
? TokenHelper::findPreviousLocal($phpcsFile, $earlyExitTokenCodes, $lastSemicolonInIfScopePointer - 1, $tokens[$ifPointer]['scope_opener']) !== null
71-
: false;
72-
73-
if ($isEarlyExitInIfScope) {
60+
if ($this->isEarlyExitInScope($phpcsFile, $tokens[$ifPointer]['scope_opener'], $tokens[$ifPointer]['scope_closer'])) {
7461
$fix = $phpcsFile->addFixableError(
7562
'Remove useless else to reduce code nesting.',
7663
$elsePointer,
@@ -153,6 +140,10 @@ private function processIf(\PHP_CodeSniffer\Files\File $phpcsFile, int $ifPointe
153140
return;
154141
}
155142

143+
if ($this->isEarlyExitInScope($phpcsFile, $tokens[$ifPointer]['scope_opener'], $tokens[$ifPointer]['scope_closer'])) {
144+
return;
145+
}
146+
156147
$fix = $phpcsFile->addFixableError(
157148
'Use early exit to reduce code nesting.',
158149
$ifPointer,
@@ -341,4 +332,12 @@ private function fixIndentation(string $code, string $eolChar, string $defaultIn
341332
}, explode($eolChar, rtrim($code))));
342333
}
343334

335+
private function isEarlyExitInScope(\PHP_CodeSniffer\Files\File $phpcsFile, int $startPointer, int $endPointer): bool
336+
{
337+
$lastSemicolonInScopePointer = TokenHelper::findPreviousEffective($phpcsFile, $endPointer - 1);
338+
return $phpcsFile->getTokens()[$lastSemicolonInScopePointer]['code'] === T_SEMICOLON
339+
? TokenHelper::findPreviousLocal($phpcsFile, TokenHelper::$earlyExitTokenCodes, $lastSemicolonInScopePointer - 1, $startPointer) !== null
340+
: false;
341+
}
342+
344343
}

tests/Sniffs/ControlStructures/data/earlyExitNoErrors.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,3 +133,9 @@ function () {
133133

134134
doSomethingAgain();
135135
}
136+
137+
foreach (['foo', 'bar'] as $identity) {
138+
if ($identity === 'foo') {
139+
break;
140+
}
141+
}

0 commit comments

Comments
 (0)