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:
parent
f0f6897fce
commit
3f79ae258f
2 changed files with 107 additions and 0 deletions
|
@ -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,
|
||||
|
|
20
src/lint/linter/__tests__/xhpast/conditional-usage.lint-test
Normal file
20
src/lint/linter/__tests__/xhpast/conditional-usage.lint-test
Normal 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"}}
|
Loading…
Reference in a new issue