1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-11-25 08:12:40 +01:00

Allow XHPAST lint name rules to be overridden through configuration

Summary:
See T326. Allow lint rules to be selectively overridden, e.g. for Conduit
methods.

Since FB has a long history of suggesting crazy patches for this stuff I think
we're safer just adding a hook class than trying to do some kind of regexp
magic.

Test Plan: Wrote a hook for Phabricator and linted some Conduit files without
issues. Ran unit tests.

Reviewers: nh, jungejason, tuomaspelkonen, aran

Reviewed By: nh

CC: aran, nh

Differential Revision: 874
This commit is contained in:
epriestley 2011-08-30 13:07:01 -07:00
parent aa138a80d2
commit 0c11b5c70c
4 changed files with 176 additions and 55 deletions

View file

@ -80,6 +80,7 @@ phutil_register_library_map(array(
'ArcanistUsageException' => 'exception/usage', 'ArcanistUsageException' => 'exception/usage',
'ArcanistUserAbortException' => 'exception/usage/userabort', 'ArcanistUserAbortException' => 'exception/usage/userabort',
'ArcanistWorkingCopyIdentity' => 'workingcopyidentity', 'ArcanistWorkingCopyIdentity' => 'workingcopyidentity',
'ArcanistXHPASTLintNamingHook' => 'lint/linter/xhpast/naminghook',
'ArcanistXHPASTLinter' => 'lint/linter/xhpast', 'ArcanistXHPASTLinter' => 'lint/linter/xhpast',
'ArcanistXHPASTLinterTestCase' => 'lint/linter/xhpast/__tests__', 'ArcanistXHPASTLinterTestCase' => 'lint/linter/xhpast/__tests__',
'BranchInfo' => 'branch', 'BranchInfo' => 'branch',

View file

@ -665,27 +665,37 @@ class ArcanistXHPASTLinter extends ArcanistLinter {
} }
protected function lintNamingConventions($root) { protected function lintNamingConventions($root) {
// We're going to build up a list of <type, name, token, error> tuples
// and then try to instantiate a hook class which has the opportunity to
// override us.
$names = array();
$classes = $root->selectDescendantsOfType('n_CLASS_DECLARATION'); $classes = $root->selectDescendantsOfType('n_CLASS_DECLARATION');
foreach ($classes as $class) { foreach ($classes as $class) {
$name_token = $class->getChildByIndex(1); $name_token = $class->getChildByIndex(1);
$name_string = $name_token->getConcreteString(); $name_string = $name_token->getConcreteString();
$is_xhp = ($name_string[0] == ':'); $is_xhp = ($name_string[0] == ':');
if ($is_xhp) { if ($is_xhp) {
if (!$this->isLowerCaseWithXHP($name_string)) { $names[] = array(
$this->raiseLintAtNode( 'xhp-class',
$name_string,
$name_token, $name_token,
self::LINT_NAMING_CONVENTIONS, $this->isLowerCaseWithXHP($name_string)
'Follow naming conventions: xhp elements should be named using '. ? null
'lower case.'); : 'Follow naming conventions: XHP elements should be named using '.
} 'lower case.',
);
} else { } else {
if (!$this->isUpperCamelCase($name_string)) { $names[] = array(
$this->raiseLintAtNode( 'class',
$name_string,
$name_token, $name_token,
self::LINT_NAMING_CONVENTIONS, $this->isUpperCamelCase($name_string)
'Follow naming conventions: classes should be named using '. ? null
'UpperCamelCase.'); : 'Follow naming conventions: classes should be named using '.
} 'UpperCamelCase.',
);
} }
} }
@ -693,13 +703,15 @@ class ArcanistXHPASTLinter extends ArcanistLinter {
foreach ($ifaces as $iface) { foreach ($ifaces as $iface) {
$name_token = $iface->getChildByIndex(1); $name_token = $iface->getChildByIndex(1);
$name_string = $name_token->getConcreteString(); $name_string = $name_token->getConcreteString();
if (!$this->isUpperCamelCase($name_string)) { $names[] = array(
$this->raiseLintAtNode( 'interface',
$name_string,
$name_token, $name_token,
self::LINT_NAMING_CONVENTIONS, $this->isUpperCamelCase($name_string)
'Follow naming conventions: interfaces should be named using '. ? null
'UpperCamelCase.'); : 'Follow naming conventions: interfaces should be named using '.
} 'UpperCamelCase.',
);
} }
@ -711,13 +723,15 @@ class ArcanistXHPASTLinter extends ArcanistLinter {
continue; continue;
} }
$name_string = $name_token->getConcreteString(); $name_string = $name_token->getConcreteString();
if (!$this->isLowercaseWithUnderscores($name_string)) { $names[] = array(
$this->raiseLintAtNode( 'function',
$name_string,
$name_token, $name_token,
self::LINT_NAMING_CONVENTIONS, $this->isLowercaseWithUnderscores($name_string)
'Follow naming conventions: functions should be named using '. ? null
'lowercase_with_underscores.'); : 'Follow naming conventions: functions should be named using '.
} 'lowercase_with_underscores.',
);
} }
@ -725,13 +739,15 @@ class ArcanistXHPASTLinter extends ArcanistLinter {
foreach ($methods as $method) { foreach ($methods as $method) {
$name_token = $method->getChildByIndex(2); $name_token = $method->getChildByIndex(2);
$name_string = $name_token->getConcreteString(); $name_string = $name_token->getConcreteString();
if (!$this->isLowerCamelCase($name_string)) { $names[] = array(
$this->raiseLintAtNode( 'method',
$name_string,
$name_token, $name_token,
self::LINT_NAMING_CONVENTIONS, $this->isLowerCamelCase($name_string)
'Follow naming conventions: methods should be named using '. ? null
'lowerCamelCase.'); : 'Follow naming conventions: methods should be named using '.
} 'lowerCamelCase.',
);
} }
@ -740,13 +756,15 @@ class ArcanistXHPASTLinter extends ArcanistLinter {
foreach ($param_list->getChildren() as $param) { foreach ($param_list->getChildren() as $param) {
$name_token = $param->getChildByIndex(1); $name_token = $param->getChildByIndex(1);
$name_string = $name_token->getConcreteString(); $name_string = $name_token->getConcreteString();
if (!$this->isLowercaseWithUnderscores($name_string)) { $names[] = array(
$this->raiseLintAtNode( 'parameter',
$name_string,
$name_token, $name_token,
self::LINT_NAMING_CONVENTIONS, $this->isLowercaseWithUnderscores($name_string)
'Follow naming conventions: parameters should be named using '. ? null
'lowercase_with_underscores.'); : 'Follow naming conventions: parameters should be named using '.
} 'lowercase_with_underscores.',
);
} }
} }
@ -757,13 +775,15 @@ class ArcanistXHPASTLinter extends ArcanistLinter {
foreach ($constant_list->getChildren() as $constant) { foreach ($constant_list->getChildren() as $constant) {
$name_token = $constant->getChildByIndex(0); $name_token = $constant->getChildByIndex(0);
$name_string = $name_token->getConcreteString(); $name_string = $name_token->getConcreteString();
if (!$this->isUppercaseWithUnderscores($name_string)) { $names[] = array(
$this->raiseLintAtNode( 'constant',
$name_string,
$name_token, $name_token,
self::LINT_NAMING_CONVENTIONS, $this->isUppercaseWithUnderscores($name_string)
'Follow naming conventions: class constants should be named using '. ? null
'UPPERCASE_WITH_UNDERSCORES.'); : 'Follow naming conventions: class constants should be named '.
} 'using UPPERCASE_WITH_UNDERSCORES.',
);
} }
} }
@ -775,14 +795,44 @@ class ArcanistXHPASTLinter extends ArcanistLinter {
} }
$name_token = $prop->getChildByIndex(0); $name_token = $prop->getChildByIndex(0);
$name_string = $name_token->getConcreteString(); $name_string = $name_token->getConcreteString();
if (!$this->isLowerCamelCase($name_string)) { $names[] = array(
$this->raiseLintAtNode( 'member',
$name_string,
$name_token, $name_token,
self::LINT_NAMING_CONVENTIONS, $this->isLowerCamelCase($name_string)
'Follow naming conventions: class properties should be named '. ? null
'using lowerCamelCase.'); : 'Follow naming conventions: class properties should be named '.
'using lowerCamelCase.',
);
} }
} }
$engine = $this->getEngine();
$working_copy = $engine->getWorkingCopy();
if ($working_copy) {
// If a naming hook is configured, give it a chance to override the
// default results for all the symbol names.
$hook_class = $working_copy->getConfig('lint.xhpast.naminghook');
if ($hook_class) {
$hook_obj = newv($hook_class, array());
foreach ($names as $k => $name_attrs) {
list($type, $name, $token, $default) = $name_attrs;
$result = $hook_obj->lintSymbolName($type, $name, $default);
$names[$k][3] = $result;
}
}
}
// Raise anything we're left with.
foreach ($names as $k => $name_attrs) {
list($type, $name, $token, $result) = $name_attrs;
if ($result) {
$this->raiseLintAtNode(
$token,
self::LINT_NAMING_CONVENTIONS,
$result);
}
} }
} }

View file

@ -0,0 +1,60 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* You can extend this class and set "lint.xhpast.naminghook" in your
* .arcconfig to have an opportunity to override lint results for symbol names.
*
* @task override Overriding Symbol Name Lint
* @group lint
*/
abstract class ArcanistXHPASTLintNamingHook {
final public function __construct() {
// <empty>
}
/**
* Callback invoked for each symbol, which can override the default
* determination of name validity or accept it by returning $default. The
* symbol types are: xhp-class, class, interface, function, method, parameter,
* constant, and member.
*
* For example, if you want to ban all symbols with "quack" in them and
* otherwise accept all the defaults, except allow any naming convention for
* methods with "duck" in them, you might implement the method like this:
*
* if (preg_match('/quack/i', $name)) {
* return 'Symbol names containing "quack" are forbidden.';
* }
* if ($type == 'method' && preg_match('/duck/i', $name)) {
* return null; // Always accept.
* }
* return $default;
*
* @param string The symbol type.
* @param string The symbol name.
* @param string|null The default result from the main rule engine.
* @return string|null Null to accept the name, or a message to reject it
* with. You should return the default value if you don't
* want to specifically provide an override.
* @task override
*/
abstract public function lintSymbolName($type, $name, $default);
}

View file

@ -0,0 +1,10 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_source('ArcanistXHPASTLintNamingHook.php');