mirror of
https://we.phorge.it/source/arcanist.git
synced 2025-03-31 06:28:12 +02:00
Summary: Ref T13577. This lint rule correctly detects the error in `pht('x %s y')` but the narrow test for `n_STRING_SCALAR` prevents it from detecting the error in `pht('x %s y'.'z')`. Make the test broader. Test Plan: - Ran `arc lint` on `HTTPSFuture.php`, got a detection of the issue in T13577. - Added a failing test and made it pass. Maniphest Tasks: T13577 Differential Revision: https://secure.phabricator.com/D21453
108 lines
2.8 KiB
PHP
108 lines
2.8 KiB
PHP
<?php
|
|
|
|
final class ArcanistFormattedStringXHPASTLinterRule
|
|
extends ArcanistXHPASTLinterRule {
|
|
|
|
const ID = 54;
|
|
|
|
private $printfFunctions = array();
|
|
|
|
public function getLintName() {
|
|
return pht('Formatted String');
|
|
}
|
|
|
|
public function getLinterConfigurationOptions() {
|
|
return parent::getLinterConfigurationOptions() + array(
|
|
'xhpast.printf-functions' => array(
|
|
'type' => 'optional map<string, int>',
|
|
'help' => pht(
|
|
'`%s`-style functions which take a format string and list of values '.
|
|
'as arguments. The value for the mapping is the start index of the '.
|
|
'function parameters (the index of the format string parameter).',
|
|
'printf()'),
|
|
),
|
|
);
|
|
}
|
|
|
|
public function setLinterConfigurationValue($key, $value) {
|
|
switch ($key) {
|
|
case 'xhpast.printf-functions':
|
|
$this->printfFunctions = $value;
|
|
return;
|
|
default:
|
|
return parent::setLinterConfigurationValue($key, $value);
|
|
}
|
|
}
|
|
|
|
public function process(XHPASTNode $root) {
|
|
static $functions = array(
|
|
// Core PHP
|
|
'fprintf' => 1,
|
|
'printf' => 0,
|
|
'sprintf' => 0,
|
|
'vfprintf' => 1,
|
|
|
|
// libphutil
|
|
'csprintf' => 0,
|
|
'execx' => 0,
|
|
'exec_manual' => 0,
|
|
'hgsprintf' => 0,
|
|
'hsprintf' => 0,
|
|
'jsprintf' => 0,
|
|
'pht' => 0,
|
|
'phutil_passthru' => 0,
|
|
'qsprintf' => 1,
|
|
'queryfx' => 1,
|
|
'queryfx_all' => 1,
|
|
'queryfx_one' => 1,
|
|
'vcsprintf' => 0,
|
|
'vqsprintf' => 1,
|
|
);
|
|
|
|
$function_calls = $root->selectDescendantsOfType('n_FUNCTION_CALL');
|
|
|
|
foreach ($function_calls as $call) {
|
|
$name = $call->getChildByIndex(0)->getConcreteString();
|
|
|
|
$name = strtolower($name);
|
|
$start = idx($functions + $this->printfFunctions, $name);
|
|
|
|
if ($start === null) {
|
|
continue;
|
|
}
|
|
|
|
$parameters = $call->getChildOfType(1, 'n_CALL_PARAMETER_LIST');
|
|
$argc = count($parameters->getChildren()) - $start;
|
|
|
|
if ($argc < 1) {
|
|
$this->raiseLintAtNode(
|
|
$call,
|
|
pht('This function is expected to have a format string.'));
|
|
continue;
|
|
}
|
|
|
|
$format = $parameters->getChildByIndex($start);
|
|
if (!$format->isConstantString()) {
|
|
|
|
// TODO: When this parameter is not a constant string, the call may
|
|
// be unsafe. We should make some attempt to warn about this for
|
|
// "qsprintf()" and other security-sensitive functions.
|
|
|
|
continue;
|
|
}
|
|
|
|
$argv = array($format->evalStatic()) + array_fill(0, $argc, null);
|
|
|
|
try {
|
|
xsprintf(null, null, $argv);
|
|
} catch (BadFunctionCallException $ex) {
|
|
$this->raiseLintAtNode(
|
|
$call,
|
|
str_replace('xsprintf', $name, $ex->getMessage()));
|
|
} catch (InvalidArgumentException $ex) {
|
|
// Ignore.
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|