diff --git a/src/Files/File.php b/src/Files/File.php index f5224948ab..e63d09f57c 100644 --- a/src/Files/File.php +++ b/src/Files/File.php @@ -2558,6 +2558,7 @@ public function findStartOfStatement($start, $ignore=null) && $this->tokens[$i]['code'] !== T_CONTINUE && $this->tokens[$i]['code'] !== T_THROW && $this->tokens[$i]['code'] !== T_EXIT + && $this->tokens[$i]['code'] !== T_GOTO ) { // Found the end of the previous scope block. return $lastNotEmpty; diff --git a/src/Standards/PSR2/Sniffs/ControlStructures/SwitchDeclarationSniff.php b/src/Standards/PSR2/Sniffs/ControlStructures/SwitchDeclarationSniff.php index 0e58e4d188..c846e43e8a 100644 --- a/src/Standards/PSR2/Sniffs/ControlStructures/SwitchDeclarationSniff.php +++ b/src/Standards/PSR2/Sniffs/ControlStructures/SwitchDeclarationSniff.php @@ -380,6 +380,7 @@ private function findNestedTerminator($phpcsFile, $stackPtr, $end) T_CONTINUE => T_CONTINUE, T_THROW => T_THROW, T_EXIT => T_EXIT, + T_GOTO => T_GOTO, ]; $terminator = $phpcsFile->findStartOfStatement(($lastToken - 1)); diff --git a/src/Standards/PSR2/Tests/ControlStructures/SwitchDeclarationUnitTest.inc b/src/Standards/PSR2/Tests/ControlStructures/SwitchDeclarationUnitTest.inc index 2ca60a93e8..440cf8a38a 100644 --- a/src/Standards/PSR2/Tests/ControlStructures/SwitchDeclarationUnitTest.inc +++ b/src/Standards/PSR2/Tests/ControlStructures/SwitchDeclarationUnitTest.inc @@ -596,3 +596,16 @@ switch (rand()) { default: break; } + +// Fix: goto should be recognized as terminating statement. +switch ( $a ) { + case 1: + doSomething(); + goto jumpOut; + default: + $other = $code; + break; +} + +jumpOut: +doSomething(); diff --git a/src/Standards/PSR2/Tests/ControlStructures/SwitchDeclarationUnitTest.inc.fixed b/src/Standards/PSR2/Tests/ControlStructures/SwitchDeclarationUnitTest.inc.fixed index bbc8b7c48e..ca39c76d02 100644 --- a/src/Standards/PSR2/Tests/ControlStructures/SwitchDeclarationUnitTest.inc.fixed +++ b/src/Standards/PSR2/Tests/ControlStructures/SwitchDeclarationUnitTest.inc.fixed @@ -591,3 +591,16 @@ switch (rand()) { default: break; } + +// Fix: goto should be recognized as terminating statement. +switch ( $a ) { + case 1: + doSomething(); + goto jumpOut; + default: + $other = $code; + break; +} + +jumpOut: +doSomething(); diff --git a/src/Standards/Squiz/Sniffs/ControlStructures/SwitchDeclarationSniff.php b/src/Standards/Squiz/Sniffs/ControlStructures/SwitchDeclarationSniff.php index be46aed55c..2a9d2c7315 100644 --- a/src/Standards/Squiz/Sniffs/ControlStructures/SwitchDeclarationSniff.php +++ b/src/Standards/Squiz/Sniffs/ControlStructures/SwitchDeclarationSniff.php @@ -153,6 +153,7 @@ public function process(File $phpcsFile, $stackPtr) || $tokens[$nextBreak]['code'] === T_CONTINUE || $tokens[$nextBreak]['code'] === T_THROW || $tokens[$nextBreak]['code'] === T_EXIT + || $tokens[$nextBreak]['code'] === T_GOTO ) { if ($tokens[$nextBreak]['scope_condition'] === $nextCase) { // Only need to check a couple of things once, even if the diff --git a/src/Standards/Squiz/Tests/ControlStructures/SwitchDeclarationUnitTest.inc b/src/Standards/Squiz/Tests/ControlStructures/SwitchDeclarationUnitTest.inc index cf397607bf..8f48451f03 100644 --- a/src/Standards/Squiz/Tests/ControlStructures/SwitchDeclarationUnitTest.inc +++ b/src/Standards/Squiz/Tests/ControlStructures/SwitchDeclarationUnitTest.inc @@ -331,3 +331,16 @@ $foo = $foo ? } } : null; + +// Fix: goto should be recognized as terminating statement. +switch ( $a ) { + case 1: + doSomething(); + goto jumpOut; + default: + $other = $code; + goto jumpOut; +} + +jumpOut: +doSomething(); diff --git a/src/Standards/Squiz/Tests/ControlStructures/SwitchDeclarationUnitTest.inc.fixed b/src/Standards/Squiz/Tests/ControlStructures/SwitchDeclarationUnitTest.inc.fixed index fd8ad1e683..5e44a7a922 100644 --- a/src/Standards/Squiz/Tests/ControlStructures/SwitchDeclarationUnitTest.inc.fixed +++ b/src/Standards/Squiz/Tests/ControlStructures/SwitchDeclarationUnitTest.inc.fixed @@ -340,3 +340,17 @@ $foo = $foo ? } } : null; + +// Fix: goto should be recognized as terminating statement. +switch ( $a ) { + case 1: + doSomething(); + goto jumpOut; + + default: + $other = $code; + goto jumpOut; +} + +jumpOut: +doSomething(); diff --git a/src/Standards/Squiz/Tests/ControlStructures/SwitchDeclarationUnitTest.php b/src/Standards/Squiz/Tests/ControlStructures/SwitchDeclarationUnitTest.php index a7f0e3afad..310024dce7 100644 --- a/src/Standards/Squiz/Tests/ControlStructures/SwitchDeclarationUnitTest.php +++ b/src/Standards/Squiz/Tests/ControlStructures/SwitchDeclarationUnitTest.php @@ -80,6 +80,8 @@ public function getErrorList($testFile='') 327 => 1, 329 => 1, 330 => 1, + 339 => 2, + 342 => 1, ]; case 'SwitchDeclarationUnitTest.js': diff --git a/src/Tokenizers/PHP.php b/src/Tokenizers/PHP.php index c6b8e29065..386ec26fc7 100644 --- a/src/Tokenizers/PHP.php +++ b/src/Tokenizers/PHP.php @@ -231,6 +231,7 @@ class PHP extends Tokenizer T_CONTINUE => T_CONTINUE, T_THROW => T_THROW, T_EXIT => T_EXIT, + T_GOTO => T_GOTO, ], 'strict' => true, 'shared' => true, @@ -251,6 +252,7 @@ class PHP extends Tokenizer T_CONTINUE => T_CONTINUE, T_THROW => T_THROW, T_EXIT => T_EXIT, + T_GOTO => T_GOTO, ], 'strict' => true, 'shared' => true, diff --git a/tests/Core/File/FindStartOfStatementTest.inc b/tests/Core/File/FindStartOfStatementTest.inc index 574b986140..5b601075a7 100644 --- a/tests/Core/File/FindStartOfStatementTest.inc +++ b/tests/Core/File/FindStartOfStatementTest.inc @@ -157,12 +157,20 @@ switch ($foo) { /* testInsideCaseThrowStatement */ throw new Exception(); + case 6: + $var = doSomething(); + /* testInsideCaseGotoStatement */ + goto myLabel; + /* testDefaultStatement */ default: /* testInsideDefaultContinueStatement */ continue $var; } +myLabel: +do_something(); + match ($var) { true => /* test437ClosureDeclaration */ diff --git a/tests/Core/File/FindStartOfStatementTest.php b/tests/Core/File/FindStartOfStatementTest.php index bfcbfaf950..aa0646c6d4 100644 --- a/tests/Core/File/FindStartOfStatementTest.php +++ b/tests/Core/File/FindStartOfStatementTest.php @@ -659,6 +659,16 @@ public static function dataFindStartInsideSwitchCaseDefaultStatements() 'targets' => T_CLOSE_PARENTHESIS, 'expectedTarget' => T_THROW, ], + 'Goto should be start for contents of the goto statement - goto label' => [ + 'testMarker' => '/* testInsideCaseGotoStatement */', + 'targets' => T_STRING, + 'expectedTarget' => T_GOTO, + ], + 'Goto should be start for contents of the goto statement - semicolon' => [ + 'testMarker' => '/* testInsideCaseGotoStatement */', + 'targets' => T_SEMICOLON, + 'expectedTarget' => T_GOTO, + ], 'Default keyword should be start of default statement - default itself' => [ 'testMarker' => '/* testDefaultStatement */', 'targets' => T_DEFAULT,