diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -91,6 +91,8 @@ 'ArcanistConsoleLintRendererTestCase' => 'lint/renderer/__tests__/ArcanistConsoleLintRendererTestCase.php', 'ArcanistConstructorParenthesesXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistConstructorParenthesesXHPASTLinterRule.php', 'ArcanistConstructorParenthesesXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistConstructorParenthesesXHPASTLinterRuleTestCase.php', + 'ArcanistContinueInsideSwitchXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistContinueInsideSwitchXHPASTLinterRule.php', + 'ArcanistContinueInsideSwitchXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistContinueInsideSwitchXHPASTLinterRuleTestCase.php', 'ArcanistControlStatementSpacingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistControlStatementSpacingXHPASTLinterRule.php', 'ArcanistControlStatementSpacingXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistControlStatementSpacingXHPASTLinterRuleTestCase.php', 'ArcanistCoverWorkflow' => 'workflow/ArcanistCoverWorkflow.php', @@ -511,6 +513,8 @@ 'ArcanistConsoleLintRendererTestCase' => 'PhutilTestCase', 'ArcanistConstructorParenthesesXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', 'ArcanistConstructorParenthesesXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase', + 'ArcanistContinueInsideSwitchXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', + 'ArcanistContinueInsideSwitchXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase', 'ArcanistControlStatementSpacingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', 'ArcanistControlStatementSpacingXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase', 'ArcanistCoverWorkflow' => 'ArcanistWorkflow', diff --git a/src/lint/linter/xhpast/rules/ArcanistContinueInsideSwitchXHPASTLinterRule.php b/src/lint/linter/xhpast/rules/ArcanistContinueInsideSwitchXHPASTLinterRule.php new file mode 100644 --- /dev/null +++ b/src/lint/linter/xhpast/rules/ArcanistContinueInsideSwitchXHPASTLinterRule.php @@ -0,0 +1,53 @@ +selectDescendantsOfType('n_CONTINUE'); + + $valid_containers = array( + 'n_WHILE' => true, + 'n_FOREACH' => true, + 'n_FOR' => true, + 'n_DO_WHILE' => true, + ); + + foreach ($continues as $continue) { + // If this is a "continue 2;" or similar, assume it's legitimate. + $label = $continue->getChildByIndex(0); + if ($label->getTypeName() !== 'n_EMPTY') { + continue; + } + + $node = $continue->getParentNode(); + while ($node) { + $node_type = $node->getTypeName(); + + // If we hit a valid loop which you can actually "continue;" inside, + // this is legitimate and we're done here. + if (isset($valid_containers[$node_type])) { + break; + } + + if ($node_type === 'n_SWITCH') { + $this->raiseLintAtNode( + $continue, + pht( + 'In a "switch" statement, "continue;" is equivalent to "break;" '. + 'but causes compile errors beginning with PHP 7.0.0.'), + 'break'); + } + + $node = $node->getParentNode(); + } + } + } + +} diff --git a/src/lint/linter/xhpast/rules/__tests__/ArcanistContinueInsideSwitchXHPASTLinterRuleTestCase.php b/src/lint/linter/xhpast/rules/__tests__/ArcanistContinueInsideSwitchXHPASTLinterRuleTestCase.php new file mode 100644 --- /dev/null +++ b/src/lint/linter/xhpast/rules/__tests__/ArcanistContinueInsideSwitchXHPASTLinterRuleTestCase.php @@ -0,0 +1,11 @@ +executeTestsInDirectory( + dirname(__FILE__).'/continue-inside-switch/'); + } + +} diff --git a/src/lint/linter/xhpast/rules/__tests__/continue-inside-switch/continue-inside-switch-1-valid.lint-test b/src/lint/linter/xhpast/rules/__tests__/continue-inside-switch/continue-inside-switch-1-valid.lint-test new file mode 100644 --- /dev/null +++ b/src/lint/linter/xhpast/rules/__tests__/continue-inside-switch/continue-inside-switch-1-valid.lint-test @@ -0,0 +1,21 @@ +