1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-12-23 05:50:54 +01:00

Allow classes/functions from a later version if they are used conditionally

Summary:
Fixes T5299. Currently, linting `src/future/http/HTTPSFuture.php` from rPHU will raise the following error:

```
   Error  (XHP31) Use Of PHP 5.3 Features
    This codebase targets PHP 5.2.3, but `curlfile` was not introduced until
    PHP 5.5.0.

             532       // use this "@" stuff.
             533
             534       if (class_exists('CURLFile')) {
    >>>      535         $file_value = new CURLFile((string)$tmp, $info['mime'], $info['name']);
             536       } else {
             537         $file_value = '@'.(string)$tmp;
             538       }
```

However, since this class is being used conditionally, it //should// be fine and no linter errors should be being raised.

Test Plan: Added a test case.

Reviewers: #blessed_reviewers, epriestley

Reviewed By: #blessed_reviewers, epriestley

Subscribers: epriestley, Korvin

Maniphest Tasks: T5299

Differential Revision: https://secure.phabricator.com/D10132
This commit is contained in:
Joshua Spence 2014-09-11 00:19:38 +10:00
parent f0f6897fce
commit 3f79ae258f
2 changed files with 107 additions and 0 deletions

View file

@ -369,6 +369,67 @@ final class ArcanistXHPASTLinter extends ArcanistBaseXHPASTLinter {
'/../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 @@ final class ArcanistXHPASTLinter extends ArcanistBaseXHPASTLinter {
$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 @@ final class ArcanistXHPASTLinter extends ArcanistBaseXHPASTLinter {
$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,

View file

@ -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"}}