Skip to content

Commit

Permalink
Tokenizer/PHP: arrow function tokenization broken when true/false use…
Browse files Browse the repository at this point in the history
…d in return type

Since PHP 8.0, `false` and `null` can be included in a union return type.
As of PHP 8.2, both `true`, `false` and `null` can be used as a stand-alone return type.

The tokenizer layer handling arrow functions did not take this into account correctly. While `null` was handled correctly, `true` and `false` was not and would result in the arrow function `fn` keyword being tokenized as `T_STRING` across all PHP versions.
As a result of that, the other typical tokenizer changes related to arrow functions (`=>` as `T_FN_ARROW`, scope/parenthesis owners etc) would also not be executed correctly.

In practice, I suspect few people will have run into this bug as, after all, what's the point of declaring an arrow function which will only ever return `true` or `false` ? so in practice, it is likely to only have come into play for people using `true` or `false` as part of an arrow function union type.
All the same, PHPCS should handle this correctly.

Includes unit tests proving the bug and safeguarding the fix.
  • Loading branch information
jrfnl committed Apr 22, 2024
1 parent 46b883d commit bd6356c
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/Tokenizers/PHP.php
Original file line number Diff line number Diff line change
Expand Up @@ -2718,6 +2718,8 @@ protected function processAdditional()
T_NAMESPACE => T_NAMESPACE,
T_NS_SEPARATOR => T_NS_SEPARATOR,
T_NULL => T_NULL,
T_TRUE => T_TRUE,
T_FALSE => T_FALSE,
T_NULLABLE => T_NULLABLE,
T_PARENT => T_PARENT,
T_SELF => T_SELF,
Expand Down
15 changes: 15 additions & 0 deletions tests/Core/Tokenizer/BackfillFnTokenTest.inc
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,27 @@ fn(array $a) : array => $a;
/* testStaticReturnType */
fn(array $a) : static => $a;

/* testFalseReturnType */
fn(array $a) : false => $a;

/* testTrueReturnType */
fn(array $a) : True => $a;

/* testNullReturnType */
fn(array $a) : null => $a;

/* testUnionParamType */
$arrowWithUnionParam = fn(int|float $param) : SomeClass => new SomeClass($param);

/* testUnionReturnType */
$arrowWithUnionReturn = fn($param) : int|float => $param | 10;

/* testUnionReturnTypeWithTrue */
$arrowWithUnionReturn = fn($param) : int|true => $param | 10;

/* testUnionReturnTypeWithFalse */
$arrowWithUnionReturn = fn($param) : string|FALSE => $param | 10;

/* testTernary */
$fn = fn($a) => $a ? /* testTernaryThen */ fn() : string => 'a' : /* testTernaryElse */ fn() : string => 'b';

Expand Down
41 changes: 41 additions & 0 deletions tests/Core/Tokenizer/BackfillFnTokenTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,15 @@ public static function dataKeywordReturnTypes()
'static' => [
'testMarker' => '/* testStaticReturnType */',
],
'false' => [
'testMarker' => '/* testFalseReturnType */',
],
'true' => [
'testMarker' => '/* testTrueReturnType */',
],
'null' => [
'testMarker' => '/* testNullReturnType */',
],
];

}//end dataKeywordReturnTypes()
Expand Down Expand Up @@ -474,6 +483,38 @@ public function testUnionReturnType()
}//end testUnionReturnType()


/**
* Test arrow function with a union return type.
*
* @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
*
* @return void
*/
public function testUnionReturnTypeWithTrue()
{
$token = $this->getTargetToken('/* testUnionReturnTypeWithTrue */', T_FN);
$this->backfillHelper($token);
$this->scopePositionTestHelper($token, 11, 18);

}//end testUnionReturnTypeWithTrue()


/**
* Test arrow function with a union return type.
*
* @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
*
* @return void
*/
public function testUnionReturnTypeWithFalse()
{
$token = $this->getTargetToken('/* testUnionReturnTypeWithFalse */', T_FN);
$this->backfillHelper($token);
$this->scopePositionTestHelper($token, 11, 18);

}//end testUnionReturnTypeWithFalse()


/**
* Test arrow functions used in ternary operators.
*
Expand Down

0 comments on commit bd6356c

Please sign in to comment.