Skip to content

Commit 130fdcb

Browse files
committed
Tokenizer/PHP: arrow function tokenization broken when true/false used 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.
1 parent 4d15227 commit 130fdcb

File tree

3 files changed

+58
-0
lines changed

3 files changed

+58
-0
lines changed

src/Tokenizers/PHP.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2540,6 +2540,8 @@ protected function processAdditional()
25402540
T_NAME_QUALIFIED => T_NAME_QUALIFIED,
25412541
T_NAME_RELATIVE => T_NAME_RELATIVE,
25422542
T_NULL => T_NULL,
2543+
T_TRUE => T_TRUE,
2544+
T_FALSE => T_FALSE,
25432545
T_NULLABLE => T_NULLABLE,
25442546
T_PARENT => T_PARENT,
25452547
T_SELF => T_SELF,

tests/Core/Tokenizer/BackfillFnTokenTest.inc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,27 @@ fn(array $a) : array => $a;
9898
/* testStaticReturnType */
9999
fn(array $a) : static => $a;
100100

101+
/* testFalseReturnType */
102+
fn(array $a) : false => $a;
103+
104+
/* testTrueReturnType */
105+
fn(array $a) : True => $a;
106+
107+
/* testNullReturnType */
108+
fn(array $a) : null => $a;
109+
101110
/* testUnionParamType */
102111
$arrowWithUnionParam = fn(int|float $param) : SomeClass => new SomeClass($param);
103112

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

116+
/* testUnionReturnTypeWithTrue */
117+
$arrowWithUnionReturn = fn($param) : int|true => $param | 10;
118+
119+
/* testUnionReturnTypeWithFalse */
120+
$arrowWithUnionReturn = fn($param) : string|FALSE => $param | 10;
121+
107122
/* testTernary */
108123
$fn = fn($a) => $a ? /* testTernaryThen */ fn() : string => 'a' : /* testTernaryElse */ fn() : string => 'b';
109124

tests/Core/Tokenizer/BackfillFnTokenTest.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,15 @@ public static function dataKeywordReturnTypes()
493493
'static' => [
494494
'testMarker' => '/* testStaticReturnType */',
495495
],
496+
'false' => [
497+
'testMarker' => '/* testFalseReturnType */',
498+
],
499+
'true' => [
500+
'testMarker' => '/* testTrueReturnType */',
501+
],
502+
'null' => [
503+
'testMarker' => '/* testNullReturnType */',
504+
],
496505
];
497506

498507
}//end dataKeywordReturnTypes()
@@ -530,6 +539,38 @@ public function testUnionReturnType()
530539
}//end testUnionReturnType()
531540

532541

542+
/**
543+
* Test arrow function with a union return type.
544+
*
545+
* @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
546+
*
547+
* @return void
548+
*/
549+
public function testUnionReturnTypeWithTrue()
550+
{
551+
$token = $this->getTargetToken('/* testUnionReturnTypeWithTrue */', T_FN);
552+
$this->backfillHelper($token);
553+
$this->scopePositionTestHelper($token, 11, 18);
554+
555+
}//end testUnionReturnTypeWithTrue()
556+
557+
558+
/**
559+
* Test arrow function with a union return type.
560+
*
561+
* @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
562+
*
563+
* @return void
564+
*/
565+
public function testUnionReturnTypeWithFalse()
566+
{
567+
$token = $this->getTargetToken('/* testUnionReturnTypeWithFalse */', T_FN);
568+
$this->backfillHelper($token);
569+
$this->scopePositionTestHelper($token, 11, 18);
570+
571+
}//end testUnionReturnTypeWithFalse()
572+
573+
533574
/**
534575
* Test arrow functions used in ternary operators.
535576
*

0 commit comments

Comments
 (0)