From e8b37eb8bbffe32c229872c1ab92921505750dc7 Mon Sep 17 00:00:00 2001 From: jrfnl Date: Sun, 21 May 2023 13:46:39 +0200 Subject: [PATCH] ControlStructures: more defensive coding against parse errors Only add an exception to the array if one was found. Includes unit tests. --- PHPCSUtils/Utils/ControlStructures.php | 15 +++++++++------ .../ControlStructures/GetCaughtExceptionsTest.inc | 6 ++++++ .../ControlStructures/GetCaughtExceptionsTest.php | 8 ++++++++ 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/PHPCSUtils/Utils/ControlStructures.php b/PHPCSUtils/Utils/ControlStructures.php index 0e8180ad..44bc053d 100644 --- a/PHPCSUtils/Utils/ControlStructures.php +++ b/PHPCSUtils/Utils/ControlStructures.php @@ -209,6 +209,7 @@ public static function isElseIf(File $phpcsFile, $stackPtr) * 'type_end_token' => integer, // The stack pointer to the end of the type declaration. * ) * ``` + * In case of an invalid catch structure, the array may be empty. * * @throws \PHP_CodeSniffer\Exceptions\RuntimeException If the specified `$stackPtr` is not of * type `T_CATCH` or doesn't exist. @@ -243,12 +244,14 @@ public static function getCaughtExceptions(File $phpcsFile, $stackPtr) } if (isset(Collections::namespacedNameTokens()[$tokens[$i]['code']]) === false) { - // Add the current exception to the result array. - $exceptions[] = [ - 'type' => $foundName, - 'type_token' => $firstToken, - 'type_end_token' => $lastToken, - ]; + // Add the current exception to the result array if one was found. + if ($foundName !== '') { + $exceptions[] = [ + 'type' => $foundName, + 'type_token' => $firstToken, + 'type_end_token' => $lastToken, + ]; + } if ($tokens[$i]['code'] === \T_BITWISE_OR) { // Multi-catch. Reset and continue. diff --git a/Tests/Utils/ControlStructures/GetCaughtExceptionsTest.inc b/Tests/Utils/ControlStructures/GetCaughtExceptionsTest.inc index aee219ad..9072c1cb 100644 --- a/Tests/Utils/ControlStructures/GetCaughtExceptionsTest.inc +++ b/Tests/Utils/ControlStructures/GetCaughtExceptionsTest.inc @@ -30,6 +30,12 @@ try { /* testPHP8NonCapturingCatch */ } catch (RuntimeException | AnotherException) { +/* testMissingExceptionName */ +} catch ($e) { + +/* testMultiMissingExceptionNames */ +} catch ( | $e) { + /* testLiveCoding */ // Intentional parse error. } catch ( diff --git a/Tests/Utils/ControlStructures/GetCaughtExceptionsTest.php b/Tests/Utils/ControlStructures/GetCaughtExceptionsTest.php index 0b0b3702..32d4dcba 100644 --- a/Tests/Utils/ControlStructures/GetCaughtExceptionsTest.php +++ b/Tests/Utils/ControlStructures/GetCaughtExceptionsTest.php @@ -214,6 +214,14 @@ public function dataGetCaughtExceptions() ], ], ], + 'catch-without-named-exception' => [ + 'testMarker' => '/* testMissingExceptionName */', + 'expected' => [], + ], + 'multi-catch-without-named-exceptions' => [ + 'testMarker' => '/* testMultiMissingExceptionNames */', + 'expected' => [], + ], ]; } }