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 for parent references

Summary:
Add a linter rule to detect static method calls which //should// reference the `parent` class instead of a hardcoded class reference. For example, consider the following:

```lang=php, counterexample
class SomeClass extends AnotherClass {
  public function someMethod() {
    AnotherClass::someOtherMethod();
  }
}
```

This should instead be written as:

```lang=php
class SomeClass extends AnotherClass {
  public function someMethod() {
    parent::someOtherMethod();
  }
}
```

Test Plan: Added unit tests.

Reviewers: epriestley, #blessed_reviewers

Reviewed By: epriestley, #blessed_reviewers

Subscribers: Korvin, epriestley

Differential Revision: https://secure.phabricator.com/D14443
This commit is contained in:
Joshua Spence 2015-11-19 08:58:03 +11:00
parent 4d512c51d4
commit 7386afc953
5 changed files with 108 additions and 0 deletions

View file

@ -248,6 +248,8 @@ phutil_register_library_map(array(
'ArcanistPHPOpenTagXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistPHPOpenTagXHPASTLinterRuleTestCase.php',
'ArcanistPHPShortTagXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistPHPShortTagXHPASTLinterRule.php',
'ArcanistPHPShortTagXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistPHPShortTagXHPASTLinterRuleTestCase.php',
'ArcanistParentMemberReferenceXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistParentMemberReferenceXHPASTLinterRule.php',
'ArcanistParentMemberReferenceXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistParentMemberReferenceXHPASTLinterRuleTestCase.php',
'ArcanistParenthesesSpacingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistParenthesesSpacingXHPASTLinterRule.php',
'ArcanistParenthesesSpacingXHPASTLinterRuleTestCase' => 'lint/linter/xhpast/rules/__tests__/ArcanistParenthesesSpacingXHPASTLinterRuleTestCase.php',
'ArcanistParseStrUseXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistParseStrUseXHPASTLinterRule.php',
@ -618,6 +620,8 @@ phutil_register_library_map(array(
'ArcanistPHPOpenTagXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
'ArcanistPHPShortTagXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistPHPShortTagXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
'ArcanistParentMemberReferenceXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistParentMemberReferenceXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
'ArcanistParenthesesSpacingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistParenthesesSpacingXHPASTLinterRuleTestCase' => 'ArcanistXHPASTLinterRuleTestCase',
'ArcanistParseStrUseXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',

View file

@ -0,0 +1,71 @@
<?php
final class ArcanistParentMemberReferenceXHPASTLinterRule
extends ArcanistXHPASTLinterRule {
const ID = 83;
public function getLintName() {
return pht('Parent Member Reference');
}
public function getLintSeverity() {
return ArcanistLintSeverity::SEVERITY_WARNING;
}
public function process(XHPASTNode $root) {
$class_declarations = $root->selectDescendantsOfType('n_CLASS_DECLARATION');
foreach ($class_declarations as $class_declaration) {
$extends_list = $class_declaration
->getChildByIndex(2);
$parent_class = null;
if ($extends_list->getTypeName() == 'n_EXTENDS_LIST') {
$parent_class = $extends_list
->getChildOfType(0, 'n_CLASS_NAME')
->getConcreteString();
}
if (!$parent_class) {
continue;
}
$class_static_accesses = $class_declaration
->selectDescendantsOfType('n_CLASS_STATIC_ACCESS');
$closures = $this->getAnonymousClosures($class_declaration);
foreach ($class_static_accesses as $class_static_access) {
$double_colons = $class_static_access
->selectTokensOfType('T_PAAMAYIM_NEKUDOTAYIM');
$class_ref = $class_static_access->getChildByIndex(0);
if ($class_ref->getTypeName() != 'n_CLASS_NAME') {
continue;
}
$class_ref_name = $class_ref->getConcreteString();
if (strtolower($parent_class) == strtolower($class_ref_name)) {
$in_closure = false;
foreach ($closures as $closure) {
if ($class_ref->isDescendantOf($closure)) {
$in_closure = true;
break;
}
}
if (version_compare($this->version, '5.4.0', '>=') || !$in_closure) {
$this->raiseLintAtNode(
$class_ref,
pht(
'Use `%s` to call parent method.',
'parent::'),
'parent');
}
}
}
}
}
}

View file

@ -0,0 +1,11 @@
<?php
final class ArcanistParentMemberReferenceXHPASTLinterRuleTestCase
extends ArcanistXHPASTLinterRuleTestCase {
public function testLinter() {
$this->executeTestsInDirectory(
dirname(__FILE__).'/parent-member-references/');
}
}

View file

@ -0,0 +1,7 @@
<?php
class Foobar {
public function someMethod() {
Bar::someOtherMethod();
}
}
~~~~~~~~~~

View file

@ -0,0 +1,15 @@
<?php
class Foo extends Bar {
public function someMethod() {
Bar::someOtherMethod();
}
}
~~~~~~~~~~
warning:4:5
~~~~~~~~~~
<?php
class Foo extends Bar {
public function someMethod() {
parent::someOtherMethod();
}
}