Skip to content

Commit 808dff8

Browse files
authored
Tokenizer/PHP: fix handling of "DNF look-a-likes" in named parameters (#507)
The last parameter in a function call using named arguments could be confused with a return type by the tokenizer layer handling type declarations. The net effect of this was that the close parenthesis of the function call would be retokenized to `T_TYPE_CLOSE_PARENTHESIS`, which is incorrect and would lead to sniffs incorrectly acting on that information. Fixed now. Includes tests. Fixes 504 Fixes 505
1 parent d49587c commit 808dff8

File tree

3 files changed

+41
-6
lines changed

3 files changed

+41
-6
lines changed

src/Tokenizers/PHP.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3212,7 +3212,17 @@ protected function processAdditional()
32123212
}
32133213

32143214
if ($suspectedType === 'return' && $this->tokens[$x]['code'] === T_COLON) {
3215-
$confirmed = true;
3215+
// Make sure this is not the colon from a parameter name.
3216+
for ($y = ($x - 1); $y > 0; $y--) {
3217+
if (isset(Tokens::$emptyTokens[$this->tokens[$y]['code']]) === false) {
3218+
break;
3219+
}
3220+
}
3221+
3222+
if ($this->tokens[$y]['code'] !== T_PARAM_NAME) {
3223+
$confirmed = true;
3224+
}
3225+
32163226
break;
32173227
}
32183228

tests/Core/Tokenizer/PHP/DNFTypesTest.inc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,14 @@ list(&$a, &$b) = $array;
4444
/* testParensNoOwnerFunctionCallwithDNFLookALikeParam */
4545
$obj->static((CONST_A&CONST_B)|CONST_C | $var);
4646

47+
/* testParensNoOwnerFunctionCallWithDNFLookALikeNamedParamPlain */
48+
callMe(label: false);
49+
50+
/* testParensNoOwnerFunctionCallWithDNFLookALikeNamedParamUnion */
51+
callMe(label: CONST_A | CONST_B);
52+
53+
/* testParensNoOwnerFunctionCallWithDNFLookALikeNamedParamIntersect */
54+
callMe(label: CONST_A & CONST_B);
4755

4856
/*
4957
* DNF parentheses.

tests/Core/Tokenizer/PHP/DNFTypesTest.php

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,19 @@ public function testNormalParentheses($testMarker, $skipCheckInside=false)
4646
$this->assertSame(T_CLOSE_PARENTHESIS, $closer['code'], 'Token tokenized as '.$closer['type'].', not T_CLOSE_PARENTHESIS (code)');
4747
$this->assertSame('T_CLOSE_PARENTHESIS', $closer['type'], 'Token tokenized as '.$closer['type'].', not T_CLOSE_PARENTHESIS (type)');
4848

49-
for ($i = ($openPtr + 1); $i < $closePtr; $i++) {
50-
// If there are ampersands, make sure these are tokenized as bitwise and.
51-
if ($skipCheckInside === false && $tokens[$i]['content'] === '&') {
52-
$this->assertSame(T_BITWISE_AND, $tokens[$i]['code'], 'Token tokenized as '.$tokens[$i]['type'].', not T_BITWISE_AND (code)');
53-
$this->assertSame('T_BITWISE_AND', $tokens[$i]['type'], 'Token tokenized as '.$tokens[$i]['type'].', not T_BITWISE_AND (type)');
49+
if ($skipCheckInside === false) {
50+
for ($i = ($openPtr + 1); $i < $closePtr; $i++) {
51+
// If there are ampersands, make sure these are tokenized as bitwise and.
52+
if ($tokens[$i]['content'] === '&') {
53+
$this->assertSame(T_BITWISE_AND, $tokens[$i]['code'], 'Token tokenized as '.$tokens[$i]['type'].', not T_BITWISE_AND (code)');
54+
$this->assertSame('T_BITWISE_AND', $tokens[$i]['type'], 'Token tokenized as '.$tokens[$i]['type'].', not T_BITWISE_AND (type)');
55+
}
56+
57+
// If there are pipes, make sure these are tokenized as bitwise or.
58+
if ($tokens[$i]['content'] === '|') {
59+
$this->assertSame(T_BITWISE_OR, $tokens[$i]['code'], 'Token tokenized as '.$tokens[$i]['type'].', not T_BITWISE_OR (code)');
60+
$this->assertSame('T_BITWISE_OR', $tokens[$i]['type'], 'Token tokenized as '.$tokens[$i]['type'].', not T_BITWISE_OR (type)');
61+
}
5462
}
5563
}
5664

@@ -141,6 +149,15 @@ public static function dataNormalParentheses()
141149
'parens without owner, function call with DNF look-a-like param' => [
142150
'testMarker' => '/* testParensNoOwnerFunctionCallwithDNFLookALikeParam */',
143151
],
152+
'parens without owner, function call, named param' => [
153+
'testMarker' => '/* testParensNoOwnerFunctionCallWithDNFLookALikeNamedParamPlain */',
154+
],
155+
'parens without owner, function call, named param + bitwise or' => [
156+
'testMarker' => '/* testParensNoOwnerFunctionCallWithDNFLookALikeNamedParamUnion */',
157+
],
158+
'parens without owner, function call, named param + bitwise and' => [
159+
'testMarker' => '/* testParensNoOwnerFunctionCallWithDNFLookALikeNamedParamIntersect */',
160+
],
144161

145162
'parens without owner in OO const default value' => [
146163
'testMarker' => '/* testParensNoOwnerOOConstDefaultValue */',

0 commit comments

Comments
 (0)