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 @@ -402,6 +402,8 @@ 'ArcanistParenthesesSpacingXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistParenthesesSpacingXHPASTLinterRuleTestCase.php', 'ArcanistParseStrUseXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistParseStrUseXHPASTLinterRule.php', 'ArcanistParseStrUseXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistParseStrUseXHPASTLinterRuleTestCase.php', + 'ArcanistPartialCatchXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistPartialCatchXHPASTLinterRule.php', + 'ArcanistPartialCatchXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistPartialCatchXHPASTLinterRuleTestCase.php', 'ArcanistPasteRef' => 'ref/paste/ArcanistPasteRef.php', 'ArcanistPasteSymbolRef' => 'ref/paste/ArcanistPasteSymbolRef.php', 'ArcanistPasteWorkflow' => 'workflow/ArcanistPasteWorkflow.php', @@ -1451,6 +1453,8 @@ 'ArcanistParenthesesSpacingXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase', 'ArcanistParseStrUseXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', 'ArcanistParseStrUseXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase', + 'ArcanistPartialCatchXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', + 'ArcanistPartialCatchXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase', 'ArcanistPasteRef' => 'ArcanistRef', 'ArcanistPasteSymbolRef' => 'ArcanistSimpleSymbolRef', 'ArcanistPasteWorkflow' => 'ArcanistArcWorkflow', diff --git a/src/lint/linter/xhpast/rules/ArcanistPartialCatchXHPASTLinterRule.php b/src/lint/linter/xhpast/rules/ArcanistPartialCatchXHPASTLinterRule.php new file mode 100644 --- /dev/null +++ b/src/lint/linter/xhpast/rules/ArcanistPartialCatchXHPASTLinterRule.php @@ -0,0 +1,45 @@ +selectDescendantsOfType('n_CATCH_LIST'); + + foreach ($catch_lists as $catch_list) { + $catches = $catch_list->getChildrenOfType('n_CATCH'); + + $classes = array(); + foreach ($catches as $catch) { + $class_node = $catch->getChildOfType(0, 'n_CLASS_NAME'); + $class_name = $class_node->getConcreteString(); + $class_name = phutil_utf8_strtolower($class_name); + + $classes[$class_name] = $class_node; + } + + $catches_exception = idx($classes, 'exception'); + $catches_throwable = idx($classes, 'throwable'); + + if ($catches_exception && !$catches_throwable) { + $this->raiseLintAtNode( + $catches_exception, + pht( + 'Try/catch block catches "Exception", but does not catch '. + '"Throwable". In PHP7 and newer, some runtime exceptions '. + 'will escape this block.')); + } + } + } + +} diff --git a/src/lint/linter/xhpast/rules/__tests__/ArcanistPartialCatchXHPASTLinterRuleTestCase.php b/src/lint/linter/xhpast/rules/__tests__/ArcanistPartialCatchXHPASTLinterRuleTestCase.php new file mode 100644 --- /dev/null +++ b/src/lint/linter/xhpast/rules/__tests__/ArcanistPartialCatchXHPASTLinterRuleTestCase.php @@ -0,0 +1,10 @@ +executeTestsInDirectory(dirname(__FILE__).'/partial-catch/'); + } + +} diff --git a/src/lint/linter/xhpast/rules/__tests__/partial-catch/catch-without-throwable.lint-test b/src/lint/linter/xhpast/rules/__tests__/partial-catch/catch-without-throwable.lint-test new file mode 100644 --- /dev/null +++ b/src/lint/linter/xhpast/rules/__tests__/partial-catch/catch-without-throwable.lint-test @@ -0,0 +1,17 @@ +