Skip to content

Commit 604b7a0

Browse files
committed
File::getDeclarationName(): prevent incorrect result during live coding
The `function` keyword for a closure is normally tokenized as `T_CLOSURE`, but will be tokenized as `T_FUNCTION` in case of an unfinished closure declaration (no scope opener/closer). For an unfinished closure with typed parameters, the `File::getDeclarationName()` method would incorrectly return the contents of the first `T_STRING` in the type declaration as if it were the name of the function. Fixed now. Includes test.
1 parent 178ed0f commit 604b7a0

File tree

3 files changed

+53
-1
lines changed

3 files changed

+53
-1
lines changed

src/Files/File.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1304,8 +1304,17 @@ public function getDeclarationName($stackPtr)
13041304
return $this->tokens[$stackPtr]['content'];
13051305
}
13061306

1307+
$stopPoint = $this->numTokens;
1308+
if (isset($this->tokens[$stackPtr]['parenthesis_opener']) === true) {
1309+
// For functions, stop searching at the parenthesis opener.
1310+
$stopPoint = $this->tokens[$stackPtr]['parenthesis_opener'];
1311+
} else if (isset($this->tokens[$stackPtr]['scope_opener']) === true) {
1312+
// For OO tokens, stop searching at the open curly.
1313+
$stopPoint = $this->tokens[$stackPtr]['scope_opener'];
1314+
}
1315+
13071316
$content = null;
1308-
for ($i = $stackPtr; $i < $this->numTokens; $i++) {
1317+
for ($i = $stackPtr; $i < $stopPoint; $i++) {
13091318
if ($this->tokens[$i]['code'] === T_STRING) {
13101319
$content = $this->tokens[$i]['content'];
13111320
break;
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?php
2+
3+
/* testLiveCoding */
4+
// Intentional parse error/live coding. This must be the only test in the file.
5+
// Safeguarding that the utility method does not confuse the `string` type with a function name.
6+
$closure = function (string $param) use ($var
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
/**
3+
* Tests for the \PHP_CodeSniffer\Files\File::getDeclarationName method.
4+
*
5+
* @author Juliette Reinders Folmer <[email protected]>
6+
* @copyright 2025 PHPCSStandards Contributors
7+
* @license https://github.com/PHPCSStandards/PHP_CodeSniffer/blob/master/licence.txt BSD Licence
8+
*/
9+
10+
namespace PHP_CodeSniffer\Tests\Core\File;
11+
12+
use PHP_CodeSniffer\Tests\Core\AbstractMethodUnitTest;
13+
14+
/**
15+
* Tests for the \PHP_CodeSniffer\Files\File:getDeclarationName method.
16+
*
17+
* @covers \PHP_CodeSniffer\Files\File::getDeclarationName
18+
*/
19+
final class GetDeclarationNameParseError2Test extends AbstractMethodUnitTest
20+
{
21+
22+
23+
/**
24+
* Test receiving "null" in case of a parse error.
25+
*
26+
* @return void
27+
*/
28+
public function testGetDeclarationNameNull()
29+
{
30+
$target = $this->getTargetToken('/* testLiveCoding */', T_FUNCTION);
31+
$result = self::$phpcsFile->getDeclarationName($target);
32+
$this->assertNull($result);
33+
34+
}//end testGetDeclarationNameNull()
35+
36+
37+
}//end class

0 commit comments

Comments
 (0)