diff --git a/package.xml b/package.xml
index dd161175ae..f0cd7498fc 100644
--- a/package.xml
+++ b/package.xml
@@ -26,6 +26,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
BSD 3-Clause License
+ - Fixed bug #2926 : phpcs hangs when using arrow functions that return heredoc
- Fixed bug #2943 : Redundant semicolon added to a file when fixing PSR2.Files.ClosingTag.NotAllowed
diff --git a/src/Tokenizers/PHP.php b/src/Tokenizers/PHP.php
index 993d937420..2fc86a86d6 100644
--- a/src/Tokenizers/PHP.php
+++ b/src/Tokenizers/PHP.php
@@ -1868,6 +1868,8 @@ protected function processAdditional()
if (isset($this->tokens[$scopeCloser]['scope_closer']) === true
&& $this->tokens[$scopeCloser]['code'] !== T_INLINE_ELSE
+ && $this->tokens[$scopeCloser]['code'] !== T_END_HEREDOC
+ && $this->tokens[$scopeCloser]['code'] !== T_END_NOWDOC
) {
// We minus 1 here in case the closer can be shared with us.
$scopeCloser = ($this->tokens[$scopeCloser]['scope_closer'] - 1);
diff --git a/tests/Core/Tokenizer/BackfillFnTokenTest.inc b/tests/Core/Tokenizer/BackfillFnTokenTest.inc
index ab57fbb621..7714a78680 100644
--- a/tests/Core/Tokenizer/BackfillFnTokenTest.inc
+++ b/tests/Core/Tokenizer/BackfillFnTokenTest.inc
@@ -12,6 +12,11 @@ $fn1 = fn ($x) => $x + $y;
/* testComment */
$fn1 = fn /* comment here */ ($x) => $x + $y;
+/* testHeredoc */
+$fn1 = fn() => <<getTokens();
@@ -98,7 +98,35 @@ public function testComments()
$this->assertSame($tokens[$closer]['scope_opener'], ($token + 8), 'Closer scope opener is not the arrow token');
$this->assertSame($tokens[$closer]['scope_closer'], ($token + 15), 'Closer scope closer is not the semicolon token');
- }//end testComments()
+ }//end testComment()
+
+
+ /**
+ * Test heredocs inside arrow function definitions.
+ *
+ * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
+ *
+ * @return void
+ */
+ public function testHeredoc()
+ {
+ $tokens = self::$phpcsFile->getTokens();
+
+ $token = $this->getTargetToken('/* testHeredoc */', T_FN);
+ $this->backfillHelper($token);
+
+ $this->assertSame($tokens[$token]['scope_opener'], ($token + 4), 'Scope opener is not the arrow token');
+ $this->assertSame($tokens[$token]['scope_closer'], ($token + 9), 'Scope closer is not the semicolon token');
+
+ $opener = $tokens[$token]['scope_opener'];
+ $this->assertSame($tokens[$opener]['scope_opener'], ($token + 4), 'Opener scope opener is not the arrow token');
+ $this->assertSame($tokens[$opener]['scope_closer'], ($token + 9), 'Opener scope closer is not the semicolon token');
+
+ $closer = $tokens[$token]['scope_opener'];
+ $this->assertSame($tokens[$closer]['scope_opener'], ($token + 4), 'Closer scope opener is not the arrow token');
+ $this->assertSame($tokens[$closer]['scope_closer'], ($token + 9), 'Closer scope closer is not the semicolon token');
+
+ }//end testHeredoc()
/**