Skip to content

Commit e3c70bc

Browse files
committed
DisallowImplicitArrayCreationSniff: Fixed false positives
1 parent 881c245 commit e3c70bc

File tree

4 files changed

+120
-16
lines changed

4 files changed

+120
-16
lines changed

SlevomatCodingStandard/Sniffs/Arrays/DisallowImplicitArrayCreationSniff.php

Lines changed: 81 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,20 @@
66
use PHP_CodeSniffer\Sniffs\Sniff;
77
use SlevomatCodingStandard\Helpers\ScopeHelper;
88
use SlevomatCodingStandard\Helpers\TokenHelper;
9-
use const T_ARRAY;
109
use const T_CLOSURE;
1110
use const T_DOUBLE_COLON;
1211
use const T_EQUAL;
12+
use const T_FOREACH;
13+
use const T_LIST;
1314
use const T_OBJECT_OPERATOR;
15+
use const T_OPEN_PARENTHESIS;
1416
use const T_OPEN_SHORT_ARRAY;
1517
use const T_OPEN_SQUARE_BRACKET;
1618
use const T_OPEN_TAG;
19+
use const T_STRING;
1720
use const T_USE;
1821
use const T_VARIABLE;
22+
use function array_key_exists;
1923
use function array_reverse;
2024
use function count;
2125
use function in_array;
@@ -55,6 +59,20 @@ public function process(File $phpcsFile, $bracketOpenerPointer): void
5559
return;
5660
}
5761

62+
if (in_array($tokens[$variablePointer]['content'], [
63+
'$GLOBALS',
64+
'$_SERVER',
65+
'$_REQUEST',
66+
'$_POST',
67+
'$_GET',
68+
'$_FILES',
69+
'$_ENV',
70+
'$_COOKIE',
71+
'$_SESSION',
72+
], true)) {
73+
return;
74+
}
75+
5876
$pointerBeforeVariable = TokenHelper::findPreviousEffective($phpcsFile, $variablePointer - 1);
5977
if (in_array($tokens[$pointerBeforeVariable]['code'], [T_OBJECT_OPERATOR, T_DOUBLE_COLON], true)) {
6078
return;
@@ -134,22 +152,78 @@ private function hasExplicitCreation(File $phpcsFile, int $scopeOpenerPointer, i
134152
continue;
135153
}
136154

137-
$assigmentPointer = TokenHelper::findNextEffective($phpcsFile, $i + 1);
138-
if ($tokens[$assigmentPointer]['code'] !== T_EQUAL) {
155+
if (!ScopeHelper::isInSameScope($phpcsFile, $variablePointer, $i)) {
139156
continue;
140157
}
141158

142-
$arrayPointer = TokenHelper::findNextEffective($phpcsFile, $assigmentPointer + 1);
143-
if (!in_array($tokens[$arrayPointer]['code'], [T_ARRAY, T_OPEN_SHORT_ARRAY], true)) {
144-
continue;
159+
$assigmentPointer = TokenHelper::findNextEffective($phpcsFile, $i + 1);
160+
if ($tokens[$assigmentPointer]['code'] === T_EQUAL) {
161+
return true;
162+
}
163+
164+
if ($this->isCreatedInList($phpcsFile, $i, $scopeOpenerPointer)) {
165+
return true;
145166
}
146167

147-
if (ScopeHelper::isInSameScope($phpcsFile, $variablePointer, $i)) {
168+
if ($this->isCreatedInForeach($phpcsFile, $i, $scopeOpenerPointer)) {
169+
return true;
170+
}
171+
172+
if ($this->isCreatedByReferencedParameterInFunctionCall($phpcsFile, $i, $scopeOpenerPointer)) {
148173
return true;
149174
}
150175
}
151176

152177
return false;
153178
}
154179

180+
private function isCreatedInList(File $phpcsFile, int $variablePointer, int $scopeOpenerPointer): bool
181+
{
182+
$tokens = $phpcsFile->getTokens();
183+
184+
$parenthesisOpenerPointer = TokenHelper::findPrevious($phpcsFile, [T_OPEN_PARENTHESIS, T_OPEN_SHORT_ARRAY, T_OPEN_SQUARE_BRACKET], $variablePointer - 1, $scopeOpenerPointer);
185+
if ($parenthesisOpenerPointer === null) {
186+
return false;
187+
}
188+
189+
if ($tokens[$parenthesisOpenerPointer]['code'] === T_OPEN_PARENTHESIS) {
190+
if ($tokens[$parenthesisOpenerPointer]['parenthesis_closer'] < $variablePointer) {
191+
return false;
192+
}
193+
194+
$pointerBeforeParenthesisOpener = TokenHelper::findPreviousEffective($phpcsFile, $parenthesisOpenerPointer - 1);
195+
return $tokens[$pointerBeforeParenthesisOpener]['code'] === T_LIST;
196+
}
197+
198+
return $tokens[$parenthesisOpenerPointer]['bracket_closer'] > $variablePointer;
199+
}
200+
201+
private function isCreatedInForeach(File $phpcsFile, int $variablePointer, int $scopeOpenerPointer): bool
202+
{
203+
$tokens = $phpcsFile->getTokens();
204+
205+
$parenthesisOpenerPointer = TokenHelper::findPrevious($phpcsFile, T_OPEN_PARENTHESIS, $variablePointer - 1, $scopeOpenerPointer);
206+
return $parenthesisOpenerPointer !== null
207+
&& array_key_exists('parenthesis_owner', $tokens[$parenthesisOpenerPointer])
208+
&& $tokens[$tokens[$parenthesisOpenerPointer]['parenthesis_owner']]['code'] === T_FOREACH
209+
&& $tokens[$parenthesisOpenerPointer]['parenthesis_closer'] > $variablePointer;
210+
}
211+
212+
private function isCreatedByReferencedParameterInFunctionCall(File $phpcsFile, int $variablePointer, int $scopeOpenerPointer): bool
213+
{
214+
$tokens = $phpcsFile->getTokens();
215+
216+
$parenthesisOpenerPointer = TokenHelper::findPrevious($phpcsFile, T_OPEN_PARENTHESIS, $variablePointer - 1, $scopeOpenerPointer);
217+
218+
if (
219+
$parenthesisOpenerPointer === null
220+
|| $tokens[$parenthesisOpenerPointer]['parenthesis_closer'] < $variablePointer
221+
) {
222+
return false;
223+
}
224+
225+
$pointerBeforeParenthesisOpener = TokenHelper::findPreviousEffective($phpcsFile, $parenthesisOpenerPointer - 1);
226+
return $tokens[$pointerBeforeParenthesisOpener]['code'] === T_STRING;
227+
}
228+
155229
}

tests/Sniffs/Arrays/DisallowImplicitArrayCreationSniffTest.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,12 @@ public function testErrors(): void
1717
{
1818
$report = self::checkFile(__DIR__ . '/data/disallowImplicitArrayCreationErrors.php');
1919

20-
self::assertSame(5, $report->getErrorCount());
20+
self::assertSame(4, $report->getErrorCount());
2121

2222
self::assertSniffError($report, 3, DisallowImplicitArrayCreationSniff::CODE_IMPLICIT_ARRAY_CREATION_USED);
2323
self::assertSniffError($report, 7, DisallowImplicitArrayCreationSniff::CODE_IMPLICIT_ARRAY_CREATION_USED);
2424
self::assertSniffError($report, 13, DisallowImplicitArrayCreationSniff::CODE_IMPLICIT_ARRAY_CREATION_USED);
25-
self::assertSniffError($report, 19, DisallowImplicitArrayCreationSniff::CODE_IMPLICIT_ARRAY_CREATION_USED);
26-
self::assertSniffError($report, 26, DisallowImplicitArrayCreationSniff::CODE_IMPLICIT_ARRAY_CREATION_USED);
25+
self::assertSniffError($report, 20, DisallowImplicitArrayCreationSniff::CODE_IMPLICIT_ARRAY_CREATION_USED);
2726
}
2827

2928
}

tests/Sniffs/Arrays/data/disallowImplicitArrayCreationErrors.php

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,6 @@ function ()
77
$a[] = 'a';
88
};
99

10-
function invalidExplicitCreation()
11-
{
12-
$a = true;
13-
$a[] = 'a';
14-
}
15-
1610
function differentVariable()
1711
{
1812
$b = [];

tests/Sniffs/Arrays/data/disallowImplicitArrayCreationNoErrors.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,40 @@ function longArray()
4646
$a = array();
4747
$a[] = 'a';
4848
}
49+
50+
function createByFunctionCall()
51+
{
52+
$a = array_map(function () {
53+
}, []);
54+
$a[] = 'a';
55+
}
56+
57+
function globalArrays()
58+
{
59+
$_SERVER['x'] = 'x';
60+
}
61+
62+
function createdInForeach($values)
63+
{
64+
foreach ($values as $value) {
65+
$value[] = true;
66+
}
67+
}
68+
69+
function createdByList($values)
70+
{
71+
list($value) = $values;
72+
$value[] = 'a';
73+
}
74+
75+
function createdByShortList($values)
76+
{
77+
[$value] = $values;
78+
$value[] = 'a';
79+
}
80+
81+
function createdByReferencedParameterInFunctionCall($query)
82+
{
83+
parse_str($query, $arguments);
84+
$arguments[] = 'a';
85+
}

0 commit comments

Comments
 (0)