Skip to content

Commit 332e094

Browse files
committed
PHP Tokenizer: fix "undefined offset" notice during live coding / arrow functions
Defensive coding for the arrow functions backfill. This fixes a number of "undefined index" PHP notices when arrow functions are being declared during live coding. ``` Notice: Undefined offset: 561 in /path/to/PHP_CodeSniffer/src/Tokenizers/PHP.php on line 1807 Notice: Undefined offset: 561 in /path/to/PHP_CodeSniffer/src/Tokenizers/PHP.php on line 1808 Notice: Undefined offset: 561 in /path/to/PHP_CodeSniffer/src/Tokenizers/PHP.php on line 1815 ``` It also fixes a bug where an inner for loop was using the wrong variable for the condition part. Includes adjusted unit test + additional unit test covering all changes. Includes minor documentation fix.
1 parent c2494e3 commit 332e094

File tree

3 files changed

+32
-4
lines changed

3 files changed

+32
-4
lines changed

src/Tokenizers/PHP.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1801,9 +1801,9 @@ protected function processAdditional()
18011801
}//end if
18021802

18031803
continue;
1804-
} else if ($this->tokens[$i]['code'] === T_FN) {
1804+
} else if ($this->tokens[$i]['code'] === T_FN && isset($this->tokens[($i + 1)]) === true) {
18051805
// Possible arrow function.
1806-
for ($x = ($i + 1); $i < $numTokens; $x++) {
1806+
for ($x = ($i + 1); $x < $numTokens; $x++) {
18071807
if (isset(Util\Tokens::$emptyTokens[$this->tokens[$x]['code']]) === false
18081808
&& $this->tokens[$x]['code'] !== T_BITWISE_AND
18091809
) {
@@ -1812,7 +1812,7 @@ protected function processAdditional()
18121812
}
18131813
}
18141814

1815-
if ($this->tokens[$x]['code'] === T_OPEN_PARENTHESIS) {
1815+
if (isset($this->tokens[$x]) === true && $this->tokens[$x]['code'] === T_OPEN_PARENTHESIS) {
18161816
$ignore = Util\Tokens::$emptyTokens;
18171817
$ignore += [
18181818
T_STRING => T_STRING,

tests/Core/Tokenizer/BackfillFnTokenTest.inc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,7 @@ fn(callable $a) : callable => $a;
6969

7070
/* testTernary */
7171
$fn = fn($a) => $a ? /* testTernaryThen */ fn() : string => 'a' : /* testTernaryElse */ fn() : string => 'b';
72+
73+
/* testLiveCoding */
74+
// Intentional parse error. This has to be the last test in the file.
75+
$fn = fn

tests/Core/Tokenizer/BackfillFnTokenTest.php

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,31 @@ public function testTernary()
552552

553553

554554
/**
555-
* Test that anonymous class tokens without parenthesis do not get assigned a parenthesis owner.
555+
* Test that the backfill presumes T_FN during live coding, but doesn't set the additional index keys.
556+
*
557+
* @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
558+
*
559+
* @return void
560+
*/
561+
public function testLiveCoding()
562+
{
563+
$tokens = self::$phpcsFile->getTokens();
564+
565+
$token = $this->getTargetToken('/* testLiveCoding */', [T_STRING, T_FN]);
566+
$this->assertSame($tokens[$token]['code'], T_FN, 'Token not tokenized as T_FN');
567+
568+
$this->assertArrayNotHasKey('scope_condition', $tokens[$token], 'Scope condition is set');
569+
$this->assertArrayNotHasKey('scope_opener', $tokens[$token], 'Scope opener is set');
570+
$this->assertArrayNotHasKey('scope_closer', $tokens[$token], 'Scope closer is set');
571+
$this->assertArrayNotHasKey('parenthesis_owner', $tokens[$token], 'Parenthesis owner is set');
572+
$this->assertArrayNotHasKey('parenthesis_opener', $tokens[$token], 'Parenthesis opener is set');
573+
$this->assertArrayNotHasKey('parenthesis_closer', $tokens[$token], 'Parenthesis closer is set');
574+
575+
}//end testLiveCoding()
576+
577+
578+
/**
579+
* Helper function to check that all token keys are correctly set for T_FN tokens.
556580
*
557581
* @param string $token The T_FN token to check.
558582
*

0 commit comments

Comments
 (0)