diff --git a/src/lint/linter/ArcanistXHPASTLinter.php b/src/lint/linter/ArcanistXHPASTLinter.php --- a/src/lint/linter/ArcanistXHPASTLinter.php +++ b/src/lint/linter/ArcanistXHPASTLinter.php @@ -67,14 +67,21 @@ } public function setLinterConfigurationValue($key, $value) { + $matched = false; + foreach ($this->rules as $rule) { foreach ($rule->getLinterConfigurationOptions() as $k => $spec) { if ($k == $key) { - return $rule->setLinterConfigurationValue($key, $value); + $matched = true; + $rule->setLinterConfigurationValue($key, $value); } } } + if ($matched) { + return; + } + return parent::setLinterConfigurationValue($key, $value); } diff --git a/src/lint/linter/__tests__/xhpast/self-member-references-php53.lint-test b/src/lint/linter/__tests__/xhpast/self-member-references-php53.lint-test new file mode 100644 --- /dev/null +++ b/src/lint/linter/__tests__/xhpast/self-member-references-php53.lint-test @@ -0,0 +1,19 @@ +setAncestorClass(__CLASS__) @@ -47,10 +50,29 @@ } public function getLinterConfigurationOptions() { - return array(); + return array( + 'xhpast.php-version' => array( + 'type' => 'optional string', + 'help' => pht('PHP version to target.'), + ), + 'xhpast.php-version.windows' => array( + 'type' => 'optional string', + 'help' => pht('PHP version to target on Windows.'), + ), + ); } - public function setLinterConfigurationValue($key, $value) {} + public function setLinterConfigurationValue($key, $value) { + switch ($key) { + case 'xhpast.php-version': + $this->version = $value; + return; + + case 'xhpast.php-version.windows': + $this->windowsVersion = $value; + return; + } + } abstract public function process(XHPASTNode $root); @@ -139,6 +161,28 @@ /* -( Utility )------------------------------------------------------------ */ /** + * Retrieve all anonymous closure(s). + * + * Returns all descendant nodes which represent an anonymous function + * declaration. + * + * @param XHPASTNode Root node. + * @return AASTNodeList + */ + protected function getAnonymousClosures(XHPASTNode $root) { + $func_decls = $root->selectDescendantsOfType('n_FUNCTION_DECLARATION'); + $nodes = array(); + + foreach ($func_decls as $func_decl) { + if ($func_decl->getChildByIndex(2)->getTypeName() == 'n_EMPTY') { + $nodes[] = $func_decl; + } + } + + return AASTNodeList::newFromTreeAndNodes($root->getTree(), $nodes); + } + + /** * Retrieve all calls to some specified function(s). * * Returns all descendant nodes which represent a function call to one of the diff --git a/src/lint/linter/xhpast/rules/ArcanistPHPCompatibilityXHPASTLinterRule.php b/src/lint/linter/xhpast/rules/ArcanistPHPCompatibilityXHPASTLinterRule.php --- a/src/lint/linter/xhpast/rules/ArcanistPHPCompatibilityXHPASTLinterRule.php +++ b/src/lint/linter/xhpast/rules/ArcanistPHPCompatibilityXHPASTLinterRule.php @@ -5,41 +5,10 @@ const ID = 45; - private $version; - private $windowsVersion; - public function getLintName() { return pht('PHP Compatibility'); } - public function getLinterConfigurationOptions() { - return parent::getLinterConfigurationOptions() + array( - 'xhpast.php-version' => array( - 'type' => 'optional string', - 'help' => pht('PHP version to target.'), - ), - 'xhpast.php-version.windows' => array( - 'type' => 'optional string', - 'help' => pht('PHP version to target on Windows.'), - ), - ); - } - - public function setLinterConfigurationValue($key, $value) { - switch ($key) { - case 'xhpast.php-version': - $this->version = $value; - return; - - case 'xhpast.php-version.windows': - $this->windowsVersion = $value; - return; - - default: - return parent::setLinterConfigurationValue($key, $value); - } - } - public function process(XHPASTNode $root) { static $compat_info; diff --git a/src/lint/linter/xhpast/rules/ArcanistSelfMemberReferenceXHPASTLinterRule.php b/src/lint/linter/xhpast/rules/ArcanistSelfMemberReferenceXHPASTLinterRule.php --- a/src/lint/linter/xhpast/rules/ArcanistSelfMemberReferenceXHPASTLinterRule.php +++ b/src/lint/linter/xhpast/rules/ArcanistSelfMemberReferenceXHPASTLinterRule.php @@ -23,6 +23,7 @@ $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 @@ -35,12 +36,23 @@ $class_ref_name = $class_ref->getConcreteString(); if (strtolower($class_name) == strtolower($class_ref_name)) { - $this->raiseLintAtNode( - $class_ref, - pht( - 'Use `%s` for local static member references.', - 'self::'), - 'self'); + $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` for local static member references.', + 'self::'), + 'self'); + } } static $self_refs = array(