From bee2eef87e412c74b783910db295b34ab18813a4 Mon Sep 17 00:00:00 2001 From: Greg Sherwood Date: Mon, 26 Apr 2021 15:25:52 +1000 Subject: [PATCH] Fixed bug #3324 : PHPCS hangs processing some nested arrow functions inside a function call --- src/Tokenizers/PHP.php | 10 ++++++ tests/Core/Tokenizer/BackfillFnTokenTest.inc | 3 ++ tests/Core/Tokenizer/BackfillFnTokenTest.php | 35 ++++++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/src/Tokenizers/PHP.php b/src/Tokenizers/PHP.php index 122a88dcba..de849049fa 100644 --- a/src/Tokenizers/PHP.php +++ b/src/Tokenizers/PHP.php @@ -2180,6 +2180,16 @@ protected function processAdditional() break; } + if ($inTernary === false + && isset($this->tokens[$scopeCloser]['scope_closer'], $this->tokens[$scopeCloser]['scope_condition']) === true + && $scopeCloser === $this->tokens[$scopeCloser]['scope_closer'] + && $this->tokens[$this->tokens[$scopeCloser]['scope_condition']]['code'] === T_FN + ) { + // Found a nested arrow function that already has the closer set and is in + // the same scope as us, so we can use its closer. + break; + } + if (isset($this->tokens[$scopeCloser]['scope_closer']) === true && $this->tokens[$scopeCloser]['code'] !== T_INLINE_ELSE && $this->tokens[$scopeCloser]['code'] !== T_END_HEREDOC diff --git a/tests/Core/Tokenizer/BackfillFnTokenTest.inc b/tests/Core/Tokenizer/BackfillFnTokenTest.inc index 42732095e4..a69bd998c9 100644 --- a/tests/Core/Tokenizer/BackfillFnTokenTest.inc +++ b/tests/Core/Tokenizer/BackfillFnTokenTest.inc @@ -23,6 +23,9 @@ function fn() {} /* testNestedOuter */ $fn = fn($x) => /* testNestedInner */ fn($y) => $x * $y + $z; +/* testNestedSharedCloserOuter */ +$foo = foo(fn() => /* testNestedSharedCloserInner */ fn() => bar() === true); + /* testFunctionCall */ $extended = fn($c) => $callable($factory($c), $c); diff --git a/tests/Core/Tokenizer/BackfillFnTokenTest.php b/tests/Core/Tokenizer/BackfillFnTokenTest.php index f4cd20ee48..3e72b3e2d2 100644 --- a/tests/Core/Tokenizer/BackfillFnTokenTest.php +++ b/tests/Core/Tokenizer/BackfillFnTokenTest.php @@ -128,6 +128,41 @@ public function testNestedInner() }//end testNestedInner() + /** + * Test nested arrow functions with a shared closer. + * + * @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional + * + * @return void + */ + public function testNestedSharedCloser() + { + $tokens = self::$phpcsFile->getTokens(); + + $token = $this->getTargetToken('/* testNestedSharedCloserOuter */', T_FN); + $this->backfillHelper($token); + $this->scopePositionTestHelper($token, 4, 20); + + $token = $this->getTargetToken('/* testNestedSharedCloserInner */', T_FN); + $this->backfillHelper($token, true); + + $expectedScopeOpener = ($token + 4); + $expectedScopeCloser = ($token + 12); + + $this->assertSame($expectedScopeOpener, $tokens[$token]['scope_opener'], 'Scope opener for "inner" arrow function is not the arrow token'); + $this->assertSame($expectedScopeCloser, $tokens[$token]['scope_closer'], 'Scope closer for "inner" arrow function is not the TRUE token'); + + $opener = $tokens[$token]['scope_opener']; + $this->assertSame($expectedScopeOpener, $tokens[$opener]['scope_opener'], 'Opener scope opener for "inner" arrow function is not the arrow token'); + $this->assertSame($expectedScopeCloser, $tokens[$opener]['scope_closer'], 'Opener scope closer for "inner" arrow function is not the semicolon token'); + + $closer = $tokens[$token]['scope_closer']; + $this->assertSame(($token - 4), $tokens[$closer]['scope_opener'], 'Closer scope opener for "inner" arrow function is not the arrow token of the "outer" arrow function (shared scope closer)'); + $this->assertSame($expectedScopeCloser, $tokens[$closer]['scope_closer'], 'Closer scope closer for "inner" arrow function is not the TRUE token'); + + }//end testNestedSharedCloser() + + /** * Test arrow functions that call functions. *