From 15aedfcf4d5c075701f0ec3adbc46ac28d1340db Mon Sep 17 00:00:00 2001 From: Greg Sherwood Date: Mon, 16 Dec 2019 12:57:47 +1100 Subject: [PATCH] Fixed bug #2688 : Case statements not tokenized correctly when switch is contained within ternary --- package.xml | 1 + .../SwitchDeclarationUnitTest.inc | 9 ++ .../SwitchDeclarationUnitTest.inc.fixed | 9 ++ .../SwitchDeclarationUnitTest.inc | 16 ++ .../SwitchDeclarationUnitTest.php | 139 ++++++++++++------ src/Tokenizers/PHP.php | 36 ++++- 6 files changed, 165 insertions(+), 45 deletions(-) diff --git a/package.xml b/package.xml index 8e6ec1d628..6df5902174 100644 --- a/package.xml +++ b/package.xml @@ -26,6 +26,7 @@ http://pear.php.net/dtd/package-2.0.xsd"> BSD 3-Clause License + - Fixed bug #2688 : Case statements not tokenized correctly when switch is contained within ternary - Fixed bug #2698 : PHPCS throws errors determining auto report width when shell_exec is disabled -- Thanks to Matthew Peveler for the patch - Fixed bug #2751 : Autoload relative paths first to avoid confusion with files from the global include path diff --git a/src/Standards/PSR2/Tests/ControlStructures/SwitchDeclarationUnitTest.inc b/src/Standards/PSR2/Tests/ControlStructures/SwitchDeclarationUnitTest.inc index 88a81869d5..af605cd71d 100644 --- a/src/Standards/PSR2/Tests/ControlStructures/SwitchDeclarationUnitTest.inc +++ b/src/Standards/PSR2/Tests/ControlStructures/SwitchDeclarationUnitTest.inc @@ -262,3 +262,12 @@ switch ($sContext) do_something(); } } + +$foo = $foo ? + function () { + switch ($a) { + case 'a': + break; + } + } : + null; diff --git a/src/Standards/PSR2/Tests/ControlStructures/SwitchDeclarationUnitTest.inc.fixed b/src/Standards/PSR2/Tests/ControlStructures/SwitchDeclarationUnitTest.inc.fixed index 748cee944f..d5671feea3 100644 --- a/src/Standards/PSR2/Tests/ControlStructures/SwitchDeclarationUnitTest.inc.fixed +++ b/src/Standards/PSR2/Tests/ControlStructures/SwitchDeclarationUnitTest.inc.fixed @@ -265,3 +265,12 @@ switch ($sContext) do_something(); } } + +$foo = $foo ? + function () { + switch ($a) { + case 'a': + break; + } + } : + null; diff --git a/src/Standards/Squiz/Tests/ControlStructures/SwitchDeclarationUnitTest.inc b/src/Standards/Squiz/Tests/ControlStructures/SwitchDeclarationUnitTest.inc index 5c71161425..cf397607bf 100644 --- a/src/Standards/Squiz/Tests/ControlStructures/SwitchDeclarationUnitTest.inc +++ b/src/Standards/Squiz/Tests/ControlStructures/SwitchDeclarationUnitTest.inc @@ -315,3 +315,19 @@ switch ($foo) { 'bar' ); } + +$foo = $foo ? + function () { + switch ($a) { + case 'a': + break; + + case (preg_match('/foo/i', $foo) ? $a : $b): + echo 'really?' + break; + + default: + break; + } + } : + null; diff --git a/src/Standards/Squiz/Tests/ControlStructures/SwitchDeclarationUnitTest.php b/src/Standards/Squiz/Tests/ControlStructures/SwitchDeclarationUnitTest.php index ab392c6181..c94c8333a5 100644 --- a/src/Standards/Squiz/Tests/ControlStructures/SwitchDeclarationUnitTest.php +++ b/src/Standards/Squiz/Tests/ControlStructures/SwitchDeclarationUnitTest.php @@ -27,48 +27,103 @@ class SwitchDeclarationUnitTest extends AbstractSniffUnitTest */ public function getErrorList($testFile='SwitchDeclarationUnitTest.inc') { - return [ - 27 => 1, - 29 => 1, - 34 => 1, - 36 => 1, - 44 => 1, - 48 => 1, - 52 => 1, - 54 => 1, - 55 => 1, - 56 => 1, - 58 => 1, - 59 => 1, - 61 => 1, - 62 => 1, - 79 => 1, - 85 => 2, - 88 => 2, - 89 => 2, - 92 => 1, - 95 => 3, - 99 => 1, - 116 => 1, - 122 => 1, - 127 => 2, - 134 => 2, - 135 => 1, - 138 => 1, - 143 => 1, - 144 => 1, - 147 => 1, - 165 => 1, - 172 => 1, - 176 => 2, - 180 => 1, - 192 => 2, - 196 => 1, - 223 => 1, - 266 => 1, - 282 => 1, - 284 => 2, - ]; + switch ($testFile) { + case 'SwitchDeclarationUnitTest.inc': + return [ + 27 => 1, + 29 => 1, + 34 => 1, + 36 => 1, + 44 => 1, + 48 => 1, + 52 => 1, + 54 => 1, + 55 => 1, + 56 => 1, + 58 => 1, + 59 => 1, + 61 => 1, + 62 => 1, + 79 => 1, + 85 => 2, + 88 => 2, + 89 => 2, + 92 => 1, + 95 => 3, + 99 => 1, + 116 => 1, + 122 => 1, + 127 => 2, + 134 => 2, + 135 => 1, + 138 => 1, + 143 => 1, + 144 => 1, + 147 => 1, + 165 => 1, + 172 => 1, + 176 => 2, + 180 => 1, + 192 => 2, + 196 => 1, + 223 => 1, + 266 => 1, + 282 => 1, + 284 => 2, + 322 => 1, + 323 => 1, + 327 => 1, + 329 => 1, + 330 => 1, + ]; + + case 'SwitchDeclarationUnitTest.js': + return [ + 27 => 1, + 29 => 1, + 34 => 1, + 36 => 1, + 44 => 1, + 48 => 1, + 52 => 1, + 54 => 1, + 55 => 1, + 56 => 1, + 58 => 1, + 59 => 1, + 61 => 1, + 62 => 1, + 79 => 1, + 85 => 2, + 88 => 2, + 89 => 2, + 92 => 1, + 95 => 3, + 99 => 1, + 116 => 1, + 122 => 1, + 127 => 2, + 134 => 2, + 135 => 1, + 138 => 1, + 143 => 1, + 144 => 1, + 147 => 1, + 165 => 1, + 172 => 1, + 176 => 2, + 180 => 1, + 192 => 2, + 196 => 1, + 223 => 1, + 266 => 1, + 282 => 1, + 284 => 2, + ]; + + default: + return []; + }//end switch }//end getErrorList() diff --git a/src/Tokenizers/PHP.php b/src/Tokenizers/PHP.php index 6eb750c3a0..6c32655712 100644 --- a/src/Tokenizers/PHP.php +++ b/src/Tokenizers/PHP.php @@ -1450,7 +1450,7 @@ function return types. We want to keep the parenthesis map clean, // inline IF statement. if (empty($insideInlineIf) === false && $newToken['code'] === T_COLON) { // Make sure this isn't the return type separator of a closure. - $isReturnType = false; + $isInlineIf = true; for ($i = ($stackPtr - 1); $i > 0; $i--) { if (is_array($tokens[$i]) === false || ($tokens[$i][0] !== T_DOC_COMMENT @@ -1487,14 +1487,44 @@ function return types. We want to keep the parenthesis map clean, } if ($tokens[$i][0] === T_FUNCTION || $tokens[$i][0] === T_FN || $tokens[$i][0] === T_USE) { - $isReturnType = true; + $isInlineIf = false; + if (PHP_CODESNIFFER_VERBOSITY > 1) { + echo "\t\t* token is function return type, not T_INLINE_ELSE".PHP_EOL; + } } }//end if - if ($isReturnType === false) { + // Check to see if this is a CASE or DEFAULT opener. + $inlineIfToken = $insideInlineIf[(count($insideInlineIf) - 1)]; + for ($i = $stackPtr; $i > $inlineIfToken; $i--) { + if (is_array($tokens[$i]) === true + && ($tokens[$i][0] === T_CASE + || $tokens[$i][0] === T_DEFAULT) + ) { + $isInlineIf = false; + if (PHP_CODESNIFFER_VERBOSITY > 1) { + echo "\t\t* token is T_CASE or T_DEFAULT opener, not T_INLINE_ELSE".PHP_EOL; + } + + break; + } + + if (is_array($tokens[$i]) === false + && ($tokens[$i] === ';' + || $tokens[$i] === '{') + ) { + break; + } + } + + if ($isInlineIf === true) { array_pop($insideInlineIf); $newToken['code'] = T_INLINE_ELSE; $newToken['type'] = 'T_INLINE_ELSE'; + + if (PHP_CODESNIFFER_VERBOSITY > 1) { + echo "\t\t* token changed from T_COLON to T_INLINE_ELSE".PHP_EOL; + } } }//end if