Skip to content

Commit b84c541

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

File tree

4 files changed

+49
-0
lines changed

4 files changed

+49
-0
lines changed

package.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ http://pear.php.net/dtd/package-2.0.xsd">
3636
- Fixed bug #3316 : Arrow function not tokenized correctly when using null in union type
3737
- Fixed bug #3317 : Problem with how phpcs handles ignored files when running in parallel
3838
-- Thanks to Emil Andersson for the patch
39+
- Fixed bug #3324 : PHPCS hangs processing some nested arrow functions inside a function call
3940
</notes>
4041
<contents>
4142
<dir name="/">

src/Tokenizers/PHP.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2355,6 +2355,16 @@ protected function processAdditional()
23552355
break;
23562356
}
23572357

2358+
if ($inTernary === false
2359+
&& isset($this->tokens[$scopeCloser]['scope_closer'], $this->tokens[$scopeCloser]['scope_condition']) === true
2360+
&& $scopeCloser === $this->tokens[$scopeCloser]['scope_closer']
2361+
&& $this->tokens[$this->tokens[$scopeCloser]['scope_condition']]['code'] === T_FN
2362+
) {
2363+
// Found a nested arrow function that already has the closer set and is in
2364+
// the same scope as us, so we can use its closer.
2365+
break;
2366+
}
2367+
23582368
if (isset($this->tokens[$scopeCloser]['scope_closer']) === true
23592369
&& $this->tokens[$scopeCloser]['code'] !== T_INLINE_ELSE
23602370
&& $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)