Skip to content

Commit

Permalink
Tokenizer/PHP: fix handling of "DNF look-a-likes" in named parameters (
Browse files Browse the repository at this point in the history
…#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
  • Loading branch information
jrfnl committed May 21, 2024
1 parent d49587c commit 808dff8
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 6 deletions.
12 changes: 11 additions & 1 deletion src/Tokenizers/PHP.php
Original file line number Diff line number Diff line change
Expand Up @@ -3212,7 +3212,17 @@ protected function processAdditional()
}

if ($suspectedType === 'return' && $this->tokens[$x]['code'] === T_COLON) {
$confirmed = true;
// Make sure this is not the colon from a parameter name.
for ($y = ($x - 1); $y > 0; $y--) {
if (isset(Tokens::$emptyTokens[$this->tokens[$y]['code']]) === false) {
break;
}
}

if ($this->tokens[$y]['code'] !== T_PARAM_NAME) {
$confirmed = true;
}

break;
}

Expand Down
8 changes: 8 additions & 0 deletions tests/Core/Tokenizer/PHP/DNFTypesTest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,14 @@ list(&$a, &$b) = $array;
/* testParensNoOwnerFunctionCallwithDNFLookALikeParam */
$obj->static((CONST_A&CONST_B)|CONST_C | $var);

/* testParensNoOwnerFunctionCallWithDNFLookALikeNamedParamPlain */
callMe(label: false);

/* testParensNoOwnerFunctionCallWithDNFLookALikeNamedParamUnion */
callMe(label: CONST_A | CONST_B);

/* testParensNoOwnerFunctionCallWithDNFLookALikeNamedParamIntersect */
callMe(label: CONST_A & CONST_B);

/*
* DNF parentheses.
Expand Down
27 changes: 22 additions & 5 deletions tests/Core/Tokenizer/PHP/DNFTypesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,19 @@ public function testNormalParentheses($testMarker, $skipCheckInside=false)
$this->assertSame(T_CLOSE_PARENTHESIS, $closer['code'], 'Token tokenized as '.$closer['type'].', not T_CLOSE_PARENTHESIS (code)');
$this->assertSame('T_CLOSE_PARENTHESIS', $closer['type'], 'Token tokenized as '.$closer['type'].', not T_CLOSE_PARENTHESIS (type)');

for ($i = ($openPtr + 1); $i < $closePtr; $i++) {
// If there are ampersands, make sure these are tokenized as bitwise and.
if ($skipCheckInside === false && $tokens[$i]['content'] === '&') {
$this->assertSame(T_BITWISE_AND, $tokens[$i]['code'], 'Token tokenized as '.$tokens[$i]['type'].', not T_BITWISE_AND (code)');
$this->assertSame('T_BITWISE_AND', $tokens[$i]['type'], 'Token tokenized as '.$tokens[$i]['type'].', not T_BITWISE_AND (type)');
if ($skipCheckInside === false) {
for ($i = ($openPtr + 1); $i < $closePtr; $i++) {
// If there are ampersands, make sure these are tokenized as bitwise and.
if ($tokens[$i]['content'] === '&') {
$this->assertSame(T_BITWISE_AND, $tokens[$i]['code'], 'Token tokenized as '.$tokens[$i]['type'].', not T_BITWISE_AND (code)');
$this->assertSame('T_BITWISE_AND', $tokens[$i]['type'], 'Token tokenized as '.$tokens[$i]['type'].', not T_BITWISE_AND (type)');
}

// If there are pipes, make sure these are tokenized as bitwise or.
if ($tokens[$i]['content'] === '|') {
$this->assertSame(T_BITWISE_OR, $tokens[$i]['code'], 'Token tokenized as '.$tokens[$i]['type'].', not T_BITWISE_OR (code)');
$this->assertSame('T_BITWISE_OR', $tokens[$i]['type'], 'Token tokenized as '.$tokens[$i]['type'].', not T_BITWISE_OR (type)');
}
}
}

Expand Down Expand Up @@ -141,6 +149,15 @@ public static function dataNormalParentheses()
'parens without owner, function call with DNF look-a-like param' => [
'testMarker' => '/* testParensNoOwnerFunctionCallwithDNFLookALikeParam */',
],
'parens without owner, function call, named param' => [
'testMarker' => '/* testParensNoOwnerFunctionCallWithDNFLookALikeNamedParamPlain */',
],
'parens without owner, function call, named param + bitwise or' => [
'testMarker' => '/* testParensNoOwnerFunctionCallWithDNFLookALikeNamedParamUnion */',
],
'parens without owner, function call, named param + bitwise and' => [
'testMarker' => '/* testParensNoOwnerFunctionCallWithDNFLookALikeNamedParamIntersect */',
],

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

0 comments on commit 808dff8

Please sign in to comment.