1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-11-28 09:42:40 +01:00

Add a lint check for catching "Exception" without catching "Throwable"

Summary:
Ref T13588. For consistency of behavior between versions on either side of PHP7, any "catch (Exception)" block should generally have a "catch (Throwable)" block in the same catch list.

Raise a lint warning when "Exception" is caught without also catching "Throwable", since this is almost certainly not desired.

Test Plan: Added tests.

Maniphest Tasks: T13588

Differential Revision: https://secure.phabricator.com/D21542
This commit is contained in:
epriestley 2021-02-03 14:25:59 -08:00
parent c1afa91f9f
commit 32fe933f3a
4 changed files with 76 additions and 0 deletions

View file

@ -402,6 +402,8 @@ phutil_register_library_map(array(
'ArcanistParenthesesSpacingXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistParenthesesSpacingXHPASTLinterRuleTestCase.php', 'ArcanistParenthesesSpacingXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistParenthesesSpacingXHPASTLinterRuleTestCase.php',
'ArcanistParseStrUseXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistParseStrUseXHPASTLinterRule.php', 'ArcanistParseStrUseXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistParseStrUseXHPASTLinterRule.php',
'ArcanistParseStrUseXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistParseStrUseXHPASTLinterRuleTestCase.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', 'ArcanistPasteRef' => 'ref/paste/ArcanistPasteRef.php',
'ArcanistPasteSymbolRef' => 'ref/paste/ArcanistPasteSymbolRef.php', 'ArcanistPasteSymbolRef' => 'ref/paste/ArcanistPasteSymbolRef.php',
'ArcanistPasteWorkflow' => 'workflow/ArcanistPasteWorkflow.php', 'ArcanistPasteWorkflow' => 'workflow/ArcanistPasteWorkflow.php',
@ -1451,6 +1453,8 @@ phutil_register_library_map(array(
'ArcanistParenthesesSpacingXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase', 'ArcanistParenthesesSpacingXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
'ArcanistParseStrUseXHPASTLinterRule' => 'ArcanistXHPASTLinterRule', 'ArcanistParseStrUseXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistParseStrUseXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase', 'ArcanistParseStrUseXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
'ArcanistPartialCatchXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistPartialCatchXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
'ArcanistPasteRef' => 'ArcanistRef', 'ArcanistPasteRef' => 'ArcanistRef',
'ArcanistPasteSymbolRef' => 'ArcanistSimpleSymbolRef', 'ArcanistPasteSymbolRef' => 'ArcanistSimpleSymbolRef',
'ArcanistPasteWorkflow' => 'ArcanistArcWorkflow', 'ArcanistPasteWorkflow' => 'ArcanistArcWorkflow',

View file

@ -0,0 +1,45 @@
<?php
final class ArcanistPartialCatchXHPASTLinterRule
extends ArcanistXHPASTLinterRule {
const ID = 132;
public function getLintName() {
return pht('Partial Catch');
}
public function getLintSeverity() {
return ArcanistLintSeverity::SEVERITY_WARNING;
}
public function process(XHPASTNode $root) {
$catch_lists = $root->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.'));
}
}
}
}

View file

@ -0,0 +1,10 @@
<?php
final class ArcanistPartialCatchXHPASTLinterRuleTestCase
extends ArcanistXHPASTLinterRuleTestCase {
public function testLinter() {
$this->executeTestsInDirectory(dirname(__FILE__).'/partial-catch/');
}
}

View file

@ -0,0 +1,17 @@
<?php
try {
f();
} catch (Exception $ex) {
g();
}
try {
f();
} catch (Exception $ex) {
g();
} catch (Throwable $ex) {
h();
}
~~~~~~~~~~
warning:4:10:XHP132