Page MenuHomePhabricator

D13867.id33493.diff
No OneTemporary

D13867.id33493.diff

diff --git a/.arclint b/.arclint
--- a/.arclint
+++ b/.arclint
@@ -31,10 +31,6 @@
"type": "phutil-library",
"include": "(\\.php$)"
},
- "phutil-xhpast": {
- "type": "phutil-xhpast",
- "include": "(\\.php$)"
- },
"spelling": {
"type": "spelling",
"exclude": "(^resources/spelling/.*\\.json$)"
@@ -47,7 +43,11 @@
"include": "(\\.php$)",
"severity": {
"16": "advice",
- "34": "error"
+ "34": "error",
+ "1002": "warning",
+ "1004": "warning",
+ "1005": "warning",
+ "1006": "advice"
},
"xhpast.blacklisted.function": {
"eval": "The eval() function should be avoided. It is potentially unsafe and makes debugging more difficult."
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -13,6 +13,7 @@
'ArcanistAliasWorkflow' => 'workflow/ArcanistAliasWorkflow.php',
'ArcanistAmendWorkflow' => 'workflow/ArcanistAmendWorkflow.php',
'ArcanistAnoidWorkflow' => 'workflow/ArcanistAnoidWorkflow.php',
+ 'ArcanistArrayCombineXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistArrayCombineXHPASTLinterRule.php',
'ArcanistArrayIndexSpacingXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistArrayIndexSpacingXHPASTLinterRule.php',
'ArcanistArraySeparatorXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistArraySeparatorXHPASTLinterRule.php',
'ArcanistArrayValueXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistArrayValueXHPASTLinterRule.php',
@@ -40,6 +41,7 @@
'ArcanistCheckstyleXMLLintRenderer' => 'lint/renderer/ArcanistCheckstyleXMLLintRenderer.php',
'ArcanistChmodLinter' => 'lint/linter/ArcanistChmodLinter.php',
'ArcanistChmodLinterTestCase' => 'lint/linter/__tests__/ArcanistChmodLinterTestCase.php',
+ 'ArcanistClassExtendsPhobjectXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistClassExtendsPhobjectXHPASTLinterRule.php',
'ArcanistClassFilenameMismatchXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistClassFilenameMismatchXHPASTLinterRule.php',
'ArcanistClassNameLiteralXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistClassNameLiteralXHPASTLinterRule.php',
'ArcanistCloseRevisionWorkflow' => 'workflow/ArcanistCloseRevisionWorkflow.php',
@@ -70,6 +72,7 @@
'ArcanistCpplintLinterTestCase' => 'lint/linter/__tests__/ArcanistCpplintLinterTestCase.php',
'ArcanistDeclarationParenthesesXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistDeclarationParenthesesXHPASTLinterRule.php',
'ArcanistDefaultParametersXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistDefaultParametersXHPASTLinterRule.php',
+ 'ArcanistDeprecatedFunctionXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistDeprecatedFunctionXHPASTLinterRule.php',
'ArcanistDiffChange' => 'parser/diff/ArcanistDiffChange.php',
'ArcanistDiffChangeType' => 'parser/diff/ArcanistDiffChangeType.php',
'ArcanistDiffHunk' => 'parser/diff/ArcanistDiffHunk.php',
@@ -191,8 +194,7 @@
'ArcanistPhpunitTestResultParser' => 'unit/parser/ArcanistPhpunitTestResultParser.php',
'ArcanistPhrequentWorkflow' => 'workflow/ArcanistPhrequentWorkflow.php',
'ArcanistPhutilLibraryLinter' => 'lint/linter/ArcanistPhutilLibraryLinter.php',
- 'ArcanistPhutilXHPASTLinter' => 'lint/linter/ArcanistPhutilXHPASTLinter.php',
- 'ArcanistPhutilXHPASTLinterTestCase' => 'lint/linter/__tests__/ArcanistPhutilXHPASTLinterTestCase.php',
+ 'ArcanistPhutilXHPASTLinterRule' => 'lint/linter/xhpast/ArcanistPhutilXHPASTLinterRule.php',
'ArcanistPlusOperatorOnStringsXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistPlusOperatorOnStringsXHPASTLinterRule.php',
'ArcanistPregQuoteMisuseXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistPregQuoteMisuseXHPASTLinterRule.php',
'ArcanistPuppetLintLinter' => 'lint/linter/ArcanistPuppetLintLinter.php',
@@ -201,6 +203,7 @@
'ArcanistPyFlakesLinterTestCase' => 'lint/linter/__tests__/ArcanistPyFlakesLinterTestCase.php',
'ArcanistPyLintLinter' => 'lint/linter/ArcanistPyLintLinter.php',
'ArcanistPyLintLinterTestCase' => 'lint/linter/__tests__/ArcanistPyLintLinterTestCase.php',
+ 'ArcanistRaggedClassTreeEdgeXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistRaggedClassTreeEdgeXHPASTLinterRule.php',
'ArcanistRepositoryAPI' => 'repository/api/ArcanistRepositoryAPI.php',
'ArcanistRepositoryAPIMiscTestCase' => 'repository/api/__tests__/ArcanistRepositoryAPIMiscTestCase.php',
'ArcanistRepositoryAPIStateTestCase' => 'repository/api/__tests__/ArcanistRepositoryAPIStateTestCase.php',
@@ -252,6 +255,7 @@
'ArcanistUnitWorkflow' => 'workflow/ArcanistUnitWorkflow.php',
'ArcanistUnnecessaryFinalModifierXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistUnnecessaryFinalModifierXHPASTLinterRule.php',
'ArcanistUnnecessarySemicolonXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistUnnecessarySemicolonXHPASTLinterRule.php',
+ 'ArcanistUnsafeDynamicStringXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistUnsafeDynamicStringXHPASTLinterRule.php',
'ArcanistUpgradeWorkflow' => 'workflow/ArcanistUpgradeWorkflow.php',
'ArcanistUploadWorkflow' => 'workflow/ArcanistUploadWorkflow.php',
'ArcanistUsageException' => 'exception/ArcanistUsageException.php',
@@ -292,6 +296,7 @@
'ArcanistAliasWorkflow' => 'ArcanistWorkflow',
'ArcanistAmendWorkflow' => 'ArcanistWorkflow',
'ArcanistAnoidWorkflow' => 'ArcanistWorkflow',
+ 'ArcanistArrayCombineXHPASTLinterRule' => 'ArcanistPhutilXHPASTLinterRule',
'ArcanistArrayIndexSpacingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistArraySeparatorXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistArrayValueXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
@@ -319,6 +324,7 @@
'ArcanistCheckstyleXMLLintRenderer' => 'ArcanistLintRenderer',
'ArcanistChmodLinter' => 'ArcanistLinter',
'ArcanistChmodLinterTestCase' => 'ArcanistLinterTestCase',
+ 'ArcanistClassExtendsPhobjectXHPASTLinterRule' => 'ArcanistPhutilXHPASTLinterRule',
'ArcanistClassFilenameMismatchXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistClassNameLiteralXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistCloseRevisionWorkflow' => 'ArcanistWorkflow',
@@ -349,6 +355,7 @@
'ArcanistCpplintLinterTestCase' => 'ArcanistExternalLinterTestCase',
'ArcanistDeclarationParenthesesXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistDefaultParametersXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
+ 'ArcanistDeprecatedFunctionXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistDiffChange' => 'Phobject',
'ArcanistDiffChangeType' => 'Phobject',
'ArcanistDiffHunk' => 'Phobject',
@@ -470,8 +477,7 @@
'ArcanistPhpunitTestResultParser' => 'ArcanistTestResultParser',
'ArcanistPhrequentWorkflow' => 'ArcanistWorkflow',
'ArcanistPhutilLibraryLinter' => 'ArcanistLinter',
- 'ArcanistPhutilXHPASTLinter' => 'ArcanistBaseXHPASTLinter',
- 'ArcanistPhutilXHPASTLinterTestCase' => 'ArcanistLinterTestCase',
+ 'ArcanistPhutilXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistPlusOperatorOnStringsXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistPregQuoteMisuseXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistPuppetLintLinter' => 'ArcanistExternalLinter',
@@ -480,6 +486,7 @@
'ArcanistPyFlakesLinterTestCase' => 'ArcanistExternalLinterTestCase',
'ArcanistPyLintLinter' => 'ArcanistExternalLinter',
'ArcanistPyLintLinterTestCase' => 'ArcanistExternalLinterTestCase',
+ 'ArcanistRaggedClassTreeEdgeXHPASTLinterRule' => 'ArcanistPhutilXHPASTLinterRule',
'ArcanistRepositoryAPI' => 'Phobject',
'ArcanistRepositoryAPIMiscTestCase' => 'PhutilTestCase',
'ArcanistRepositoryAPIStateTestCase' => 'PhutilTestCase',
@@ -531,6 +538,7 @@
'ArcanistUnitWorkflow' => 'ArcanistWorkflow',
'ArcanistUnnecessaryFinalModifierXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistUnnecessarySemicolonXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
+ 'ArcanistUnsafeDynamicStringXHPASTLinterRule' => 'ArcanistPhutilXHPASTLinterRule',
'ArcanistUpgradeWorkflow' => 'ArcanistWorkflow',
'ArcanistUploadWorkflow' => 'ArcanistWorkflow',
'ArcanistUsageException' => 'Exception',
diff --git a/src/lint/linter/ArcanistPhutilXHPASTLinter.php b/src/lint/linter/ArcanistPhutilXHPASTLinter.php
deleted file mode 100644
--- a/src/lint/linter/ArcanistPhutilXHPASTLinter.php
+++ /dev/null
@@ -1,336 +0,0 @@
-<?php
-
-final class ArcanistPhutilXHPASTLinter extends ArcanistBaseXHPASTLinter {
-
- const LINT_ARRAY_COMBINE = 2;
- const LINT_DEPRECATED_FUNCTION = 3;
- const LINT_UNSAFE_DYNAMIC_STRING = 4;
- const LINT_RAGGED_CLASSTREE_EDGE = 5;
- const LINT_EXTENDS_PHOBJECT = 6;
-
- private $deprecatedFunctions = array();
- private $dynamicStringFunctions = array();
- private $dynamicStringClasses = array();
-
- public function getInfoName() {
- return 'XHPAST/libphutil Lint';
- }
-
- public function getInfoDescription() {
- return pht(
- 'Use XHPAST to run libphutil-specific rules on a PHP library. This '.
- 'linter is intended for use in Phabricator libraries and extensions.');
- }
-
- public function getLintNameMap() {
- return array(
- self::LINT_ARRAY_COMBINE =>
- pht('%s Unreliable', 'array_combine()'),
- self::LINT_DEPRECATED_FUNCTION =>
- pht('Use of Deprecated Function'),
- self::LINT_UNSAFE_DYNAMIC_STRING =>
- pht('Unsafe Usage of Dynamic String'),
- self::LINT_RAGGED_CLASSTREE_EDGE =>
- pht('Class Not %s Or %s', 'abstract', 'final'),
- self::LINT_EXTENDS_PHOBJECT =>
- pht('Class Not Extending %s', 'Phobject'),
- );
- }
-
- public function getLinterName() {
- return 'PHLXHP';
- }
-
- public function getLinterConfigurationName() {
- return 'phutil-xhpast';
- }
-
- public function getLintSeverityMap() {
- $advice = ArcanistLintSeverity::SEVERITY_ADVICE;
- $warning = ArcanistLintSeverity::SEVERITY_WARNING;
-
- return array(
- self::LINT_ARRAY_COMBINE => $warning,
- self::LINT_DEPRECATED_FUNCTION => $warning,
- self::LINT_UNSAFE_DYNAMIC_STRING => $warning,
- self::LINT_RAGGED_CLASSTREE_EDGE => $warning,
- self::LINT_EXTENDS_PHOBJECT => $advice,
- );
- }
-
- public function getLinterConfigurationOptions() {
- $options = array(
- 'phutil-xhpast.deprecated.functions' => array(
- 'type' => 'optional map<string, string>',
- 'help' => pht(
- 'Functions which should should be considered deprecated.'),
- ),
- 'phutil-xhpast.dynamic-string.functions' => array(
- 'type' => 'optional map<string, string>',
- 'help' => pht(
- 'Functions which should should not be used because they represent '.
- 'the unsafe usage of dynamic strings.'),
- ),
- 'phutil-xhpast.dynamic-string.classes' => array(
- 'type' => 'optional map<string, string>',
- 'help' => pht(
- 'Classes which should should not be used because they represent the '.
- 'unsafe usage of dynamic strings.'),
- ),
- );
-
- return $options + parent::getLinterConfigurationOptions();
- }
-
- public function setLinterConfigurationValue($key, $value) {
- switch ($key) {
- case 'phutil-xhpast.deprecated.functions':
- $this->setDeprecatedFunctions($value);
- return;
- case 'phutil-xhpast.dynamic-string.functions':
- $this->setDynamicStringFunctions($value);
- return;
- case 'phutil-xhpast.dynamic-string.classes':
- $this->setDynamicStringClasses($value);
- return;
- default:
- parent::setLinterConfigurationValue($key, $value);
- return;
- }
- }
-
- public function getVersion() {
- // The version number should be incremented whenever a new rule is added.
- return '3';
- }
-
- protected function resolveFuture($path, Future $future) {
- $tree = $this->getXHPASTLinter()->getXHPASTTreeForPath($path);
- if (!$tree) {
- return;
- }
-
- $root = $tree->getRootNode();
-
- $method_codes = array(
- 'lintArrayCombine' => self::LINT_ARRAY_COMBINE,
- 'lintUnsafeDynamicString' => self::LINT_UNSAFE_DYNAMIC_STRING,
- 'lintDeprecatedFunctions' => self::LINT_DEPRECATED_FUNCTION,
- 'lintRaggedClasstreeEdges' => self::LINT_RAGGED_CLASSTREE_EDGE,
- 'lintClassExtendsPhobject' => self::LINT_EXTENDS_PHOBJECT,
- );
-
- foreach ($method_codes as $method => $codes) {
- foreach ((array)$codes as $code) {
- if ($this->isCodeEnabled($code)) {
- call_user_func(array($this, $method), $root);
- break;
- }
- }
- }
- }
-
-
-/* -( Setters )------------------------------------------------------------ */
-
- public function setDeprecatedFunctions(array $map) {
- $this->deprecatedFunctions = $map;
- return $this;
- }
-
- public function setDynamicStringClasses(array $map) {
- $this->dynamicStringClasses = $map;
- return $this;
- }
-
- public function setDynamicStringFunctions(array $map) {
- $this->dynamicStringFunctions = $map;
- return $this;
- }
-
-
-/* -( Linter Rules )------------------------------------------------------- */
-
- private function lintUnsafeDynamicString(XHPASTNode $root) {
- $safe = $this->dynamicStringFunctions + array(
- 'pht' => 0,
-
- 'hsprintf' => 0,
- 'jsprintf' => 0,
-
- 'hgsprintf' => 0,
-
- 'csprintf' => 0,
- 'vcsprintf' => 0,
- 'execx' => 0,
- 'exec_manual' => 0,
- 'phutil_passthru' => 0,
-
- 'qsprintf' => 1,
- 'vqsprintf' => 1,
- 'queryfx' => 1,
- 'queryfx_all' => 1,
- 'queryfx_one' => 1,
- );
-
- $calls = $root->selectDescendantsOfType('n_FUNCTION_CALL');
- $this->lintUnsafeDynamicStringCall($calls, $safe);
-
- $safe = $this->dynamicStringClasses + array(
- 'ExecFuture' => 0,
- );
-
- $news = $root->selectDescendantsOfType('n_NEW');
- $this->lintUnsafeDynamicStringCall($news, $safe);
- }
-
- private function lintUnsafeDynamicStringCall(
- AASTNodeList $calls,
- array $safe) {
-
- $safe = array_combine(
- array_map('strtolower', array_keys($safe)),
- $safe);
-
- foreach ($calls as $call) {
- $name = $call->getChildByIndex(0)->getConcreteString();
- $param = idx($safe, strtolower($name));
-
- if ($param === null) {
- continue;
- }
-
- $parameters = $call->getChildByIndex(1);
- if (count($parameters->getChildren()) <= $param) {
- continue;
- }
-
- $identifier = $parameters->getChildByIndex($param);
- if (!$identifier->isConstantString()) {
- $this->raiseLintAtNode(
- $call,
- self::LINT_UNSAFE_DYNAMIC_STRING,
- pht(
- "Parameter %d of %s should be a scalar string, ".
- "otherwise it's not safe.",
- $param + 1,
- $name.'()'));
- }
- }
- }
-
- private function lintArrayCombine(XHPASTNode $root) {
- $function_calls = $this->getFunctionCalls($root, array('array_combine'));
-
- foreach ($function_calls as $call) {
- $name = $call->getChildByIndex(0)->getConcreteString();
- $parameter_list = $call->getChildOfType(1, 'n_CALL_PARAMETER_LIST');
-
- if (count($parameter_list->getChildren()) !== 2) {
- // Wrong number of parameters, but raise that elsewhere if we want.
- continue;
- }
-
- $first = $parameter_list->getChildByIndex(0);
- $second = $parameter_list->getChildByIndex(1);
-
- if ($first->getConcreteString() == $second->getConcreteString()) {
- $this->raiseLintAtNode(
- $call,
- self::LINT_ARRAY_COMBINE,
- pht(
- 'Prior to PHP 5.4, `%s` fails when given empty arrays. '.
- 'Prefer to write `%s` as `%s`.',
- 'array_combine()',
- 'array_combine(x, x)',
- 'array_fuse(x)'));
- }
- }
- }
-
- private function lintDeprecatedFunctions(XHPASTNode $root) {
- $map = $this->deprecatedFunctions;
- $function_calls = $this->getFunctionCalls($root, array_keys($map));
-
- foreach ($function_calls as $call) {
- $name = $call
- ->getChildByIndex(0)
- ->getConcreteString();
-
- $name = strtolower($name);
- if (empty($map[$name])) {
- continue;
- }
-
- $this->raiseLintAtNode(
- $call,
- self::LINT_DEPRECATED_FUNCTION,
- $map[$name]);
- }
- }
-
- private function lintRaggedClasstreeEdges(XHPASTNode $root) {
- $parser = new PhutilDocblockParser();
-
- $classes = $root->selectDescendantsOfType('n_CLASS_DECLARATION');
- foreach ($classes as $class) {
- $is_final = false;
- $is_abstract = false;
- $is_concrete_extensible = false;
-
- $attributes = $class->getChildOfType(0, 'n_CLASS_ATTRIBUTES');
- foreach ($attributes->getChildren() as $child) {
- if ($child->getConcreteString() == 'final') {
- $is_final = true;
- }
- if ($child->getConcreteString() == 'abstract') {
- $is_abstract = true;
- }
- }
-
- $docblock = $class->getDocblockToken();
- if ($docblock) {
- list($text, $specials) = $parser->parse($docblock->getValue());
- $is_concrete_extensible = idx($specials, 'concrete-extensible');
- }
-
- if (!$is_final && !$is_abstract && !$is_concrete_extensible) {
- $this->raiseLintAtNode(
- $class->getChildOfType(1, 'n_CLASS_NAME'),
- self::LINT_RAGGED_CLASSTREE_EDGE,
- pht(
- "This class is neither '%s' nor '%s', and does not have ".
- "a docblock marking it '%s'.",
- 'final',
- 'abstract',
- '@concrete-extensible'));
- }
- }
- }
-
- private function lintClassExtendsPhobject(XHPASTNode $root) {
- $classes = $root->selectDescendantsOfType('n_CLASS_DECLARATION');
-
- foreach ($classes as $class) {
- // TODO: This doesn't quite work for namespaced classes (see T8534).
- $name = $class->getChildOfType(1, 'n_CLASS_NAME');
- $extends = $class->getChildByIndex(2);
-
- if ($name->getConcreteString() == 'Phobject') {
- continue;
- }
-
- if ($extends->getTypeName() == 'n_EMPTY') {
- $this->raiseLintAtNode(
- $class,
- self::LINT_EXTENDS_PHOBJECT,
- pht(
- 'Classes should extend from %s or from some other class. '.
- 'All classes (except for %s itself) should have a base class.',
- 'Phobject',
- 'Phobject'));
- }
- }
- }
-
-}
diff --git a/src/lint/linter/__tests__/ArcanistPhutilXHPASTLinterTestCase.php b/src/lint/linter/__tests__/ArcanistPhutilXHPASTLinterTestCase.php
deleted file mode 100644
--- a/src/lint/linter/__tests__/ArcanistPhutilXHPASTLinterTestCase.php
+++ /dev/null
@@ -1,14 +0,0 @@
-<?php
-
-final class ArcanistPhutilXHPASTLinterTestCase extends ArcanistLinterTestCase {
-
- public function testLinter() {
- $linter = new ArcanistPhutilXHPASTLinter();
- $linter->setDeprecatedFunctions(array(
- 'deprecated_function' => pht('This function is most likely deprecated.'),
- ));
-
- $this->executeTestsInDirectory(dirname(__FILE__).'/phlxhp/', $linter);
- }
-
-}
diff --git a/src/lint/linter/__tests__/phlxhp/array-combine.lint-test b/src/lint/linter/__tests__/xhpast/array-combine.lint-test
rename from src/lint/linter/__tests__/phlxhp/array-combine.lint-test
rename to src/lint/linter/__tests__/xhpast/array-combine.lint-test
--- a/src/lint/linter/__tests__/phlxhp/array-combine.lint-test
+++ b/src/lint/linter/__tests__/xhpast/array-combine.lint-test
@@ -3,4 +3,4 @@
array_combine($x, $x);
array_combine($x, $y);
~~~~~~~~~~
-warning:3:1
+disabled:3:1
diff --git a/src/lint/linter/__tests__/phlxhp/deprecated-function.lint-test b/src/lint/linter/__tests__/xhpast/deprecated-function.lint-test
rename from src/lint/linter/__tests__/phlxhp/deprecated-function.lint-test
rename to src/lint/linter/__tests__/xhpast/deprecated-function.lint-test
diff --git a/src/lint/linter/__tests__/phlxhp/extends-phobject.lint-test b/src/lint/linter/__tests__/xhpast/extends-phobject.lint-test
rename from src/lint/linter/__tests__/phlxhp/extends-phobject.lint-test
rename to src/lint/linter/__tests__/xhpast/extends-phobject.lint-test
--- a/src/lint/linter/__tests__/phlxhp/extends-phobject.lint-test
+++ b/src/lint/linter/__tests__/xhpast/extends-phobject.lint-test
@@ -4,4 +4,4 @@
final class B extends A {}
final class C {}
~~~~~~~~~~
-advice:5:1
+disabled:5:1
diff --git a/src/lint/linter/__tests__/phlxhp/pht.lint-test b/src/lint/linter/__tests__/xhpast/pht.lint-test
rename from src/lint/linter/__tests__/phlxhp/pht.lint-test
rename to src/lint/linter/__tests__/xhpast/pht.lint-test
--- a/src/lint/linter/__tests__/phlxhp/pht.lint-test
+++ b/src/lint/linter/__tests__/xhpast/pht.lint-test
@@ -20,8 +20,8 @@
EOT
);
~~~~~~~~~~
-warning:5:1
-warning:7:1
-warning:8:1
-warning:10:1
-warning:18:1
+disabled:5:1
+disabled:7:1
+disabled:8:1
+disabled:10:1
+disabled:18:1
diff --git a/src/lint/linter/__tests__/phlxhp/ragged-classtree-edges.lint-test b/src/lint/linter/__tests__/xhpast/ragged-classtree-edges.lint-test
rename from src/lint/linter/__tests__/phlxhp/ragged-classtree-edges.lint-test
rename to src/lint/linter/__tests__/xhpast/ragged-classtree-edges.lint-test
--- a/src/lint/linter/__tests__/phlxhp/ragged-classtree-edges.lint-test
+++ b/src/lint/linter/__tests__/xhpast/ragged-classtree-edges.lint-test
@@ -9,4 +9,4 @@
*/
class D extends Phobject {}
~~~~~~~~~~
-warning:3:7
+disabled:3:7
diff --git a/src/lint/linter/__tests__/phlxhp/xsprintf.lint-test b/src/lint/linter/__tests__/xhpast/xsprintf.lint-test
rename from src/lint/linter/__tests__/phlxhp/xsprintf.lint-test
rename to src/lint/linter/__tests__/xhpast/xsprintf.lint-test
--- a/src/lint/linter/__tests__/phlxhp/xsprintf.lint-test
+++ b/src/lint/linter/__tests__/xhpast/xsprintf.lint-test
@@ -9,5 +9,5 @@
qsprintf('x', $y);
~~~~~~~~~~
-warning:4:1
-warning:9:1
+disabled:4:1
+disabled:9:1
diff --git a/src/lint/linter/xhpast/ArcanistPhutilXHPASTLinterRule.php b/src/lint/linter/xhpast/ArcanistPhutilXHPASTLinterRule.php
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/xhpast/ArcanistPhutilXHPASTLinterRule.php
@@ -0,0 +1,10 @@
+<?php
+
+abstract class ArcanistPhutilXHPASTLinterRule
+ extends ArcanistXHPASTLinterRule {
+
+ final public function getLintSeverity() {
+ return ArcanistLintSeverity::SEVERITY_DISABLED;
+ }
+
+}
diff --git a/src/lint/linter/xhpast/rules/ArcanistArrayCombineXHPASTLinterRule.php b/src/lint/linter/xhpast/rules/ArcanistArrayCombineXHPASTLinterRule.php
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/xhpast/rules/ArcanistArrayCombineXHPASTLinterRule.php
@@ -0,0 +1,40 @@
+<?php
+
+final class ArcanistArrayCombineXHPASTLinterRule
+ extends ArcanistPhutilXHPASTLinterRule {
+
+ const ID = 1002;
+
+ public function getLintName() {
+ return pht('%s Unreliable', 'array_combine()');
+ }
+
+ public function process(XHPASTNode $root) {
+ $function_calls = $this->getFunctionCalls($root, array('array_combine'));
+
+ foreach ($function_calls as $call) {
+ $name = $call->getChildByIndex(0)->getConcreteString();
+ $parameter_list = $call->getChildOfType(1, 'n_CALL_PARAMETER_LIST');
+
+ if (count($parameter_list->getChildren()) !== 2) {
+ // Wrong number of parameters, but raise that elsewhere if we want.
+ continue;
+ }
+
+ $first = $parameter_list->getChildByIndex(0);
+ $second = $parameter_list->getChildByIndex(1);
+
+ if ($first->getConcreteString() == $second->getConcreteString()) {
+ $this->raiseLintAtNode(
+ $call,
+ pht(
+ 'Prior to PHP 5.4, `%s` fails when given empty arrays. '.
+ 'Prefer to write `%s` as `%s`.',
+ 'array_combine()',
+ 'array_combine(x, x)',
+ 'array_fuse(x)'));
+ }
+ }
+ }
+
+}
diff --git a/src/lint/linter/xhpast/rules/ArcanistClassExtendsPhobjectXHPASTLinterRule.php b/src/lint/linter/xhpast/rules/ArcanistClassExtendsPhobjectXHPASTLinterRule.php
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/xhpast/rules/ArcanistClassExtendsPhobjectXHPASTLinterRule.php
@@ -0,0 +1,36 @@
+<?php
+
+final class ArcanistClassExtendsPhobjectXHPASTLinterRule
+ extends ArcanistPhutilXHPASTLinterRule {
+
+ const ID = 1006;
+
+ public function getLintName() {
+ return pht('Class Not Extending %s', 'Phobject');
+ }
+
+ public function process(XHPASTNode $root) {
+ $classes = $root->selectDescendantsOfType('n_CLASS_DECLARATION');
+
+ foreach ($classes as $class) {
+ // TODO: This doesn't quite work for namespaced classes (see T8534).
+ $name = $class->getChildOfType(1, 'n_CLASS_NAME');
+ $extends = $class->getChildByIndex(2);
+
+ if ($name->getConcreteString() == 'Phobject') {
+ continue;
+ }
+
+ if ($extends->getTypeName() == 'n_EMPTY') {
+ $this->raiseLintAtNode(
+ $class,
+ pht(
+ 'Classes should extend from %s or from some other class. '.
+ 'All classes (except for %s itself) should have a base class.',
+ 'Phobject',
+ 'Phobject'));
+ }
+ }
+ }
+
+}
diff --git a/src/lint/linter/xhpast/rules/ArcanistDeprecatedFunctionXHPASTLinterRule.php b/src/lint/linter/xhpast/rules/ArcanistDeprecatedFunctionXHPASTLinterRule.php
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/xhpast/rules/ArcanistDeprecatedFunctionXHPASTLinterRule.php
@@ -0,0 +1,57 @@
+<?php
+
+final class ArcanistDeprecatedFunctionXHPASTLinterRule
+ extends ArcanistXHPASTLinterRule {
+
+ const ID = 77;
+
+ private $deprecatedFunctions = array();
+
+ public function getLintName() {
+ return pht('Use of Deprecated Function');
+ }
+
+ public function getLintSeverity() {
+ return ArcanistLintSeverity::SEVERITY_WARNING;
+ }
+
+ public function getLinterConfigurationOptions() {
+ return parent::getLinterConfigurationOptions() + array(
+ 'xhpast.deprecated.function' => array(
+ 'type' => 'optional map<string, string>',
+ 'help' => pht(
+ 'Functions which should should be considered deprecated.'),
+ ),
+ );
+ }
+
+ public function setLinterConfigurationValue($key, $value) {
+ switch ($key) {
+ case 'xhpast.deprecatedFunctions.function':
+ $this->deprecatedFunctions = $value;
+ return;
+
+ default:
+ return parent::getLinterConfigurationOptions();
+ }
+ }
+
+ public function process(XHPASTNode $root) {
+ $map = $this->deprecatedFunctions;
+ $function_calls = $this->getFunctionCalls($root, array_keys($map));
+
+ foreach ($function_calls as $call) {
+ $name = $call
+ ->getChildByIndex(0)
+ ->getConcreteString();
+
+ $name = strtolower($name);
+ if (empty($map[$name])) {
+ continue;
+ }
+
+ $this->raiseLintAtNode($call, $map[$name]);
+ }
+ }
+
+}
diff --git a/src/lint/linter/xhpast/rules/ArcanistRaggedClassTreeEdgeXHPASTLinterRule.php b/src/lint/linter/xhpast/rules/ArcanistRaggedClassTreeEdgeXHPASTLinterRule.php
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/xhpast/rules/ArcanistRaggedClassTreeEdgeXHPASTLinterRule.php
@@ -0,0 +1,50 @@
+<?php
+
+final class ArcanistRaggedClassTreeEdgeXHPASTLinterRule
+ extends ArcanistPhutilXHPASTLinterRule {
+
+ const ID = 1005;
+
+ public function getLintName() {
+ return pht('Class Not %s Or %s', 'abstract', 'final');
+ }
+
+ public function process(XHPASTNode $root) {
+ $parser = new PhutilDocblockParser();
+
+ $classes = $root->selectDescendantsOfType('n_CLASS_DECLARATION');
+ foreach ($classes as $class) {
+ $is_final = false;
+ $is_abstract = false;
+ $is_concrete_extensible = false;
+
+ $attributes = $class->getChildOfType(0, 'n_CLASS_ATTRIBUTES');
+ foreach ($attributes->getChildren() as $child) {
+ if ($child->getConcreteString() == 'final') {
+ $is_final = true;
+ }
+ if ($child->getConcreteString() == 'abstract') {
+ $is_abstract = true;
+ }
+ }
+
+ $docblock = $class->getDocblockToken();
+ if ($docblock) {
+ list($text, $specials) = $parser->parse($docblock->getValue());
+ $is_concrete_extensible = idx($specials, 'concrete-extensible');
+ }
+
+ if (!$is_final && !$is_abstract && !$is_concrete_extensible) {
+ $this->raiseLintAtNode(
+ $class->getChildOfType(1, 'n_CLASS_NAME'),
+ pht(
+ "This class is neither '%s' nor '%s', and does not have ".
+ "a docblock marking it '%s'.",
+ 'final',
+ 'abstract',
+ '@concrete-extensible'));
+ }
+ }
+ }
+
+}
diff --git a/src/lint/linter/xhpast/rules/ArcanistUnsafeDynamicStringXHPASTLinterRule.php b/src/lint/linter/xhpast/rules/ArcanistUnsafeDynamicStringXHPASTLinterRule.php
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/xhpast/rules/ArcanistUnsafeDynamicStringXHPASTLinterRule.php
@@ -0,0 +1,117 @@
+<?php
+
+final class ArcanistUnsafeDynamicStringXHPASTLinterRule
+ extends ArcanistPhutilXHPASTLinterRule {
+
+ const ID = 1004;
+
+ private $dynamicStringFunctions = array();
+ private $dynamicStringClasses = array();
+
+ public function getLintName() {
+ return pht('Unsafe Usage of Dynamic String');
+ }
+
+ public function getLinterConfigurationOptions() {
+ $options = array(
+ 'xhpast.dynamic-string.functions' => array(
+ 'type' => 'optional map<string, string>',
+ 'help' => pht(
+ 'Functions which should should not be used because they represent '.
+ 'the unsafe usage of dynamic strings.'),
+ ),
+ 'xhpast.dynamic-string.classes' => array(
+ 'type' => 'optional map<string, string>',
+ 'help' => pht(
+ 'Classes which should should not be used because they represent the '.
+ 'unsafe usage of dynamic strings.'),
+ ),
+ );
+
+ return $options + parent::getLinterConfigurationOptions();
+ }
+
+ public function setLinterConfigurationValue($key, $value) {
+ switch ($key) {
+ case 'phutil-xhpast.dynamic-string.functions':
+ $this->dynamicStringFunctions = $value;
+ return;
+
+ case 'phutil-xhpast.dynamic-string.classes':
+ $this->dynamicStringClasses = $value;
+ return;
+
+ default:
+ parent::setLinterConfigurationValue($key, $value);
+ return;
+ }
+ }
+
+ public function process(XHPASTNode $root) {
+ $safe = $this->dynamicStringFunctions + array(
+ 'pht' => 0,
+
+ 'hsprintf' => 0,
+ 'jsprintf' => 0,
+
+ 'hgsprintf' => 0,
+
+ 'csprintf' => 0,
+ 'vcsprintf' => 0,
+ 'execx' => 0,
+ 'exec_manual' => 0,
+ 'phutil_passthru' => 0,
+
+ 'qsprintf' => 1,
+ 'vqsprintf' => 1,
+ 'queryfx' => 1,
+ 'queryfx_all' => 1,
+ 'queryfx_one' => 1,
+ );
+
+ $calls = $root->selectDescendantsOfType('n_FUNCTION_CALL');
+ $this->lintUnsafeDynamicStringCall($calls, $safe);
+
+ $safe = $this->dynamicStringClasses + array(
+ 'ExecFuture' => 0,
+ );
+
+ $news = $root->selectDescendantsOfType('n_NEW');
+ $this->lintUnsafeDynamicStringCall($news, $safe);
+ }
+
+ private function lintUnsafeDynamicStringCall(
+ AASTNodeList $calls,
+ array $safe) {
+
+ $safe = array_combine(
+ array_map('strtolower', array_keys($safe)),
+ $safe);
+
+ foreach ($calls as $call) {
+ $name = $call->getChildByIndex(0)->getConcreteString();
+ $param = idx($safe, strtolower($name));
+
+ if ($param === null) {
+ continue;
+ }
+
+ $parameters = $call->getChildByIndex(1);
+ if (count($parameters->getChildren()) <= $param) {
+ continue;
+ }
+
+ $identifier = $parameters->getChildByIndex($param);
+ if (!$identifier->isConstantString()) {
+ $this->raiseLintAtNode(
+ $call,
+ pht(
+ "Parameter %d of %s should be a scalar string, ".
+ "otherwise it's not safe.",
+ $param + 1,
+ $name.'()'));
+ }
+ }
+ }
+
+}

File Metadata

Mime Type
text/plain
Expires
Sat, Mar 29, 7:53 PM (1 w, 1 d ago)
Storage Engine
amazon-s3
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
phabricator/secure/n3/5f/kviazh6gsegg4quw
Default Alt Text
D13867.id33493.diff (31 KB)

Event Timeline