Page MenuHomeSealhub

No OneTemporary

diff --git a/src/lint/linter/ArcanistJSHintLinter.php b/src/lint/linter/ArcanistJSHintLinter.php
index ac6ecc31..9a066dae 100644
--- a/src/lint/linter/ArcanistJSHintLinter.php
+++ b/src/lint/linter/ArcanistJSHintLinter.php
@@ -1,163 +1,171 @@
<?php
/**
* Uses "JSHint" to detect errors and potential problems in JavaScript code.
* To use this linter, you must install jshint through NPM (Node Package
* Manager). You can configure different JSHint options on a per-file basis.
*
* If you have NodeJS installed you should be able to install jshint with
* ##npm install jshint -g## (don't forget the -g flag or NPM will install
* the package locally). If your system is unusual, you can manually specify
* the location of jshint and its dependencies by configuring these keys in
* your .arcconfig:
*
* lint.jshint.prefix
* lint.jshint.bin
*
* If you want to configure custom options for your project, create a JSON
* file with these options and add the path to the file to your .arcconfig
* by configuring this key:
*
* lint.jshint.config
*
* Example JSON file (config.json):
*
* {
* "predef": [ // Custom globals
* "myGlobalVariable",
* "anotherGlobalVariable"
* ],
*
* "es5": true, // Allow ES5 syntax
* "strict": true // Require strict mode
* }
*
* For more options see http://www.jshint.com/options/.
*
* @group linter
*/
final class ArcanistJSHintLinter extends ArcanistLinter {
const JSHINT_ERROR = 1;
public function getLinterName() {
return 'JSHint';
}
public function getLintSeverityMap() {
return array(
self::JSHINT_ERROR => ArcanistLintSeverity::SEVERITY_ERROR
);
}
public function getLintNameMap() {
return array(
self::JSHINT_ERROR => "JSHint Error"
);
}
public function getJSHintOptions() {
$working_copy = $this->getEngine()->getWorkingCopy();
$options = '--reporter '.dirname(realpath(__FILE__)).'/reporter.js';
$config = $working_copy->getConfig('lint.jshint.config');
if ($config !== null) {
$config = Filesystem::resolvePath(
$config,
$working_copy->getProjectRoot());
if (!Filesystem::pathExists($config)) {
throw new ArcanistUsageException(
"Unable to find custom options file defined by ".
"'lint.jshint.config'. Make sure that the path is correct.");
}
$options .= ' --config '.$config;
}
return $options;
}
private function getJSHintPath() {
$working_copy = $this->getEngine()->getWorkingCopy();
$prefix = $working_copy->getConfig('lint.jshint.prefix');
$bin = $working_copy->getConfig('lint.jshint.bin');
if ($bin === null) {
$bin = "jshint";
}
if ($prefix !== null) {
$bin = $prefix."/".$bin;
if (!Filesystem::pathExists($bin)) {
throw new ArcanistUsageException(
"Unable to find JSHint binary in a specified directory. Make sure ".
"that 'lint.jshint.prefix' and 'lint.jshint.bin' keys are set ".
"correctly. If you'd rather use a copy of JSHint installed ".
"globally, you can just remove these keys from your .arcconfig");
}
return $bin;
}
// Look for globally installed JSHint
list($err) = (phutil_is_windows()
? exec_manual('where %s', $bin)
: exec_manual('which %s', $bin));
if ($err) {
throw new ArcanistUsageException(
"JSHint does not appear to be installed on this system. Install it ".
"(e.g., with 'npm install jshint -g') or configure ".
"'lint.jshint.prefix' in your .arcconfig to point to the directory ".
"where it resides.");
}
return $bin;
}
public function willLintPaths(array $paths) {
+ if (!$this->isCodeEnabled(self::JSHINT_ERROR)) {
+ return;
+ }
+
$jshint_bin = $this->getJSHintPath();
$jshint_options = $this->getJSHintOptions();
$futures = array();
foreach ($paths as $path) {
$filepath = $this->getEngine()->getFilePathOnDisk($path);
$futures[$path] = new ExecFuture(
"%s %s %C",
$jshint_bin,
$filepath,
$jshint_options);
}
foreach (Futures($futures)->limit(8) as $path => $future) {
$this->results[$path] = $future->resolve();
}
}
public function lintPath($path) {
+ if (!$this->isCodeEnabled(self::JSHINT_ERROR)) {
+ return;
+ }
+
list($rc, $stdout, $stderr) = $this->results[$path];
if ($rc === 0) {
return;
}
$errors = json_decode($stdout);
if (!is_array($errors)) {
// Something went wrong and we can't decode the output. Exit abnormally.
throw new ArcanistUsageException(
"JSHint returned unparseable output.\n".
"stdout:\n\n{$stdout}".
"stderr:\n\n{$stderr}");
}
foreach ($errors as $err) {
$this->raiseLintAtLine(
$err->line,
$err->col,
self::JSHINT_ERROR,
$err->reason);
}
}
}
diff --git a/src/lint/linter/ArcanistPEP8Linter.php b/src/lint/linter/ArcanistPEP8Linter.php
index 735cfcfd..3e4a51a0 100644
--- a/src/lint/linter/ArcanistPEP8Linter.php
+++ b/src/lint/linter/ArcanistPEP8Linter.php
@@ -1,119 +1,125 @@
<?php
/**
* Uses "pep8.py" to enforce PEP8 rules for Python.
*
* @group linter
*/
final class ArcanistPEP8Linter extends ArcanistLinter {
public function willLintPaths(array $paths) {
return;
}
public function getLinterName() {
return 'PEP8';
}
public function getLintSeverityMap() {
return array();
}
public function getLintNameMap() {
return array();
}
public function getCacheVersion() {
list($stdout) = execx('%C --version', $this->getPEP8Path());
return $stdout.$this->getPEP8Options();
}
public function getPEP8Options() {
$working_copy = $this->getEngine()->getWorkingCopy();
$options = $working_copy->getConfig('lint.pep8.options');
if ($options === null) {
$options = $this->getConfig('options');
}
return $options;
}
public function getPEP8Path() {
$working_copy = $this->getEngine()->getWorkingCopy();
$prefix = $working_copy->getConfig('lint.pep8.prefix');
$bin = $working_copy->getConfig('lint.pep8.bin');
if ($bin === null && $prefix === null) {
$bin = csprintf('/usr/bin/env python %s',
phutil_get_library_root('arcanist').
'/../externals/pep8/pep8.py');
} else {
if ($bin === null) {
$bin = 'pep8';
}
if ($prefix !== null) {
if (!Filesystem::pathExists($prefix.'/'.$bin)) {
throw new ArcanistUsageException(
"Unable to find PEP8 binary in a specified directory. Make sure ".
"that 'lint.pep8.prefix' and 'lint.pep8.bin' keys are set ".
"correctly. If you'd rather use a copy of PEP8 installed ".
"globally, you can just remove these keys from your .arcconfig.");
}
$bin = csprintf("%s/%s", $prefix, $bin);
return $bin;
}
// Look for globally installed PEP8
list($err) = exec_manual('which %s', $bin);
if ($err) {
throw new ArcanistUsageException(
"PEP8 does not appear to be installed on this system. Install it ".
"(e.g., with 'easy_install pep8') or configure ".
"'lint.pep8.prefix' in your .arcconfig to point to the directory ".
"where it resides.");
}
}
return $bin;
}
public function lintPath($path) {
+ $severity = ArcanistLintSeverity::SEVERITY_WARNING;
+
+ if (!$this->getEngine()->isSeverityEnabled($severity)) {
+ return;
+ }
+
$pep8_bin = $this->getPEP8Path();
$options = $this->getPEP8Options();
list($rc, $stdout) = exec_manual(
"%C %C %s",
$pep8_bin,
$options,
$this->getEngine()->getFilePathOnDisk($path));
$lines = explode("\n", $stdout);
$messages = array();
foreach ($lines as $line) {
$matches = null;
if (!preg_match('/^(.*?):(\d+):(\d+): (\S+) (.*)$/', $line, $matches)) {
continue;
}
foreach ($matches as $key => $match) {
$matches[$key] = trim($match);
}
if (!$this->isMessageEnabled($matches[4])) {
continue;
}
$message = new ArcanistLintMessage();
$message->setPath($path);
$message->setLine($matches[2]);
$message->setChar($matches[3]);
$message->setCode($matches[4]);
$message->setName('PEP8 '.$matches[4]);
$message->setDescription($matches[5]);
- $message->setSeverity(ArcanistLintSeverity::SEVERITY_WARNING);
+ $message->setSeverity($severity);
$this->addLintMessage($message);
}
}
}
diff --git a/src/lint/linter/ArcanistPhutilXHPASTLinter.php b/src/lint/linter/ArcanistPhutilXHPASTLinter.php
index bd98b3da..7d03bbbf 100644
--- a/src/lint/linter/ArcanistPhutilXHPASTLinter.php
+++ b/src/lint/linter/ArcanistPhutilXHPASTLinter.php
@@ -1,202 +1,213 @@
<?php
/**
* @group linter
*/
final class ArcanistPhutilXHPASTLinter extends ArcanistBaseXHPASTLinter {
const LINT_ARRAY_COMBINE = 2;
const LINT_DEPRECATED_FUNCTION = 3;
const LINT_UNSAFE_DYNAMIC_STRING = 4;
private $xhpastLinter;
private $deprecatedFunctions = array();
private $dynamicStringFunctions = array();
private $dynamicStringClasses = array();
public function setXHPASTLinter(ArcanistXHPASTLinter $linter) {
$this->xhpastLinter = $linter;
return $this;
}
public function setDeprecatedFunctions($map) {
$this->deprecatedFunctions = $map;
return $this;
}
public function setDynamicStringFunctions($map) {
$this->dynamicStringFunctions = $map;
return $this;
}
public function setDynamicStringClasses($map) {
$this->dynamicStringClasses = $map;
return $this;
}
public function setEngine(ArcanistLintEngine $engine) {
if (!$this->xhpastLinter) {
throw new Exception(
'Call setXHPASTLinter() before using ArcanistPhutilXHPASTLinter.');
}
$this->xhpastLinter->setEngine($engine);
return parent::setEngine($engine);
}
public function getLintNameMap() {
return array(
self::LINT_ARRAY_COMBINE => 'array_combine() Unreliable',
self::LINT_DEPRECATED_FUNCTION => 'Use of Deprecated Function',
self::LINT_UNSAFE_DYNAMIC_STRING => 'Unsafe Usage of Dynamic String',
);
}
public function getLintSeverityMap() {
$warning = ArcanistLintSeverity::SEVERITY_WARNING;
return array(
self::LINT_ARRAY_COMBINE => $warning,
self::LINT_DEPRECATED_FUNCTION => $warning,
self::LINT_UNSAFE_DYNAMIC_STRING => $warning,
);
}
public function getLinterName() {
return 'PHLXHP';
}
public function getCacheVersion() {
return 2;
}
public function willLintPaths(array $paths) {
$this->xhpastLinter->willLintPaths($paths);
}
public function lintPath($path) {
$tree = $this->xhpastLinter->getXHPASTTreeForPath($path);
if (!$tree) {
return;
}
$root = $tree->getRootNode();
- $this->lintArrayCombine($root);
- $this->lintUnsafeDynamicString($root);
- $this->lintDeprecatedFunctions($root);
+ $method_codes = array(
+ 'lintArrayCombine' => self::LINT_ARRAY_COMBINE,
+ 'lintUnsafeDynamicString' => self::LINT_UNSAFE_DYNAMIC_STRING,
+ 'lintDeprecatedFunctions' => self::LINT_DEPRECATED_FUNCTION,
+ );
+
+ foreach ($method_codes as $method => $codes) {
+ foreach ((array)$codes as $code) {
+ if ($this->isCodeEnabled($code)) {
+ call_user_func(array($this, $method), $root);
+ break;
+ }
+ }
+ }
}
private function lintUnsafeDynamicString($root) {
$safe = $this->dynamicStringFunctions + array(
'pht' => 0,
'hsprintf' => 0,
'csprintf' => 0,
'vcsprintf' => 0,
'execx' => 0,
'exec_manual' => 0,
'phutil_passthru' => 0,
'qsprintf' => 1,
'vqsprintf' => 1,
'queryfx' => 1,
'vqueryfx' => 1,
'queryfx_all' => 1,
'vqueryfx_all' => 1,
'queryfx_one' => 1,
);
$calls = $root->selectDescendantsOfType('n_FUNCTION_CALL');
$this->lintUnsafeDynamicStringCall($calls, $safe);
$safe = $this->dynamicStringClasses + array(
'ExecFuture' => 0,
);
$news = $root->selectDescendantsOfType('n_NEW');
$this->lintUnsafeDynamicStringCall($news, $safe);
}
private function lintUnsafeDynamicStringCall(
AASTNodeList $calls,
array $safe) {
$safe = array_combine(
array_map('strtolower', array_keys($safe)),
$safe);
foreach ($calls as $call) {
$name = $call->getChildByIndex(0)->getConcreteString();
$param = idx($safe, strtolower($name));
if ($param === null) {
continue;
}
$parameters = $call->getChildByIndex(1);
if (count($parameters->getChildren()) <= $param) {
continue;
}
$identifier = $parameters->getChildByIndex($param);
if (!$identifier->isConstantString()) {
$this->raiseLintAtNode(
$call,
self::LINT_UNSAFE_DYNAMIC_STRING,
"Parameter ".($param + 1)." of {$name}() should be a scalar string, ".
"otherwise it's not safe.");
}
}
}
private function lintArrayCombine($root) {
$function_calls = $root->selectDescendantsOfType('n_FUNCTION_CALL');
foreach ($function_calls as $call) {
$name = $call->getChildByIndex(0)->getConcreteString();
if (strcasecmp($name, 'array_combine') == 0) {
$parameter_list = $call->getChildOfType(1, 'n_CALL_PARAMETER_LIST');
if (count($parameter_list->getChildren()) !== 2) {
// Wrong number of parameters, but raise that elsewhere if we want.
continue;
}
$first = $parameter_list->getChildByIndex(0);
$second = $parameter_list->getChildByIndex(1);
if ($first->getConcreteString() == $second->getConcreteString()) {
$this->raiseLintAtNode(
$call,
self::LINT_ARRAY_COMBINE,
'Prior to PHP 5.4, array_combine() fails when given empty '.
'arrays. Prefer to write array_combine(x, x) as array_fuse(x).');
}
}
}
}
private function lintDeprecatedFunctions($root) {
$map = $this->deprecatedFunctions + array(
'phutil_render_tag' =>
'The phutil_render_tag() function is deprecated and unsafe. '.
'Use phutil_tag() instead.',
);
$function_calls = $root->selectDescendantsOfType('n_FUNCTION_CALL');
foreach ($function_calls as $call) {
$name = $call->getChildByIndex(0)->getConcreteString();
$name = strtolower($name);
if (empty($map[$name])) {
continue;
}
$this->raiseLintAtNode(
$call,
self::LINT_DEPRECATED_FUNCTION,
$map[$name]);
}
}
}
diff --git a/src/lint/linter/ArcanistSpellingLinter.php b/src/lint/linter/ArcanistSpellingLinter.php
index 032e51e1..928bad43 100644
--- a/src/lint/linter/ArcanistSpellingLinter.php
+++ b/src/lint/linter/ArcanistSpellingLinter.php
@@ -1,157 +1,164 @@
<?php
/**
* Enforces basic spelling. Spelling inside code is actually pretty hard to
* get right without false positives. I take a conservative approach and
* just use a blacklisted set of words that are commonly spelled
* incorrectly.
*
* @group linter
*/
final class ArcanistSpellingLinter extends ArcanistLinter {
const LINT_SPELLING_PICKY = 0;
const LINT_SPELLING_IMPORTANT = 1;
private $partialWordRules;
private $wholeWordRules;
private $severity;
public function __construct($severity = self::LINT_SPELLING_PICKY) {
$this->severity = $severity;
$this->wholeWordRules = ArcanistSpellingDefaultData::getFullWordRules();
$this->partialWordRules =
ArcanistSpellingDefaultData::getPartialWordRules();
}
public function willLintPaths(array $paths) {
return;
}
public function getLinterName() {
return 'SPELL';
}
public function removeLintRule($word) {
foreach ($this->partialWordRules as $severity=>&$wordlist) {
unset($wordlist[$word]);
}
foreach ($this->wholeWordRules as $severity=>&$wordlist) {
unset($wordlist[$word]);
}
}
public function addPartialWordRule(
$incorrect_word,
$correct_word,
$severity=self::LINT_SPELLING_IMPORTANT) {
$this->partialWordRules[$severity][$incorrect_word] = $correct_word;
}
public function addWholeWordRule(
$incorrect_word,
$correct_word,
$severity=self::LINT_SPELLING_IMPORTANT) {
$this->wholeWordRules[$severity][$incorrect_word] = $correct_word;
}
public function getLintSeverityMap() {
return array(
self::LINT_SPELLING_PICKY => ArcanistLintSeverity::SEVERITY_WARNING,
self::LINT_SPELLING_IMPORTANT => ArcanistLintSeverity::SEVERITY_ERROR,
);
}
public function getLintNameMap() {
return array(
self::LINT_SPELLING_PICKY => 'Possible spelling mistake',
self::LINT_SPELLING_IMPORTANT => 'Possible spelling mistake',
);
}
public function lintPath($path) {
foreach ($this->partialWordRules as $severity => $wordlist) {
if ($severity >= $this->severity) {
+ if (!$this->isCodeEnabled($severity)) {
+ continue;
+ }
foreach ($wordlist as $misspell => $correct) {
$this->checkPartialWord($path, $misspell, $correct, $severity);
}
}
}
+
foreach ($this->wholeWordRules as $severity => $wordlist) {
if ($severity >= $this->severity) {
+ if (!$this->isCodeEnabled($severity)) {
+ continue;
+ }
foreach ($wordlist as $misspell => $correct) {
$this->checkWholeWord($path, $misspell, $correct, $severity);
}
}
}
}
protected function checkPartialWord($path, $word, $correct_word, $severity) {
$text = $this->getData($path);
$pos = 0;
while ($pos < strlen($text)) {
$next = stripos($text, $word, $pos);
if ($next === false) {
return;
}
$original = substr($text, $next, strlen($word));
$replacement = self::fixLetterCase($correct_word, $original);
$this->raiseLintAtOffset(
$next,
$severity,
sprintf(
"Possible spelling error. You wrote '%s', but did you mean '%s'?",
$word,
$correct_word
),
$original,
$replacement
);
$pos = $next + 1;
}
}
protected function checkWholeWord($path, $word, $correct_word, $severity) {
$text = $this->getData($path);
$matches = array();
$num_matches = preg_match_all(
'#\b' . preg_quote($word, '#') . '\b#i',
$text,
$matches,
PREG_OFFSET_CAPTURE
);
if (!$num_matches) {
return;
}
foreach ($matches[0] as $match) {
$original = $match[0];
$replacement = self::fixLetterCase($correct_word, $original);
$this->raiseLintAtOffset(
$match[1],
$severity,
sprintf(
"Possible spelling error. You wrote '%s', but did you mean '%s'?",
$word,
$correct_word
),
$original,
$replacement
);
}
}
public static function fixLetterCase($string, $case) {
if ($case == strtolower($case)) {
return strtolower($string);
}
if ($case == strtoupper($case)) {
return strtoupper($string);
}
if ($case == ucwords(strtolower($case))) {
return ucwords(strtolower($string));
}
return null;
}
}

File Metadata

Mime Type
text/x-diff
Expires
Sat, Nov 23, 01:26 (1 d, 5 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
547770
Default Alt Text
(19 KB)

Event Timeline