Skip to content
This repository was archived by the owner on Jan 21, 2020. It is now read-only.

Commit 4d14354

Browse files
committed
Improvements for Throw sniff
Better analyze if throw tag should be added on the function
1 parent 2800f34 commit 4d14354

File tree

3 files changed

+116
-2
lines changed

3 files changed

+116
-2
lines changed

src/ZendCodingStandard/Sniffs/Functions/ThrowsSniff.php

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,20 @@
1919

2020
use const T_BITWISE_OR;
2121
use const T_CATCH;
22+
use const T_CLOSE_CURLY_BRACKET;
23+
use const T_CLOSE_PARENTHESIS;
24+
use const T_CLOSE_SHORT_ARRAY;
25+
use const T_CLOSE_SQUARE_BRACKET;
2226
use const T_CLOSURE;
2327
use const T_DOC_COMMENT_STRING;
2428
use const T_FUNCTION;
2529
use const T_NEW;
2630
use const T_NS_SEPARATOR;
31+
use const T_OPEN_CURLY_BRACKET;
32+
use const T_OPEN_PARENTHESIS;
33+
use const T_OPEN_SHORT_ARRAY;
34+
use const T_OPEN_SQUARE_BRACKET;
35+
use const T_SEMICOLON;
2736
use const T_STRING;
2837
use const T_THROW;
2938
use const T_TRY;
@@ -132,7 +141,7 @@ protected function processThrowStatements(File $phpcsFile, int $stackPtr) : void
132141
}
133142

134143
// The throw statement is in another scope.
135-
if (! $this->isLastScope($tokens[$throw]['conditions'], $stackPtr)) {
144+
if (! $this->isLastScope($phpcsFile, $tokens[$throw]['conditions'], $stackPtr)) {
136145
continue;
137146
}
138147

@@ -305,16 +314,67 @@ private function getExceptions(File $phpcsFile, int $from, int $to) : array
305314
* @param string[] $conditions
306315
* @param int $scope Scope to check in conditions.
307316
*/
308-
private function isLastScope(array $conditions, int $scope) : bool
317+
private function isLastScope(File $phpcsFile, array $conditions, int $scope) : bool
309318
{
319+
$tokens = $phpcsFile->getTokens();
320+
310321
foreach (array_reverse($conditions, true) as $ptr => $code) {
311322
if ($code !== T_FUNCTION && $code !== T_CLOSURE && $code !== T_TRY) {
312323
continue;
313324
}
314325

326+
if ($code === T_CLOSURE && $ptr !== $scope) {
327+
// Check if closure is called.
328+
$afterClosure = $phpcsFile->findNext(
329+
Tokens::$emptyTokens,
330+
$tokens[$ptr]['scope_closer'] + 1,
331+
null,
332+
true
333+
);
334+
if ($afterClosure && $tokens[$afterClosure]['code'] === T_CLOSE_PARENTHESIS) {
335+
$next = $phpcsFile->findNext(Tokens::$emptyTokens, $afterClosure + 1, null, true);
336+
if ($next && $tokens[$next]['code'] === T_OPEN_PARENTHESIS) {
337+
return true;
338+
}
339+
}
340+
341+
// Check if closure is passed to function/class.
342+
if (($token = $this->findPrevious($phpcsFile, $ptr))
343+
&& in_array($tokens[$token]['code'], [T_STRING, T_VARIABLE], true)
344+
) {
345+
return true;
346+
}
347+
}
348+
315349
return $ptr === $scope;
316350
}
317351

318352
return false;
319353
}
354+
355+
private function findPrevious(File $phpcsFile, int $ptr) : ?int
356+
{
357+
$tokens = $phpcsFile->getTokens();
358+
359+
while (--$ptr) {
360+
if ($tokens[$ptr]['code'] === T_CLOSE_PARENTHESIS) {
361+
$ptr = $tokens[$ptr]['parenthesis_opener'];
362+
} elseif ($tokens[$ptr]['code'] === T_CLOSE_CURLY_BRACKET
363+
|| $tokens[$ptr]['code'] === T_CLOSE_SHORT_ARRAY
364+
|| $tokens[$ptr]['code'] === T_CLOSE_SQUARE_BRACKET
365+
) {
366+
$ptr = $tokens[$ptr]['bracket_opener'];
367+
} elseif ($tokens[$ptr]['code'] === T_OPEN_PARENTHESIS) {
368+
return $phpcsFile->findPrevious(Tokens::$emptyTokens, $ptr - 1, null, true);
369+
} elseif (in_array(
370+
$tokens[$ptr]['code'],
371+
[T_SEMICOLON, T_OPEN_CURLY_BRACKET, T_OPEN_SHORT_ARRAY, T_OPEN_SQUARE_BRACKET],
372+
true
373+
)) {
374+
break;
375+
}
376+
}
377+
378+
return null;
379+
}
320380
}

test/Sniffs/Functions/ThrowsUnitTest.inc

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,4 +283,31 @@ class ThrowUnitTest
283283
{
284284
throw new Ex\Exception;
285285
}
286+
287+
public function closure()
288+
{
289+
return function () {
290+
throw new Exception();
291+
};
292+
}
293+
294+
/**
295+
* @throws Exception
296+
*/
297+
public function calledClosure()
298+
{
299+
(function () {
300+
throw new Exception();
301+
})();
302+
}
303+
304+
/**
305+
* @throws Exception
306+
*/
307+
public function callback(array $arr)
308+
{
309+
return array_filter([] + array_filter($arr), function () {
310+
throw new Exception();
311+
});
312+
}
286313
}

test/Sniffs/Functions/ThrowsUnitTest.inc.fixed

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,4 +283,31 @@ class ThrowUnitTest
283283
{
284284
throw new Ex\Exception;
285285
}
286+
287+
public function closure()
288+
{
289+
return function () {
290+
throw new Exception();
291+
};
292+
}
293+
294+
/**
295+
* @throws Exception
296+
*/
297+
public function calledClosure()
298+
{
299+
(function () {
300+
throw new Exception();
301+
})();
302+
}
303+
304+
/**
305+
* @throws Exception
306+
*/
307+
public function callback(array $arr)
308+
{
309+
return array_filter([] + array_filter($arr), function () {
310+
throw new Exception();
311+
});
312+
}
286313
}

0 commit comments

Comments
 (0)