Page MenuHomePhabricator

D13867.id34993.diff
No OneTemporary

D13867.id34993.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$)"
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',
+ 'ArcanistClassExtendsObjectXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistClassExtendsObjectXHPASTLinterRule.php',
'ArcanistClassFilenameMismatchXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistClassFilenameMismatchXHPASTLinterRule.php',
'ArcanistClassNameLiteralXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistClassNameLiteralXHPASTLinterRule.php',
'ArcanistCloseRevisionWorkflow' => 'workflow/ArcanistCloseRevisionWorkflow.php',
@@ -71,6 +73,7 @@
'ArcanistCpplintLinterTestCase' => 'lint/linter/__tests__/ArcanistCpplintLinterTestCase.php',
'ArcanistDeclarationParenthesesXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistDeclarationParenthesesXHPASTLinterRule.php',
'ArcanistDefaultParametersXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistDefaultParametersXHPASTLinterRule.php',
+ 'ArcanistDeprecationXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistDeprecationXHPASTLinterRule.php',
'ArcanistDiffChange' => 'parser/diff/ArcanistDiffChange.php',
'ArcanistDiffChangeType' => 'parser/diff/ArcanistDiffChangeType.php',
'ArcanistDiffHunk' => 'parser/diff/ArcanistDiffHunk.php',
@@ -203,9 +206,7 @@
'ArcanistPhpunitTestResultParser' => 'unit/parser/ArcanistPhpunitTestResultParser.php',
'ArcanistPhrequentWorkflow' => 'workflow/ArcanistPhrequentWorkflow.php',
'ArcanistPhutilLibraryLinter' => 'lint/linter/ArcanistPhutilLibraryLinter.php',
- 'ArcanistPhutilXHPASTLinter' => 'lint/linter/ArcanistPhutilXHPASTLinter.php',
'ArcanistPhutilXHPASTLinterStandard' => 'lint/linter/standards/phutil/ArcanistPhutilXHPASTLinterStandard.php',
- 'ArcanistPhutilXHPASTLinterTestCase' => 'lint/linter/__tests__/ArcanistPhutilXHPASTLinterTestCase.php',
'ArcanistPlusOperatorOnStringsXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistPlusOperatorOnStringsXHPASTLinterRule.php',
'ArcanistPregQuoteMisuseXHPASTLinterRule' => 'lint/linter/xhpast/rules/ArcanistPregQuoteMisuseXHPASTLinterRule.php',
'ArcanistPuppetLintLinter' => 'lint/linter/ArcanistPuppetLintLinter.php',
@@ -214,6 +215,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',
@@ -265,6 +267,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',
@@ -305,6 +308,7 @@
'ArcanistAliasWorkflow' => 'ArcanistWorkflow',
'ArcanistAmendWorkflow' => 'ArcanistWorkflow',
'ArcanistAnoidWorkflow' => 'ArcanistWorkflow',
+ 'ArcanistArrayCombineXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistArrayIndexSpacingXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistArraySeparatorXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistArrayValueXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
@@ -332,6 +336,7 @@
'ArcanistCheckstyleXMLLintRenderer' => 'ArcanistLintRenderer',
'ArcanistChmodLinter' => 'ArcanistLinter',
'ArcanistChmodLinterTestCase' => 'ArcanistLinterTestCase',
+ 'ArcanistClassExtendsObjectXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistClassFilenameMismatchXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistClassNameLiteralXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistCloseRevisionWorkflow' => 'ArcanistWorkflow',
@@ -363,6 +368,7 @@
'ArcanistCpplintLinterTestCase' => 'ArcanistExternalLinterTestCase',
'ArcanistDeclarationParenthesesXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistDefaultParametersXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
+ 'ArcanistDeprecationXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistDiffChange' => 'Phobject',
'ArcanistDiffChangeType' => 'Phobject',
'ArcanistDiffHunk' => 'Phobject',
@@ -495,9 +501,7 @@
'ArcanistPhpunitTestResultParser' => 'ArcanistTestResultParser',
'ArcanistPhrequentWorkflow' => 'ArcanistWorkflow',
'ArcanistPhutilLibraryLinter' => 'ArcanistLinter',
- 'ArcanistPhutilXHPASTLinter' => 'ArcanistBaseXHPASTLinter',
'ArcanistPhutilXHPASTLinterStandard' => 'ArcanistLinterStandard',
- 'ArcanistPhutilXHPASTLinterTestCase' => 'ArcanistLinterTestCase',
'ArcanistPlusOperatorOnStringsXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistPregQuoteMisuseXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistPuppetLintLinter' => 'ArcanistExternalLinter',
@@ -506,6 +510,7 @@
'ArcanistPyFlakesLinterTestCase' => 'ArcanistExternalLinterTestCase',
'ArcanistPyLintLinter' => 'ArcanistExternalLinter',
'ArcanistPyLintLinterTestCase' => 'ArcanistExternalLinterTestCase',
+ 'ArcanistRaggedClassTreeEdgeXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistRepositoryAPI' => 'Phobject',
'ArcanistRepositoryAPIMiscTestCase' => 'PhutilTestCase',
'ArcanistRepositoryAPIStateTestCase' => 'PhutilTestCase',
@@ -557,6 +562,7 @@
'ArcanistUnitWorkflow' => 'ArcanistWorkflow',
'ArcanistUnnecessaryFinalModifierXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'ArcanistUnnecessarySemicolonXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
+ 'ArcanistUnsafeDynamicStringXHPASTLinterRule' => 'ArcanistXHPASTLinterRule',
'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/deprecated-function.lint-test b/src/lint/linter/__tests__/phlxhp/deprecated-function.lint-test
deleted file mode 100644
--- a/src/lint/linter/__tests__/phlxhp/deprecated-function.lint-test
+++ /dev/null
@@ -1,6 +0,0 @@
-<?php
-
-deprecated_function();
-modern_function();
-~~~~~~~~~~
-warning:3:1
diff --git a/src/lint/linter/__tests__/phlxhp/pht.lint-test b/src/lint/linter/__tests__/phlxhp/pht.lint-test
deleted file mode 100644
--- a/src/lint/linter/__tests__/phlxhp/pht.lint-test
+++ /dev/null
@@ -1,27 +0,0 @@
-<?php
-pht('a');
-pht("a");
-pht('a'.'b');
-pht(f());
-pht();
-pht($a);
-pht('a'.$a);
-pht('$a');
-pht("$a");
-pht('%s', $a);
-
-pht(<<<EOT
-a
-EOT
-);
-
-pht(<<<EOT
-$a
-EOT
-);
-~~~~~~~~~~
-warning:5:1
-warning:7:1
-warning:8:1
-warning:10:1
-warning:18:1
diff --git a/src/lint/linter/__tests__/phlxhp/xsprintf.lint-test b/src/lint/linter/__tests__/phlxhp/xsprintf.lint-test
deleted file mode 100644
--- a/src/lint/linter/__tests__/phlxhp/xsprintf.lint-test
+++ /dev/null
@@ -1,13 +0,0 @@
-<?php
-
-csprintf('x');
-csprintf($x);
-
-qsprintf();
-qsprintf('x');
-qsprintf('x', 'y');
-qsprintf('x', $y);
-
-~~~~~~~~~~
-warning:4:1
-warning:9:1
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__/xhpast/call-time-pass-by-reference.lint-test b/src/lint/linter/__tests__/xhpast/call-time-pass-by-reference.lint-test
--- a/src/lint/linter/__tests__/xhpast/call-time-pass-by-reference.lint-test
+++ b/src/lint/linter/__tests__/xhpast/call-time-pass-by-reference.lint-test
@@ -25,7 +25,9 @@
array_walk(array(), function () use (&$x) {});
MyClass::myfunc(array(&$x, &$y));
~~~~~~~~~~
+disabled:3:1
error:3:7 XHP19
+disabled:3:7
error:10:8
error:13:15
error:16:17
diff --git a/src/lint/linter/__tests__/xhpast/class-name-literal.lint-test b/src/lint/linter/__tests__/xhpast/class-name-literal.lint-test
--- a/src/lint/linter/__tests__/xhpast/class-name-literal.lint-test
+++ b/src/lint/linter/__tests__/xhpast/class-name-literal.lint-test
@@ -12,6 +12,8 @@
}
}
~~~~~~~~~~
+disabled:3:1
+disabled:3:7
error:3:7
advice:5:12
advice:9:10
diff --git a/src/lint/linter/__tests__/xhpast/constant-case.lint-test b/src/lint/linter/__tests__/xhpast/constant-case.lint-test
--- a/src/lint/linter/__tests__/xhpast/constant-case.lint-test
+++ b/src/lint/linter/__tests__/xhpast/constant-case.lint-test
@@ -9,5 +9,7 @@
~~~~~~~~~~
warning:3:8
warning:4:7
+disabled:6:1
error:6:7
+disabled:6:7
warning:7:9
diff --git a/src/lint/linter/__tests__/xhpast/decl-parens-hug-closing.lint-test b/src/lint/linter/__tests__/xhpast/decl-parens-hug-closing.lint-test
--- a/src/lint/linter/__tests__/xhpast/decl-parens-hug-closing.lint-test
+++ b/src/lint/linter/__tests__/xhpast/decl-parens-hug-closing.lint-test
@@ -28,6 +28,7 @@
warning:4:14
warning:5:11
warning:8:15
+disabled:10:1
error:10:13
warning:13:23
warning:16:31
diff --git a/src/lint/linter/__tests__/xhpast/default-parameters.lint-test b/src/lint/linter/__tests__/xhpast/default-parameters.lint-test
--- a/src/lint/linter/__tests__/xhpast/default-parameters.lint-test
+++ b/src/lint/linter/__tests__/xhpast/default-parameters.lint-test
@@ -9,5 +9,7 @@
}
~~~~~~~~~~
warning:4:13
+disabled:7:1
error:7:7
+disabled:7:7
warning:8:27
diff --git a/src/lint/linter/__tests__/xhpast/deprecated-function.lint-test b/src/lint/linter/__tests__/xhpast/deprecated-function.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/xhpast/deprecated-function.lint-test
@@ -0,0 +1,15 @@
+<?php
+
+deprecated_function();
+modern_function();
+~~~~~~~~~~
+warning:3:1
+~~~~~~~~~~
+~~~~~~~~~~
+{
+ "config": {
+ "xhpast.deprecated.functions": {
+ "deprecated_function": "This function is deprecated."
+ }
+ }
+}
diff --git a/src/lint/linter/__tests__/xhpast/empty-statement.lint-test b/src/lint/linter/__tests__/xhpast/empty-statement.lint-test
--- a/src/lint/linter/__tests__/xhpast/empty-statement.lint-test
+++ b/src/lint/linter/__tests__/xhpast/empty-statement.lint-test
@@ -3,6 +3,7 @@
final class Foo {};
$x = null;;
~~~~~~~~~~
+disabled:3:1
error:3:13 XHP19
advice:3:19
advice:4:11
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__/xhpast/implicit-visibility.lint-test b/src/lint/linter/__tests__/xhpast/implicit-visibility.lint-test
--- a/src/lint/linter/__tests__/xhpast/implicit-visibility.lint-test
+++ b/src/lint/linter/__tests__/xhpast/implicit-visibility.lint-test
@@ -11,6 +11,7 @@
private $z;
}
~~~~~~~~~~
+disabled:3:1
error:3:13 XHP19
advice:5:3
advice:6:3
diff --git a/src/lint/linter/__tests__/xhpast/invalid-modifiers.lint-test b/src/lint/linter/__tests__/xhpast/invalid-modifiers.lint-test
--- a/src/lint/linter/__tests__/xhpast/invalid-modifiers.lint-test
+++ b/src/lint/linter/__tests__/xhpast/invalid-modifiers.lint-test
@@ -11,6 +11,8 @@
abstract final public function baz() {}
}
~~~~~~~~~~
+disabled:3:1
+disabled:3:7
error:3:7 XHP19
error:5:10
error:6:10
diff --git a/src/lint/linter/__tests__/xhpast/modifier-ordering.lint-test b/src/lint/linter/__tests__/xhpast/modifier-ordering.lint-test
--- a/src/lint/linter/__tests__/xhpast/modifier-ordering.lint-test
+++ b/src/lint/linter/__tests__/xhpast/modifier-ordering.lint-test
@@ -9,6 +9,8 @@
static final public function foobar() {}
}
~~~~~~~~~~
+disabled:3:1
+disabled:3:7
error:3:7 XHP19
advice:5:3
advice:7:3
diff --git a/src/lint/linter/__tests__/xhpast/naming-conventions.lint-test b/src/lint/linter/__tests__/xhpast/naming-conventions.lint-test
--- a/src/lint/linter/__tests__/xhpast/naming-conventions.lint-test
+++ b/src/lint/linter/__tests__/xhpast/naming-conventions.lint-test
@@ -50,6 +50,7 @@
$mIxEdCaSe = 1;
}
~~~~~~~~~~
+disabled:3:1
warning:3:13
warning:4:9
warning:4:9
@@ -63,6 +64,7 @@
warning:9:11
warning:11:10
warning:11:13
+disabled:13:1
warning:21:13
warning:24:3
warning:25:3
diff --git a/src/lint/linter/__tests__/xhpast/no-parent-scope.lint-test b/src/lint/linter/__tests__/xhpast/no-parent-scope.lint-test
--- a/src/lint/linter/__tests__/xhpast/no-parent-scope.lint-test
+++ b/src/lint/linter/__tests__/xhpast/no-parent-scope.lint-test
@@ -16,4 +16,5 @@
}
}
~~~~~~~~~~
+disabled:9:1
error:11:5
diff --git a/src/lint/linter/__tests__/xhpast/no-segfault-on-abstract.lint-test b/src/lint/linter/__tests__/xhpast/no-segfault-on-abstract.lint-test
--- a/src/lint/linter/__tests__/xhpast/no-segfault-on-abstract.lint-test
+++ b/src/lint/linter/__tests__/xhpast/no-segfault-on-abstract.lint-test
@@ -3,3 +3,5 @@
abstract class A {}
final class F {}
~~~~~~~~~~
+disabled:3:1
+disabled:4:1
diff --git a/src/lint/linter/__tests__/xhpast/parens-hug-contents.lint-test b/src/lint/linter/__tests__/xhpast/parens-hug-contents.lint-test
--- a/src/lint/linter/__tests__/xhpast/parens-hug-contents.lint-test
+++ b/src/lint/linter/__tests__/xhpast/parens-hug-contents.lint-test
@@ -33,6 +33,7 @@
warning:14:19
warning:15:12
warning:15:15
+disabled:16:1
error:16:13 XHP19 Class-Filename Mismatch
warning:17:21
warning:17:24
diff --git a/src/lint/linter/__tests__/xhpast/pht.lint-test b/src/lint/linter/__tests__/xhpast/pht.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/xhpast/pht.lint-test
@@ -0,0 +1,39 @@
+<?php
+
+pht('a');
+pht("a");
+pht('a'.'b');
+pht(f());
+pht();
+pht($a);
+pht('a'.$a);
+pht('$a');
+pht("$a");
+pht('%s', $a);
+
+pht(<<<EOT
+a
+EOT
+);
+
+pht(<<<EOT
+$a
+EOT
+);
+~~~~~~~~~~
+advice:4:5
+error:6:1
+error:7:1
+error:8:1
+error:9:1
+error:11:1
+error:19:1
+~~~~~~~~~~
+~~~~~~~~~~
+{
+ "config": {
+ "xhpast.dynamic-string.functions": {
+ "pht": 0
+ }
+ }
+}
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__/xhpast/self-member-references.lint-test b/src/lint/linter/__tests__/xhpast/self-member-references.lint-test
--- a/src/lint/linter/__tests__/xhpast/self-member-references.lint-test
+++ b/src/lint/linter/__tests__/xhpast/self-member-references.lint-test
@@ -25,6 +25,7 @@
SomeReallyLongClassName
::someMethod();
~~~~~~~~~~
+disabled:3:7
error:3:7
advice:7:5
advice:12:10
diff --git a/src/lint/linter/__tests__/xhpast/surprising-constructors.lint-test b/src/lint/linter/__tests__/xhpast/surprising-constructors.lint-test
--- a/src/lint/linter/__tests__/xhpast/surprising-constructors.lint-test
+++ b/src/lint/linter/__tests__/xhpast/surprising-constructors.lint-test
@@ -6,5 +6,6 @@
}
}
~~~~~~~~~~
+disabled:3:1
error:3:13 XHP19 Class-Filename Mismatch
error:4:19
diff --git a/src/lint/linter/__tests__/xhpast/switches.lint-test b/src/lint/linter/__tests__/xhpast/switches.lint-test
--- a/src/lint/linter/__tests__/xhpast/switches.lint-test
+++ b/src/lint/linter/__tests__/xhpast/switches.lint-test
@@ -89,6 +89,7 @@
warning:53:3
warning:57:3
warning:66:3
+disabled:69:5
warning:71:3
warning:75:3
~~~~~~~~~~
diff --git a/src/lint/linter/__tests__/xhpast/tostring-exception.lint-test b/src/lint/linter/__tests__/xhpast/tostring-exception.lint-test
--- a/src/lint/linter/__tests__/xhpast/tostring-exception.lint-test
+++ b/src/lint/linter/__tests__/xhpast/tostring-exception.lint-test
@@ -24,4 +24,9 @@
abstract public function __toString();
}
~~~~~~~~~~
+disabled:3:1
+disabled:3:7
error:6:7
+disabled:13:1
+disabled:13:7
+disabled:23:1
diff --git a/src/lint/linter/__tests__/xhpast/undeclared-variables.lint-test b/src/lint/linter/__tests__/xhpast/undeclared-variables.lint-test
--- a/src/lint/linter/__tests__/xhpast/undeclared-variables.lint-test
+++ b/src/lint/linter/__tests__/xhpast/undeclared-variables.lint-test
@@ -190,6 +190,7 @@
error:28:3
error:30:3
error:36:3
+disabled:40:1
error:42:5
error:43:7
error:44:5
@@ -202,7 +203,9 @@
error:104:15 Variables in instance derefs should be checked, static should not.
error:118:3 isset() and empty() should not trigger errors.
error:122:3 Should only warn once in this function.
+disabled:136:1
error:144:8
+disabled:147:1
error:150:9
error:164:9
error:171:5
diff --git a/src/lint/linter/__tests__/xhpast/unnecessary-final-modifier.lint-test b/src/lint/linter/__tests__/xhpast/unnecessary-final-modifier.lint-test
--- a/src/lint/linter/__tests__/xhpast/unnecessary-final-modifier.lint-test
+++ b/src/lint/linter/__tests__/xhpast/unnecessary-final-modifier.lint-test
@@ -5,5 +5,6 @@
final public function baz() {}
}
~~~~~~~~~~
+disabled:3:1
error:3:13 XHP19
advice:5:3
diff --git a/src/lint/linter/__tests__/xhpast/use-of-this-in-static-method.lint-test b/src/lint/linter/__tests__/xhpast/use-of-this-in-static-method.lint-test
--- a/src/lint/linter/__tests__/xhpast/use-of-this-in-static-method.lint-test
+++ b/src/lint/linter/__tests__/xhpast/use-of-this-in-static-method.lint-test
@@ -9,5 +9,6 @@
}
}
~~~~~~~~~~
+disabled:3:1
error:3:13 XHP19 Class-Filename Mismatch
error:8:5 Use of $this in a static method.
diff --git a/src/lint/linter/__tests__/xhpast/xsprintf.lint-test b/src/lint/linter/__tests__/xhpast/xsprintf.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/xhpast/xsprintf.lint-test
@@ -0,0 +1,15 @@
+<?php
+
+csprintf('x');
+csprintf($x);
+~~~~~~~~~~
+error:4:1
+~~~~~~~~~~
+~~~~~~~~~~
+{
+ "config": {
+ "xhpast.dynamic-string.functions": {
+ "csprintf": 0
+ }
+ }
+}
diff --git a/src/lint/linter/standards/phutil/ArcanistPhutilXHPASTLinterStandard.php b/src/lint/linter/standards/phutil/ArcanistPhutilXHPASTLinterStandard.php
--- a/src/lint/linter/standards/phutil/ArcanistPhutilXHPASTLinterStandard.php
+++ b/src/lint/linter/standards/phutil/ArcanistPhutilXHPASTLinterStandard.php
@@ -26,8 +26,31 @@
'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',
+ 'xhpast.dynamic-string.classes' => array(
+ 'ExecFuture' => 0,
+ ),
+ 'xhpast.dynamic-string.functions' => 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,
),
);
}
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,44 @@
+<?php
+
+final class ArcanistArrayCombineXHPASTLinterRule
+ extends ArcanistXHPASTLinterRule {
+
+ const ID = 84;
+
+ public function getLintName() {
+ return pht('%s Unreliable', 'array_combine()');
+ }
+
+ public function getLintSeverity() {
+ return ArcanistLintSeverity::SEVERITY_DISABLED;
+ }
+
+ 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/ArcanistClassExtendsObjectXHPASTLinterRule.php b/src/lint/linter/xhpast/rules/ArcanistClassExtendsObjectXHPASTLinterRule.php
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/xhpast/rules/ArcanistClassExtendsObjectXHPASTLinterRule.php
@@ -0,0 +1,40 @@
+<?php
+
+final class ArcanistClassExtendsObjectXHPASTLinterRule
+ extends ArcanistXHPASTLinterRule {
+
+ const ID = 88;
+
+ public function getLintName() {
+ return pht('Class Not Extending %s', 'Phobject');
+ }
+
+ public function getLintSeverity() {
+ return ArcanistLintSeverity::SEVERITY_DISABLED;
+ }
+
+ 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/ArcanistDeprecationXHPASTLinterRule.php b/src/lint/linter/xhpast/rules/ArcanistDeprecationXHPASTLinterRule.php
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/xhpast/rules/ArcanistDeprecationXHPASTLinterRule.php
@@ -0,0 +1,57 @@
+<?php
+
+final class ArcanistDeprecationXHPASTLinterRule
+ extends ArcanistXHPASTLinterRule {
+
+ const ID = 85;
+
+ 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.functions' => array(
+ 'type' => 'optional map<string, string>',
+ 'help' => pht(
+ 'Functions which should should be considered deprecated.'),
+ ),
+ );
+ }
+
+ public function setLinterConfigurationValue($key, $value) {
+ switch ($key) {
+ case 'xhpast.deprecated.functions':
+ $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,54 @@
+<?php
+
+final class ArcanistRaggedClassTreeEdgeXHPASTLinterRule
+ extends ArcanistXHPASTLinterRule {
+
+ const ID = 87;
+
+ public function getLintName() {
+ return pht('Class Not %s Or %s', 'abstract', 'final');
+ }
+
+ public function getLintSeverity() {
+ return ArcanistLintSeverity::SEVERITY_DISABLED;
+ }
+
+ 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,103 @@
+<?php
+
+final class ArcanistUnsafeDynamicStringXHPASTLinterRule
+ extends ArcanistXHPASTLinterRule {
+
+ const ID = 86;
+
+ private $dynamicStringFunctions = array();
+ private $dynamicStringClasses = array();
+
+ public function getLintName() {
+ return pht('Unsafe Usage of Dynamic String');
+ }
+
+ public function getLintSeverity() {
+ return ArcanistLintSeverity::SEVERITY_ERROR;
+ }
+
+ public function getLinterConfigurationOptions() {
+ $options = array(
+ '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.'),
+ ),
+ '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.'),
+ ),
+ );
+
+ return $options + parent::getLinterConfigurationOptions();
+ }
+
+ public function setLinterConfigurationValue($key, $value) {
+ switch ($key) {
+ case 'xhpast.dynamic-string.classes':
+ $this->dynamicStringClasses = $value;
+ return;
+
+ case 'xhpast.dynamic-string.functions':
+ $this->dynamicStringFunctions = $value;
+ return;
+
+ default:
+ parent::setLinterConfigurationValue($key, $value);
+ return;
+ }
+ }
+
+ public function process(XHPASTNode $root) {
+ $this->lintUnsafeDynamicStringClasses($root);
+ $this->lintUnsafeDynamicStringFunctions($root);
+ }
+
+ private function lintUnsafeDynamicStringClasses(XHPASTNode $root) {
+ $news = $root->selectDescendantsOfType('n_NEW');
+ $this->lintUnsafeDynamicStringCall($news, $this->dynamicStringClasses);
+ }
+
+ private function lintUnsafeDynamicStringFunctions(XHPASTNode $root) {
+ $calls = $root->selectDescendantsOfType('n_FUNCTION_CALL');
+ $this->lintUnsafeDynamicStringCall($calls, $this->dynamicStringFunctions);
+ }
+
+ 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
Wed, Mar 5, 5:35 PM (4 d, 21 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7226144
Default Alt Text
D13867.id34993.diff (41 KB)

Event Timeline