Page MenuHomePhabricator

D11661.id28108.diff
No OneTemporary

D11661.id28108.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
@@ -53,9 +53,11 @@
const LINT_BLACKLISTED_FUNCTION = 51;
const LINT_IMPLICIT_VISIBILITY = 52;
const LINT_CALL_TIME_PASS_BY_REF = 53;
+ const LINT_FORMATTED_STRING = 54;
private $blacklistedFunctions = array();
private $naminghook;
+ private $printfFunctions = array();
private $switchhook;
private $version;
private $windowsVersion;
@@ -118,6 +120,7 @@
self::LINT_BLACKLISTED_FUNCTION => 'Use of Blacklisted Function',
self::LINT_IMPLICIT_VISIBILITY => 'Implicit Method Visibility',
self::LINT_CALL_TIME_PASS_BY_REF => 'Call-Time Pass-By-Reference',
+ self::LINT_FORMATTED_STRING => 'Formatted String',
);
}
@@ -175,6 +178,13 @@
'Name of a concrete subclass of ArcanistXHPASTLintNamingHook which '.
'enforces more granular naming convention rules for symbols.'),
),
+ '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.',
+ 'printf()'),
+ ),
'xhpast.switchhook' => array(
'type' => 'optional string',
'help' => pht(
@@ -200,6 +210,9 @@
case 'xhpast.naminghook':
$this->naminghook = $value;
return;
+ case 'xhpast.printf-functions':
+ $this->printfFunctions = $value;
+ return;
case 'xhpast.switchhook':
$this->switchhook = $value;
return;
@@ -216,7 +229,7 @@
public function getVersion() {
// The version number should be incremented whenever a new rule is added.
- return '15';
+ return '16';
}
protected function resolveFuture($path, Future $future) {
@@ -293,6 +306,7 @@
'lintMethodModifier' => self::LINT_IMPLICIT_VISIBILITY,
'lintPropertyModifier' => self::LINT_IMPLICIT_VISIBILITY,
'lintCallTimePassByReference' => self::LINT_CALL_TIME_PASS_BY_REF,
+ 'lintFormattedString' => self::LINT_FORMATTED_STRING,
);
foreach ($method_codes as $method => $codes) {
@@ -3101,6 +3115,56 @@
}
}
+ private function lintFormattedString(XHPASTNode $root) {
+ static $functions = array(
+ 'fprintf' => 1,
+ 'printf' => 0,
+ 'sprintf' => 0,
+ 'vfprintf' => 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) {
+ continue;
+ }
+
+ $format = $parameters->getChildByIndex($start);
+ if ($format->getTypeName() != 'n_STRING_SCALAR') {
+ continue;
+ }
+
+ $argv = array($format->evalStatic()) + array_fill(0, $argc, null);
+
+ try {
+ xsprintf(array($this, 'xsprintfCallback'), null, $argv);
+ } catch (BadFunctionCallException $ex) {
+ $this->raiseLintAtNode(
+ $call,
+ self::LINT_FORMATTED_STRING,
+ $ex->getMessage());
+ }
+ }
+ }
+
+ public function xsprintfCallback() {
+ // TODO: Can we just pass a dummy callback to `xsprintf`?
+ // Empty
+ }
+
public function getSuperGlobalNames() {
return array(
'$GLOBALS',
diff --git a/src/lint/linter/__tests__/xhpast/formatted-string.lint-test b/src/lint/linter/__tests__/xhpast/formatted-string.lint-test
new file mode 100644
--- /dev/null
+++ b/src/lint/linter/__tests__/xhpast/formatted-string.lint-test
@@ -0,0 +1,26 @@
+<?php
+printf();
+printf(null);
+printf('');
+
+sprintf('%s');
+sprintf('%s', 'foo', 'bar');
+
+fprintf(null, 'x');
+fprintf(null, 'x', 'y');
+
+foobar(null, null, '%s');
+~~~~~~~~~~
+error:6:1
+error:7:1
+error:10:1
+error:12:1
+~~~~~~~~~~
+~~~~~~~~~~
+{
+ "config": {
+ "xhpast.printf-functions": {
+ "foobar": 2
+ }
+ }
+}

File Metadata

Mime Type
text/plain
Expires
Wed, Mar 26, 9:08 AM (6 d, 8 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7723581
Default Alt Text
D11661.id28108.diff (4 KB)

Event Timeline