Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15345574
D13867.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
41 KB
Referenced Files
None
Subscribers
None
D13867.diff
View Options
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
Details
Attached
Mime Type
text/plain
Expires
Tue, Mar 11, 11:28 AM (20 h, 10 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7226144
Default Alt Text
D13867.diff (41 KB)
Attached To
Mode
D13867: Fold `ArcanistPhutilXHPASTLinter` into `ArcanistXHPASTLinter`
Attached
Detach File
Event Timeline
Log In to Comment