diff --git a/PHPCSUtils/BackCompat/BCFile.php b/PHPCSUtils/BackCompat/BCFile.php index b5280633..fdbc2eb3 100644 --- a/PHPCSUtils/BackCompat/BCFile.php +++ b/PHPCSUtils/BackCompat/BCFile.php @@ -76,7 +76,7 @@ final class BCFile * * Changelog for the PHPCS native function: * - Introduced in PHPCS 0.0.5. - * - The upstream method has received no significant updates since PHPCS 3.10.1. + * - PHPCS 3.12.0: hardening to handle unfinished closure better. * * @see \PHP_CodeSniffer\Files\File::getDeclarationName() Original source. * @see \PHPCSUtils\Utils\ObjectDeclarations::getName() PHPCSUtils native improved version. @@ -98,7 +98,47 @@ final class BCFile */ public static function getDeclarationName(File $phpcsFile, $stackPtr) { - return $phpcsFile->getDeclarationName($stackPtr); + $tokens = $phpcsFile->getTokens(); + + $tokenCode = $tokens[$stackPtr]['code']; + + if ($tokenCode === T_ANON_CLASS || $tokenCode === T_CLOSURE) { + return null; + } + + if ($tokenCode !== T_FUNCTION + && $tokenCode !== T_CLASS + && $tokenCode !== T_INTERFACE + && $tokenCode !== T_TRAIT + && $tokenCode !== T_ENUM + ) { + throw new RuntimeException('Token type "' . $tokens[$stackPtr]['type'] . '" is not T_FUNCTION, T_CLASS, T_INTERFACE, T_TRAIT or T_ENUM'); + } + + if ($tokenCode === T_FUNCTION + && strtolower($tokens[$stackPtr]['content']) !== 'function' + ) { + // This is a function declared without the "function" keyword. + // So this token is the function name. + return $tokens[$stackPtr]['content']; + } + + $stopPoint = $phpcsFile->numTokens; + if ($tokenCode === T_FUNCTION && isset($tokens[$stackPtr]['parenthesis_opener']) === true) { + $stopPoint = $tokens[$stackPtr]['parenthesis_opener']; + } elseif (isset($tokens[$stackPtr]['scope_opener']) === true) { + $stopPoint = $tokens[$stackPtr]['scope_opener']; + } + + $content = null; + for ($i = $stackPtr; $i < $stopPoint; $i++) { + if ($tokens[$i]['code'] === T_STRING) { + $content = $tokens[$i]['content']; + break; + } + } + + return $content; } /** diff --git a/Tests/BackCompat/BCFile/GetDeclarationNameParseError1Test.inc b/Tests/BackCompat/BCFile/GetDeclarationNameParseError1Test.inc new file mode 100644 index 00000000..451d4dfb --- /dev/null +++ b/Tests/BackCompat/BCFile/GetDeclarationNameParseError1Test.inc @@ -0,0 +1,5 @@ +getTargetToken($testMarker, $targetType); + $result = BCFile::getDeclarationName(self::$phpcsFile, $target); + $this->assertNull($result); + } + + /** + * Data provider. + * + * @see testGetDeclarationNameNull() For the array format. + * + * @return array> + */ + public static function dataGetDeclarationNameNull() + { + return [ + 'unfinished function/live coding' => [ + 'testMarker' => '/* testLiveCoding */', + 'targetType' => \T_FUNCTION, + ], + ]; + } +} diff --git a/Tests/BackCompat/BCFile/GetDeclarationNameParseError2Test.inc b/Tests/BackCompat/BCFile/GetDeclarationNameParseError2Test.inc new file mode 100644 index 00000000..e9a61cf4 --- /dev/null +++ b/Tests/BackCompat/BCFile/GetDeclarationNameParseError2Test.inc @@ -0,0 +1,6 @@ +getTargetToken($testMarker, $targetType); + $result = BCFile::getDeclarationName(self::$phpcsFile, $target); + $this->assertNull($result); + } + + /** + * Data provider. + * + * @see testGetDeclarationNameNull() For the array format. + * + * @return array> + */ + public static function dataGetDeclarationNameNull() + { + return [ + 'unfinished closure/live coding' => [ + 'testMarker' => '/* testLiveCoding */', + 'targetType' => \T_FUNCTION, + ], + ]; + } +} diff --git a/Tests/BackCompat/BCFile/GetDeclarationNameTest.inc b/Tests/BackCompat/BCFile/GetDeclarationNameTest.inc index 14902245..a7a53c9a 100644 --- a/Tests/BackCompat/BCFile/GetDeclarationNameTest.inc +++ b/Tests/BackCompat/BCFile/GetDeclarationNameTest.inc @@ -96,7 +96,3 @@ function &self() {} /* testFunctionReturnByRefWithReservedKeywordStatic */ function &static() {} - -/* testLiveCoding */ -// Intentional parse error. This has to be the last test in the file. -function // Comment. diff --git a/Tests/BackCompat/BCFile/GetDeclarationNameTest.php b/Tests/BackCompat/BCFile/GetDeclarationNameTest.php index a94bcbec..3dcc0140 100644 --- a/Tests/BackCompat/BCFile/GetDeclarationNameTest.php +++ b/Tests/BackCompat/BCFile/GetDeclarationNameTest.php @@ -85,10 +85,6 @@ public static function dataGetDeclarationNameNull() 'testMarker' => '/* testAnonClassExtendsWithoutParens */', 'targetType' => \T_ANON_CLASS, ], - 'live-coding' => [ - 'testMarker' => '/* testLiveCoding */', - 'targetType' => \T_FUNCTION, - ], ]; } diff --git a/Tests/Utils/ObjectDeclarations/GetNameParseError1Test.php b/Tests/Utils/ObjectDeclarations/GetNameParseError1Test.php new file mode 100644 index 00000000..c7e7a69a --- /dev/null +++ b/Tests/Utils/ObjectDeclarations/GetNameParseError1Test.php @@ -0,0 +1,66 @@ +getTargetToken($testMarker, $targetType); + $result = ObjectDeclarations::getName(self::$phpcsFile, $target); + $this->assertNull($result); + } +} diff --git a/Tests/Utils/ObjectDeclarations/GetNameParseError2Test.php b/Tests/Utils/ObjectDeclarations/GetNameParseError2Test.php new file mode 100644 index 00000000..b3f53606 --- /dev/null +++ b/Tests/Utils/ObjectDeclarations/GetNameParseError2Test.php @@ -0,0 +1,66 @@ +getTargetToken($testMarker, $targetType); + $result = ObjectDeclarations::getName(self::$phpcsFile, $target); + $this->assertNull($result); + } +}