Skip to content

Commit bee2eef

Browse files
committed
Fixed bug #3324 : PHPCS hangs processing some nested arrow functions inside a function call
1 parent c2e3ba3 commit bee2eef

File tree

3 files changed

+48
-0
lines changed

3 files changed

+48
-0
lines changed

src/Tokenizers/PHP.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2180,6 +2180,16 @@ protected function processAdditional()
21802180
break;
21812181
}
21822182

2183+
if ($inTernary === false
2184+
&& isset($this->tokens[$scopeCloser]['scope_closer'], $this->tokens[$scopeCloser]['scope_condition']) === true
2185+
&& $scopeCloser === $this->tokens[$scopeCloser]['scope_closer']
2186+
&& $this->tokens[$this->tokens[$scopeCloser]['scope_condition']]['code'] === T_FN
2187+
) {
2188+
// Found a nested arrow function that already has the closer set and is in
2189+
// the same scope as us, so we can use its closer.
2190+
break;
2191+
}
2192+
21832193
if (isset($this->tokens[$scopeCloser]['scope_closer']) === true
21842194
&& $this->tokens[$scopeCloser]['code'] !== T_INLINE_ELSE
21852195
&& $this->tokens[$scopeCloser]['code'] !== T_END_HEREDOC

tests/Core/Tokenizer/BackfillFnTokenTest.inc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ function fn() {}
2323
/* testNestedOuter */
2424
$fn = fn($x) => /* testNestedInner */ fn($y) => $x * $y + $z;
2525

26+
/* testNestedSharedCloserOuter */
27+
$foo = foo(fn() => /* testNestedSharedCloserInner */ fn() => bar() === true);
28+
2629
/* testFunctionCall */
2730
$extended = fn($c) => $callable($factory($c), $c);
2831

tests/Core/Tokenizer/BackfillFnTokenTest.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,41 @@ public function testNestedInner()
128128
}//end testNestedInner()
129129

130130

131+
/**
132+
* Test nested arrow functions with a shared closer.
133+
*
134+
* @covers PHP_CodeSniffer\Tokenizers\PHP::processAdditional
135+
*
136+
* @return void
137+
*/
138+
public function testNestedSharedCloser()
139+
{
140+
$tokens = self::$phpcsFile->getTokens();
141+
142+
$token = $this->getTargetToken('/* testNestedSharedCloserOuter */', T_FN);
143+
$this->backfillHelper($token);
144+
$this->scopePositionTestHelper($token, 4, 20);
145+
146+
$token = $this->getTargetToken('/* testNestedSharedCloserInner */', T_FN);
147+
$this->backfillHelper($token, true);
148+
149+
$expectedScopeOpener = ($token + 4);
150+
$expectedScopeCloser = ($token + 12);
151+
152+
$this->assertSame($expectedScopeOpener, $tokens[$token]['scope_opener'], 'Scope opener for "inner" arrow function is not the arrow token');
153+
$this->assertSame($expectedScopeCloser, $tokens[$token]['scope_closer'], 'Scope closer for "inner" arrow function is not the TRUE token');
154+
155+
$opener = $tokens[$token]['scope_opener'];
156+
$this->assertSame($expectedScopeOpener, $tokens[$opener]['scope_opener'], 'Opener scope opener for "inner" arrow function is not the arrow token');
157+
$this->assertSame($expectedScopeCloser, $tokens[$opener]['scope_closer'], 'Opener scope closer for "inner" arrow function is not the semicolon token');
158+
159+
$closer = $tokens[$token]['scope_closer'];
160+
$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)');
161+
$this->assertSame($expectedScopeCloser, $tokens[$closer]['scope_closer'], 'Closer scope closer for "inner" arrow function is not the TRUE token');
162+
163+
}//end testNestedSharedCloser()
164+
165+
131166
/**
132167
* Test arrow functions that call functions.
133168
*

0 commit comments

Comments
 (0)