Skip to content

Commit 5fc33cc

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 57ded36 commit 5fc33cc

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
@@ -1249,8 +1249,17 @@ public function getDeclarationName($stackPtr)
12491249
return $this->tokens[$stackPtr]['content'];
12501250
}
12511251

1252+
$stopPoint = $this->numTokens;
1253+
if (isset($this->tokens[$stackPtr]['parenthesis_opener']) === true) {
1254+
// For functions, stop searching at the parenthesis opener.
1255+
$stopPoint = $this->tokens[$stackPtr]['parenthesis_opener'];
1256+
} else if (isset($this->tokens[$stackPtr]['scope_opener']) === true) {
1257+
// For OO tokens, stop searching at the open curly.
1258+
$stopPoint = $this->tokens[$stackPtr]['scope_opener'];
1259+
}
1260+
12521261
$content = null;
1253-
for ($i = $stackPtr; $i < $this->numTokens; $i++) {
1262+
for ($i = $stackPtr; $i < $stopPoint; $i++) {
12541263
if ($this->tokens[$i]['code'] === T_STRING) {
12551264
$content = $this->tokens[$i]['content'];
12561265
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)