mirror of
https://we.phorge.it/source/arcanist.git
synced 2024-11-22 14:52:40 +01:00
Add XHAST linter standards
Summary: Ref T8742. As mentioned in D13512. This still needs some work, but looks roughly how I expect it to. Mainly, I want to move the standards stuff to the linter itself rather than the linter rule. I wanted to push this out for some initial feedback though. Test Plan: This still needs work. Reviewers: epriestley, #blessed_reviewers Reviewed By: epriestley, #blessed_reviewers Subscribers: Korvin Maniphest Tasks: T8742 Differential Revision: https://secure.phabricator.com/D13942
This commit is contained in:
parent
2db40f9953
commit
dd0deb2407
6 changed files with 208 additions and 10 deletions
10
.arclint
10
.arclint
|
@ -45,15 +45,7 @@
|
||||||
"xhpast": {
|
"xhpast": {
|
||||||
"type": "xhpast",
|
"type": "xhpast",
|
||||||
"include": "(\\.php$)",
|
"include": "(\\.php$)",
|
||||||
"severity": {
|
"standard": "phutil.xhpast"
|
||||||
"16": "advice",
|
|
||||||
"34": "error"
|
|
||||||
},
|
|
||||||
"xhpast.blacklisted.function": {
|
|
||||||
"eval": "The eval() function should be avoided. It is potentially unsafe and makes debugging more difficult."
|
|
||||||
},
|
|
||||||
"xhpast.php-version": "5.2.3",
|
|
||||||
"xhpast.php-version.windows": "5.3.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -161,6 +161,8 @@ phutil_register_library_map(array(
|
||||||
'ArcanistLintSeverity' => 'lint/ArcanistLintSeverity.php',
|
'ArcanistLintSeverity' => 'lint/ArcanistLintSeverity.php',
|
||||||
'ArcanistLintWorkflow' => 'workflow/ArcanistLintWorkflow.php',
|
'ArcanistLintWorkflow' => 'workflow/ArcanistLintWorkflow.php',
|
||||||
'ArcanistLinter' => 'lint/linter/ArcanistLinter.php',
|
'ArcanistLinter' => 'lint/linter/ArcanistLinter.php',
|
||||||
|
'ArcanistLinterStandard' => 'lint/linter/standards/ArcanistLinterStandard.php',
|
||||||
|
'ArcanistLinterStandardTestCase' => 'lint/linter/standards/__tests__/ArcanistLinterStandardTestCase.php',
|
||||||
'ArcanistLinterTestCase' => 'lint/linter/__tests__/ArcanistLinterTestCase.php',
|
'ArcanistLinterTestCase' => 'lint/linter/__tests__/ArcanistLinterTestCase.php',
|
||||||
'ArcanistLintersWorkflow' => 'workflow/ArcanistLintersWorkflow.php',
|
'ArcanistLintersWorkflow' => 'workflow/ArcanistLintersWorkflow.php',
|
||||||
'ArcanistListAssignmentXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistListAssignmentXHPASTLinterRule.php',
|
'ArcanistListAssignmentXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistListAssignmentXHPASTLinterRule.php',
|
||||||
|
@ -202,6 +204,7 @@ phutil_register_library_map(array(
|
||||||
'ArcanistPhrequentWorkflow' => 'workflow/ArcanistPhrequentWorkflow.php',
|
'ArcanistPhrequentWorkflow' => 'workflow/ArcanistPhrequentWorkflow.php',
|
||||||
'ArcanistPhutilLibraryLinter' => 'lint/linter/ArcanistPhutilLibraryLinter.php',
|
'ArcanistPhutilLibraryLinter' => 'lint/linter/ArcanistPhutilLibraryLinter.php',
|
||||||
'ArcanistPhutilXHPASTLinter' => 'lint/linter/ArcanistPhutilXHPASTLinter.php',
|
'ArcanistPhutilXHPASTLinter' => 'lint/linter/ArcanistPhutilXHPASTLinter.php',
|
||||||
|
'ArcanistPhutilXHPASTLinterStandard' => 'lint/linter/standards/phutil/ArcanistPhutilXHPASTLinterStandard.php',
|
||||||
'ArcanistPhutilXHPASTLinterTestCase' => 'lint/linter/__tests__/ArcanistPhutilXHPASTLinterTestCase.php',
|
'ArcanistPhutilXHPASTLinterTestCase' => 'lint/linter/__tests__/ArcanistPhutilXHPASTLinterTestCase.php',
|
||||||
'ArcanistPlusOperatorOnStringsXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistPlusOperatorOnStringsXHPASTLinterRule.php',
|
'ArcanistPlusOperatorOnStringsXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistPlusOperatorOnStringsXHPASTLinterRule.php',
|
||||||
'ArcanistPregQuoteMisuseXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistPregQuoteMisuseXHPASTLinterRule.php',
|
'ArcanistPregQuoteMisuseXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistPregQuoteMisuseXHPASTLinterRule.php',
|
||||||
|
@ -450,6 +453,8 @@ phutil_register_library_map(array(
|
||||||
'ArcanistLintSeverity' => 'Phobject',
|
'ArcanistLintSeverity' => 'Phobject',
|
||||||
'ArcanistLintWorkflow' => 'ArcanistWorkflow',
|
'ArcanistLintWorkflow' => 'ArcanistWorkflow',
|
||||||
'ArcanistLinter' => 'Phobject',
|
'ArcanistLinter' => 'Phobject',
|
||||||
|
'ArcanistLinterStandard' => 'Phobject',
|
||||||
|
'ArcanistLinterStandardTestCase' => 'PhutilTestCase',
|
||||||
'ArcanistLinterTestCase' => 'PhutilTestCase',
|
'ArcanistLinterTestCase' => 'PhutilTestCase',
|
||||||
'ArcanistLintersWorkflow' => 'ArcanistWorkflow',
|
'ArcanistLintersWorkflow' => 'ArcanistWorkflow',
|
||||||
'ArcanistListAssignmentXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
'ArcanistListAssignmentXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||||
|
@ -491,6 +496,7 @@ phutil_register_library_map(array(
|
||||||
'ArcanistPhrequentWorkflow' => 'ArcanistWorkflow',
|
'ArcanistPhrequentWorkflow' => 'ArcanistWorkflow',
|
||||||
'ArcanistPhutilLibraryLinter' => 'ArcanistLinter',
|
'ArcanistPhutilLibraryLinter' => 'ArcanistLinter',
|
||||||
'ArcanistPhutilXHPASTLinter' => 'ArcanistBaseXHPASTLinter',
|
'ArcanistPhutilXHPASTLinter' => 'ArcanistBaseXHPASTLinter',
|
||||||
|
'ArcanistPhutilXHPASTLinterStandard' => 'ArcanistLinterStandard',
|
||||||
'ArcanistPhutilXHPASTLinterTestCase' => 'ArcanistLinterTestCase',
|
'ArcanistPhutilXHPASTLinterTestCase' => 'ArcanistLinterTestCase',
|
||||||
'ArcanistPlusOperatorOnStringsXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
'ArcanistPlusOperatorOnStringsXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||||
'ArcanistPregQuoteMisuseXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
'ArcanistPregQuoteMisuseXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
|
||||||
|
|
|
@ -196,7 +196,6 @@ abstract class ArcanistLinter extends Phobject {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function getLinterPriority() {
|
public function getLinterPriority() {
|
||||||
return 1.0;
|
return 1.0;
|
||||||
}
|
}
|
||||||
|
@ -209,6 +208,11 @@ abstract class ArcanistLinter extends Phobject {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function addCustomSeverityMap(array $map) {
|
||||||
|
$this->customSeverityMap = $this->customSeverityMap + $map;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
final public function setCustomSeverityRules(array $rules) {
|
final public function setCustomSeverityRules(array $rules) {
|
||||||
$this->customSeverityRules = $rules;
|
$this->customSeverityRules = $rules;
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -490,6 +494,10 @@ abstract class ArcanistLinter extends Phobject {
|
||||||
'Provide a map of regular expressions to severity levels. All '.
|
'Provide a map of regular expressions to severity levels. All '.
|
||||||
'matching codes have their severity adjusted.'),
|
'matching codes have their severity adjusted.'),
|
||||||
),
|
),
|
||||||
|
'standard' => array(
|
||||||
|
'type' => 'optional string | list<string>',
|
||||||
|
'help' => pht('The coding standard(s) to apply.'),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -548,6 +556,22 @@ abstract class ArcanistLinter extends Phobject {
|
||||||
}
|
}
|
||||||
$this->setCustomSeverityRules($value);
|
$this->setCustomSeverityRules($value);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
case 'standard':
|
||||||
|
$standards = (array)$value;
|
||||||
|
|
||||||
|
foreach ($standards as $standard) {
|
||||||
|
$standard = ArcanistLinterStandard::getStandard($value, $this);
|
||||||
|
|
||||||
|
foreach ($standard->getLinterConfiguration() as $k => $v) {
|
||||||
|
$this->setLinterConfigurationValue($k, $v);
|
||||||
|
}
|
||||||
|
$this->addCustomSeverityMap($standard->getLinterSeverityMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Exception(pht('Incomplete implementation: %s!', $key));
|
throw new Exception(pht('Incomplete implementation: %s!', $key));
|
||||||
|
|
121
src/lint/linter/standards/ArcanistLinterStandard.php
Normal file
121
src/lint/linter/standards/ArcanistLinterStandard.php
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A "linter standard" is a collection of linter rules with associated
|
||||||
|
* severities and configuration.
|
||||||
|
*
|
||||||
|
* Basically, a linter standard allows a set of linter rules and configuration
|
||||||
|
* to be easily reused across multiple repositories without duplicating the
|
||||||
|
* contents of the `.arclint` file (and the associated maintenance costs in
|
||||||
|
* keeping changes to this file synchronized).
|
||||||
|
*/
|
||||||
|
abstract class ArcanistLinterStandard extends Phobject {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a unique identifier for the linter standard.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
abstract public function getKey();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a human-readable name for the linter standard.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
abstract public function getName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a human-readable description for the linter standard.
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
abstract public function getDescription();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the linter standard supports a specified linter.
|
||||||
|
*
|
||||||
|
* @param ArcanistLinter The linter which is being configured.
|
||||||
|
* @return bool True if the linter standard supports the specified
|
||||||
|
* linter, otherwise false.
|
||||||
|
*/
|
||||||
|
abstract public function supportsLinter(ArcanistLinter $linter);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get linter configuration.
|
||||||
|
*
|
||||||
|
* Returns linter configuration which is passed to
|
||||||
|
* @{method:ArcanistLinter::setLinterConfigurationValue}.
|
||||||
|
*
|
||||||
|
* @return map<string, wild>
|
||||||
|
*/
|
||||||
|
public function getLinterConfiguration() {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get linter severities.
|
||||||
|
*
|
||||||
|
* Returns linter severities which are passed to
|
||||||
|
* @{method:ArcanistLinter::addCustomSeverityMap}.
|
||||||
|
*
|
||||||
|
* @return map
|
||||||
|
*/
|
||||||
|
public function getLinterSeverityMap() {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load a linter standard by key.
|
||||||
|
*
|
||||||
|
* @param string
|
||||||
|
* @param ArcanistLinter
|
||||||
|
* @return ArcanistLinterStandard
|
||||||
|
*/
|
||||||
|
final public static function getStandard($key, ArcanistLinter $linter) {
|
||||||
|
$standards = self::loadAllStandardsForLinter($linter);
|
||||||
|
|
||||||
|
if (empty($standards[$key])) {
|
||||||
|
throw new ArcanistUsageException(
|
||||||
|
pht(
|
||||||
|
'No such linter standard. Available standards are: %s.',
|
||||||
|
implode(', ', array_keys($standards))));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $standards[$key];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load all linter standards.
|
||||||
|
*
|
||||||
|
* @return list<ArcanistLinterStandard>
|
||||||
|
*/
|
||||||
|
final public static function loadAllStandards() {
|
||||||
|
return id(new PhutilClassMapQuery())
|
||||||
|
->setAncestorClass(__CLASS__)
|
||||||
|
->setUniqueMethod('getKey')
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load all linter standards which support a specified linter.
|
||||||
|
*
|
||||||
|
* @param ArcanistLinter
|
||||||
|
* @return list<ArcanistLinterStandard>
|
||||||
|
*/
|
||||||
|
final public static function loadAllStandardsForLinter(
|
||||||
|
ArcanistLinter $linter) {
|
||||||
|
|
||||||
|
$all_standards = self::loadAllStandards();
|
||||||
|
$standards = array();
|
||||||
|
|
||||||
|
foreach ($all_standards as $standard) {
|
||||||
|
if ($standard->supportsLinter($linter)) {
|
||||||
|
$standards[$standard->getKey()] = $standard;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $standards;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class ArcanistLinterStandardTestCase extends PhutilTestCase {
|
||||||
|
|
||||||
|
public function testLoadAllStandards() {
|
||||||
|
ArcanistLinterStandard::loadAllStandards();
|
||||||
|
$this->assertTrue(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class ArcanistPhutilXHPASTLinterStandard
|
||||||
|
extends ArcanistLinterStandard {
|
||||||
|
|
||||||
|
public function getKey() {
|
||||||
|
return 'phutil.xhpast';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName() {
|
||||||
|
return pht('Phutil XHPAST');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDescription() {
|
||||||
|
return pht('PHP Coding Standards for Phutil libraries.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function supportsLinter(ArcanistLinter $linter) {
|
||||||
|
return $linter instanceof ArcanistXHPASTLinter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLinterConfiguration() {
|
||||||
|
return array(
|
||||||
|
'xhpast.blacklisted.function' => array(
|
||||||
|
'eval' => pht(
|
||||||
|
'The `%s` function should be avoided. It is potentially unsafe '.
|
||||||
|
'and makes debugging more difficult.',
|
||||||
|
'eval'),
|
||||||
|
'xhpast.php-version' => '5.2.3',
|
||||||
|
'xhpast.php-version.windows' => '5.3.0',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getLinterSeverityMap() {
|
||||||
|
$advice = ArcanistLintSeverity::SEVERITY_ADVICE;
|
||||||
|
$error = ArcanistLintSeverity::SEVERITY_ERROR;
|
||||||
|
|
||||||
|
return array(
|
||||||
|
ArcanistTodoCommentXHPASTLinterRule::ID => $advice,
|
||||||
|
ArcanistCommentSpacingXHPASTLinterRule::ID => $error,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue