Page MenuHomePhabricator

D10132.diff
No OneTemporary

D10132.diff

diff --git a/src/lint/linter/ArcanistXHPASTLinter.php b/src/lint/linter/ArcanistXHPASTLinter.php
--- a/src/lint/linter/ArcanistXHPASTLinter.php
+++ b/src/lint/linter/ArcanistXHPASTLinter.php
@@ -369,6 +369,67 @@
'/../resources/php_compat_info.json';
$compat_info = phutil_json_decode(Filesystem::readFile($target));
+ // Create a whitelist for symbols which are being used conditionally.
+ $whitelist = array(
+ 'class' => array(),
+ 'function' => array(),
+ );
+
+ $conditionals = $root->selectDescendantsOfType('n_IF');
+ foreach ($conditionals as $conditional) {
+ $condition = $conditional->getChildOfType(0, 'n_CONTROL_CONDITION');
+ $function = $condition->getChildByIndex(0);
+
+ if ($function->getTypeName() != 'n_FUNCTION_CALL') {
+ continue;
+ }
+
+ $function_name = $function
+ ->getChildOfType(0, 'n_SYMBOL_NAME')
+ ->getConcreteString();
+
+ switch ($function_name) {
+ case 'class_exists':
+ case 'function_exists':
+ case 'interface_exists':
+ $type = null;
+ switch ($function_name) {
+ case 'class_exists':
+ $type = 'class';
+ break;
+
+ case 'function_exists':
+ $type = 'function';
+ break;
+
+ case 'interface_exists':
+ $type = 'interface';
+ break;
+ }
+
+ $params = $function->getChildOfType(1, 'n_CALL_PARAMETER_LIST');
+ $symbol = $params->getChildByIndex(0);
+
+ if (!$symbol->isStaticScalar()) {
+ continue;
+ }
+
+ $symbol_name = $symbol->evalStatic();
+ if (!idx($whitelist[$type], $symbol_name)) {
+ $whitelist[$type][$symbol_name] = array();
+ }
+
+ $span = $conditional
+ ->getChildOfType(1, 'n_STATEMENT_LIST')
+ ->getTokens();
+
+ $whitelist[$type][$symbol_name][] = range(
+ head_key($span),
+ last_key($span));
+ break;
+ }
+ }
+
$calls = $root->selectDescendantsOfType('n_FUNCTION_CALL');
foreach ($calls as $call) {
$node = $call->getChildByIndex(0);
@@ -376,6 +437,19 @@
$version = idx($compat_info['functions'], $name);
if ($version && version_compare($version['min'], $this->version, '>')) {
+ // Check if whitelisted.
+ $whitelisted = false;
+ foreach (idx($whitelist['function'], $name, array()) as $range) {
+ if (array_intersect($range, array_keys($node->getTokens()))) {
+ $whitelisted = true;
+ break;
+ }
+ }
+
+ if ($whitelisted) {
+ continue;
+ }
+
$this->raiseLintAtNode(
$node,
self::LINT_PHP_COMPATIBILITY,
@@ -422,6 +496,19 @@
$version = idx($compat_info['interfaces'], $name);
$version = idx($compat_info['classes'], $name, $version);
if ($version && version_compare($version['min'], $this->version, '>')) {
+ // Check if whitelisted.
+ $whitelisted = false;
+ foreach (idx($whitelist['class'], $name, array()) as $range) {
+ if (array_intersect($range, array_keys($node->getTokens()))) {
+ $whitelisted = true;
+ break;
+ }
+ }
+
+ if ($whitelisted) {
+ continue;
+ }
+
$this->raiseLintAtNode(
$node,
self::LINT_PHP_COMPATIBILITY,
diff --git a/src/lint/linter/__tests__/xhpast/conditional-usage.lint-test b/src/lint/linter/__tests__/xhpast/conditional-usage.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/xhpast/conditional-usage.lint-test
@@ -0,0 +1,20 @@
+<?php
+
+if (function_exists('array_column')) {
+ array_column(array(), '');
+ json_last_error_msg();
+} else {
+ array_column(array(), '');
+}
+
+if (class_exists('CURLFile')) {
+ new CURLFile('');
+ new DateTimeImmutable();
+}
+~~~~~~~~~~
+error:5:3
+error:7:3
+error:12:7
+~~~~~~~~~~
+~~~~~~~~~~
+{"config": {"xhpast.php-version": "5.3.0"}}

File Metadata

Mime Type
text/plain
Expires
Sun, Mar 9, 2:29 PM (3 w, 2 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7389230
Default Alt Text
D10132.diff (4 KB)

Event Timeline