mirror of
https://we.phorge.it/source/arcanist.git
synced 2024-11-26 08:42:40 +01:00
Add a linter rule for abstract
methods containing a body
Summary: `abstract` methods cannot contain a body. ``` PHP Fatal error: Abstract function X::Y() cannot contain body in /home/josh/workspace/github.com/phacility/arcanist/test.php on line 4 ``` Test Plan: Added unit tests. Reviewers: #blessed_reviewers, epriestley Reviewed By: #blessed_reviewers, epriestley Subscribers: Korvin Differential Revision: https://secure.phabricator.com/D14560
This commit is contained in:
parent
37571d8839
commit
72d9013d29
8 changed files with 88 additions and 28 deletions
|
@ -9,6 +9,8 @@
|
||||||
phutil_register_library_map(array(
|
phutil_register_library_map(array(
|
||||||
'__library_version__' => 2,
|
'__library_version__' => 2,
|
||||||
'class' => array(
|
'class' => array(
|
||||||
|
'ArcanistAbstractMethodBodyXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistAbstractMethodBodyXHPASTLinterRule.php',
|
||||||
|
'ArcanistAbstractMethodBodyXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistAbstractMethodBodyXHPASTLinterRuleTestCase.php',
|
||||||
'ArcanistAbstractPrivateMethodXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistAbstractPrivateMethodXHPASTLinterRule.php',
|
'ArcanistAbstractPrivateMethodXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistAbstractPrivateMethodXHPASTLinterRule.php',
|
||||||
'ArcanistAbstractPrivateMethodXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistAbstractPrivateMethodXHPASTLinterRuleTestCase.php',
|
'ArcanistAbstractPrivateMethodXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistAbstractPrivateMethodXHPASTLinterRuleTestCase.php',
|
||||||
'ArcanistAliasFunctionXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistAliasFunctionXHPASTLinterRule.php',
|
'ArcanistAliasFunctionXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistAliasFunctionXHPASTLinterRule.php',
|
||||||
|
@ -395,6 +397,8 @@ phutil_register_library_map(array(
|
||||||
),
|
),
|
||||||
'function' => array(),
|
'function' => array(),
|
||||||
'xmap' => array(
|
'xmap' => array(
|
||||||
|
'ArcanistAbstractMethodBodyXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||||
|
'ArcanistAbstractMethodBodyXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
||||||
'ArcanistAbstractPrivateMethodXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
'ArcanistAbstractPrivateMethodXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||||
'ArcanistAbstractPrivateMethodXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
'ArcanistAbstractPrivateMethodXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
|
||||||
'ArcanistAliasFunctionXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
'ArcanistAliasFunctionXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||||
|
|
|
@ -225,6 +225,34 @@ abstract class ArcanistXHPASTLinterRule extends Phobject {
|
||||||
return AASTNodeList::newFromTreeAndNodes($root->getTree(), $nodes);
|
return AASTNodeList::newFromTreeAndNodes($root->getTree(), $nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get class/method modifiers.
|
||||||
|
*
|
||||||
|
* @param XHPASTNode A node of type `n_CLASS_DECLARATION` or
|
||||||
|
* `n_METHOD_DECLARATION`.
|
||||||
|
* @return map<string, bool> Class/method modifiers.
|
||||||
|
*/
|
||||||
|
final protected function getModifiers(XHPASTNode $node) {
|
||||||
|
$modifier_list = $node->getChildByIndex(0);
|
||||||
|
|
||||||
|
switch ($modifier_list->getTypeName()) {
|
||||||
|
case 'n_CLASS_ATTRIBUTES':
|
||||||
|
case 'n_METHOD_MODIFIER_LIST':
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$modifiers = array();
|
||||||
|
|
||||||
|
foreach ($modifier_list->selectDescendantsOfType('n_STRING') as $modifier) {
|
||||||
|
$modifiers[strtolower($modifier->getConcreteString())] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $modifiers;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get PHP superglobals.
|
* Get PHP superglobals.
|
||||||
*
|
*
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class ArcanistAbstractMethodBodyXHPASTLinterRule
|
||||||
|
extends ArcanistXHPASTLinterRule {
|
||||||
|
|
||||||
|
const ID = 108;
|
||||||
|
|
||||||
|
public function getLintName() {
|
||||||
|
return pht('`%s` Method Cannot Contain Body', 'abstract');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function process(XHPASTNode $root) {
|
||||||
|
$methods = $root->selectDescendantsOfType('n_METHOD_DECLARATION');
|
||||||
|
|
||||||
|
foreach ($methods as $method) {
|
||||||
|
$modifiers = $this->getModifiers($method);
|
||||||
|
$body = $method->getChildByIndex(5);
|
||||||
|
|
||||||
|
if (idx($modifiers, 'abstract') && $body->getTypeName() != 'n_EMPTY') {
|
||||||
|
$this->raiseLintAtNode(
|
||||||
|
$body,
|
||||||
|
pht(
|
||||||
|
'`%s` methods cannot contain a body. This construct will '.
|
||||||
|
'cause a fatal error.',
|
||||||
|
'abstract'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -43,32 +43,4 @@ final class ArcanistClassMustBeDeclaredAbstractXHPASTLinterRule
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get class/method modifiers.
|
|
||||||
*
|
|
||||||
* @param XHPASTNode A node of type `n_CLASS_DECLARATION` or
|
|
||||||
* `n_METHOD_DECLARATION`.
|
|
||||||
* @return map<string, bool> Class/method modifiers.
|
|
||||||
*/
|
|
||||||
private function getModifiers(XHPASTNode $node) {
|
|
||||||
$modifier_list = $node->getChildByIndex(0);
|
|
||||||
|
|
||||||
switch ($modifier_list->getTypeName()) {
|
|
||||||
case 'n_CLASS_ATTRIBUTES':
|
|
||||||
case 'n_METHOD_MODIFIER_LIST':
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
|
|
||||||
$modifiers = array();
|
|
||||||
|
|
||||||
foreach ($modifier_list->selectDescendantsOfType('n_STRING') as $modifier) {
|
|
||||||
$modifiers[strtolower($modifier->getConcreteString())] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $modifiers;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class ArcanistAbstractMethodBodyXHPASTLinterRuleTestCase
|
||||||
|
extends ArcanistXHPASTLinterRuleTestCase {
|
||||||
|
|
||||||
|
public function testLinter() {
|
||||||
|
$this->executeTestsInDirectory(dirname(__FILE__).'/abstract-method-body/');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
<?php
|
||||||
|
abstract class SomeClass {
|
||||||
|
abstract public function someMethod() {}
|
||||||
|
}
|
||||||
|
~~~~~~~~~~
|
||||||
|
error:3:41
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?php
|
||||||
|
abstract class SomeClass {
|
||||||
|
abstract public function someMethod();
|
||||||
|
}
|
||||||
|
~~~~~~~~~~
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?php
|
||||||
|
abstract class SomeClass {
|
||||||
|
public function someMethod() {}
|
||||||
|
}
|
||||||
|
~~~~~~~~~~
|
Loading…
Reference in a new issue