Skip to content

Commit 382ad27

Browse files
committed
Tokenizer/PHP: bug fix for short list tokens in control structures without braces
Originally reported in sirbrillig/phpcs-variable-analysis 263. When the short list syntax would be used as the first contents within a control structure without braces, the square brackets would be tokenized as plain square brackets, not as short array brackets. ```php if ( true ) [ $a ] = [ 'hi' ]; // The first `[` in this line was tokenized incorrectly. return $a ?? ''; ``` Fixed now by checking whether the preceding parenthesis closer has an owner and if that owner could be a scope owner. Includes unit tests. Includes updating the data providers to use named data sets to allow for easier debugging.
1 parent f3a8342 commit 382ad27

File tree

3 files changed

+54
-32
lines changed

3 files changed

+54
-32
lines changed

src/Tokenizers/PHP.php

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2688,13 +2688,18 @@ protected function processAdditional()
26882688
}
26892689

26902690
if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false) {
2691-
if (isset($allowed[$this->tokens[$x]['code']]) === false) {
2691+
// Allow for control structures without braces.
2692+
if (($this->tokens[$x]['code'] === T_CLOSE_PARENTHESIS
2693+
&& isset($this->tokens[$x]['parenthesis_owner']) === true
2694+
&& isset(Util\Tokens::$scopeOpeners[$this->tokens[$this->tokens[$x]['parenthesis_owner']]['code']]) === true)
2695+
|| isset($allowed[$this->tokens[$x]['code']]) === false
2696+
) {
26922697
$isShortArray = true;
26932698
}
26942699

26952700
break;
26962701
}
2697-
}
2702+
}//end for
26982703

26992704
if ($isShortArray === true) {
27002705
$this->tokens[$i]['code'] = T_OPEN_SHORT_ARRAY;

tests/Core/Tokenizer/ShortArrayTest.inc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,20 @@ echo [1, 2, 3][0];
9292
/* testArrayWithinFunctionCall */
9393
$var = functionCall([$x, $y]);
9494

95+
if ( true ) {
96+
/* testShortListDeclarationAfterBracedControlStructure */
97+
[ $a ] = [ 'hi' ];
98+
}
99+
100+
if ( true )
101+
/* testShortListDeclarationAfterNonBracedControlStructure */
102+
[ $a ] = [ 'hi' ];
103+
104+
if ( true ) :
105+
/* testShortListDeclarationAfterAlternativeControlStructure */
106+
[ $a ] = [ 'hi' ];
107+
endif;
108+
95109
/* testLiveCoding */
96110
// Intentional parse error. This has to be the last test in the file.
97111
$array = [

tests/Core/Tokenizer/ShortArrayTest.php

Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -52,29 +52,29 @@ public function testSquareBrackets($testMarker)
5252
public function dataSquareBrackets()
5353
{
5454
return [
55-
['/* testArrayAccess1 */'],
56-
['/* testArrayAccess2 */'],
57-
['/* testArrayAssignment */'],
58-
['/* testFunctionCallDereferencing */'],
59-
['/* testMethodCallDereferencing */'],
60-
['/* testStaticMethodCallDereferencing */'],
61-
['/* testPropertyDereferencing */'],
62-
['/* testPropertyDereferencingWithInaccessibleName */'],
63-
['/* testStaticPropertyDereferencing */'],
64-
['/* testStringDereferencing */'],
65-
['/* testStringDereferencingDoubleQuoted */'],
66-
['/* testConstantDereferencing */'],
67-
['/* testClassConstantDereferencing */'],
68-
['/* testMagicConstantDereferencing */'],
69-
['/* testArrayAccessCurlyBraces */'],
70-
['/* testArrayLiteralDereferencing */'],
71-
['/* testShortArrayLiteralDereferencing */'],
72-
['/* testClassMemberDereferencingOnInstantiation1 */'],
73-
['/* testClassMemberDereferencingOnInstantiation2 */'],
74-
['/* testClassMemberDereferencingOnClone */'],
75-
['/* testNullsafeMethodCallDereferencing */'],
76-
['/* testInterpolatedStringDereferencing */'],
77-
['/* testLiveCoding */'],
55+
'array access 1' => ['/* testArrayAccess1 */'],
56+
'array access 2' => ['/* testArrayAccess2 */'],
57+
'array assignment' => ['/* testArrayAssignment */'],
58+
'function call dereferencing' => ['/* testFunctionCallDereferencing */'],
59+
'method call dereferencing' => ['/* testMethodCallDereferencing */'],
60+
'static method call dereferencing' => ['/* testStaticMethodCallDereferencing */'],
61+
'property dereferencing' => ['/* testPropertyDereferencing */'],
62+
'property dereferencing with inaccessable name' => ['/* testPropertyDereferencingWithInaccessibleName */'],
63+
'static property dereferencing' => ['/* testStaticPropertyDereferencing */'],
64+
'string dereferencing single quotes' => ['/* testStringDereferencing */'],
65+
'string dereferencing double quotes' => ['/* testStringDereferencingDoubleQuoted */'],
66+
'global constant dereferencing' => ['/* testConstantDereferencing */'],
67+
'class constant dereferencing' => ['/* testClassConstantDereferencing */'],
68+
'magic constant dereferencing' => ['/* testMagicConstantDereferencing */'],
69+
'array access with curly braces' => ['/* testArrayAccessCurlyBraces */'],
70+
'array literal dereferencing' => ['/* testArrayLiteralDereferencing */'],
71+
'short array literal dereferencing' => ['/* testShortArrayLiteralDereferencing */'],
72+
'class member dereferencing on instantiation 1' => ['/* testClassMemberDereferencingOnInstantiation1 */'],
73+
'class member dereferencing on instantiation 2' => ['/* testClassMemberDereferencingOnInstantiation2 */'],
74+
'class member dereferencing on clone' => ['/* testClassMemberDereferencingOnClone */'],
75+
'nullsafe method call dereferencing' => ['/* testNullsafeMethodCallDereferencing */'],
76+
'interpolated string dereferencing' => ['/* testInterpolatedStringDereferencing */'],
77+
'live coding' => ['/* testLiveCoding */'],
7878
];
7979

8080
}//end dataSquareBrackets()
@@ -117,13 +117,16 @@ public function testShortArrays($testMarker)
117117
public function dataShortArrays()
118118
{
119119
return [
120-
['/* testShortArrayDeclarationEmpty */'],
121-
['/* testShortArrayDeclarationWithOneValue */'],
122-
['/* testShortArrayDeclarationWithMultipleValues */'],
123-
['/* testShortArrayDeclarationWithDereferencing */'],
124-
['/* testShortListDeclaration */'],
125-
['/* testNestedListDeclaration */'],
126-
['/* testArrayWithinFunctionCall */'],
120+
'short array empty' => ['/* testShortArrayDeclarationEmpty */'],
121+
'short array with value' => ['/* testShortArrayDeclarationWithOneValue */'],
122+
'short array with values' => ['/* testShortArrayDeclarationWithMultipleValues */'],
123+
'short array with dereferencing' => ['/* testShortArrayDeclarationWithDereferencing */'],
124+
'short list' => ['/* testShortListDeclaration */'],
125+
'short list nested' => ['/* testNestedListDeclaration */'],
126+
'short array within function call' => ['/* testArrayWithinFunctionCall */'],
127+
'short list after braced control structure' => ['/* testShortListDeclarationAfterBracedControlStructure */'],
128+
'short list after non-braced control structure' => ['/* testShortListDeclarationAfterNonBracedControlStructure */'],
129+
'short list after alternative control structure' => ['/* testShortListDeclarationAfterAlternativeControlStructure */'],
127130
];
128131

129132
}//end dataShortArrays()

0 commit comments

Comments
 (0)