1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-11-25 16:22:42 +01:00

Add a linter rule to prevent exceptions from being thrown in a toString method

Summary: Fixes T8207. It is not possible to throw an exception from within the `__toString()` method. Add a linter rule to prevent this from happening.

Test Plan: Added test cases.

Reviewers: epriestley, #blessed_reviewers

Reviewed By: epriestley, #blessed_reviewers

Subscribers: Korvin, epriestley

Maniphest Tasks: T8207

Differential Revision: https://secure.phabricator.com/D12854
This commit is contained in:
Joshua Spence 2015-05-18 08:17:32 +10:00
parent 2e66d03c68
commit 1e3073a4f8
2 changed files with 51 additions and 1 deletions

View file

@ -66,6 +66,7 @@ final class ArcanistXHPASTLinter extends ArcanistBaseXHPASTLinter {
const LINT_NO_PARENT_SCOPE = 64;
const LINT_ALIAS_FUNCTION = 65;
const LINT_CAST_SPACING = 66;
const LINT_TOSTRING_EXCEPTION = 67;
private $blacklistedFunctions = array();
private $naminghook;
@ -206,6 +207,8 @@ final class ArcanistXHPASTLinter extends ArcanistBaseXHPASTLinter {
=> pht('Alias Functions'),
self::LINT_CAST_SPACING
=> pht('Cast Spacing'),
self::LINT_TOSTRING_EXCEPTION
=> pht('Throwing Exception in %s Method', '__toString'),
);
}
@ -326,7 +329,7 @@ final class ArcanistXHPASTLinter extends ArcanistBaseXHPASTLinter {
public function getVersion() {
// The version number should be incremented whenever a new rule is added.
return '28';
return '29';
}
protected function resolveFuture($path, Future $future) {
@ -419,6 +422,7 @@ final class ArcanistXHPASTLinter extends ArcanistBaseXHPASTLinter {
'lintNoParentScope' => self::LINT_NO_PARENT_SCOPE,
'lintAliasFunctions' => self::LINT_ALIAS_FUNCTION,
'lintCastSpacing' => self::LINT_CAST_SPACING,
'lintThrowExceptionInToStringMethod' => self::LINT_TOSTRING_EXCEPTION,
);
foreach ($method_codes as $method => $codes) {
@ -4124,6 +4128,34 @@ final class ArcanistXHPASTLinter extends ArcanistBaseXHPASTLinter {
}
}
private function lintThrowExceptionInToStringMethod(XHPASTNode $root) {
$methods = $root->selectDescendantsOfType('n_METHOD_DECLARATION');
foreach ($methods as $method) {
$name = $method
->getChildOfType(2, 'n_STRING')
->getConcreteString();
if ($name != '__toString') {
continue;
}
$throws = $method
->getChildOfType(5, 'n_STATEMENT_LIST')
->selectDescendantsOfType('n_THROW');
foreach ($throws as $throw) {
$this->raiseLintAtNode(
$throw,
self::LINT_TOSTRING_EXCEPTION,
pht(
'It is not possible to throw an %s from within the %s method.',
'Exception',
'__toString'));
}
}
}
/**
* Retrieve all calls to some specified function(s).

View file

@ -0,0 +1,18 @@
<?php
class MyClass {
public function __toString() {
if (some_function()) {
throw new Exception('Oops');
}
return __CLASS__;
}
}
class MyOtherClass {
public function __toString() {
return 'Success';
}
}
~~~~~~~~~~
error:5:7