Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2889757
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Advanced/Developer...
View Handle
View Hovercard
Size
11 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/.arcconfig b/.arcconfig
index f112192e50..0ca12325b8 100644
--- a/.arcconfig
+++ b/.arcconfig
@@ -1,9 +1,8 @@
{
"project.name" : "phabricator",
"phabricator.uri" : "https://secure.phabricator.com/",
- "lint.engine" : "PhabricatorLintEngine",
"unit.engine" : "PhutilUnitTestEngine",
"load" : ["src/"],
"lint.xhpast.naminghook" : "PhabricatorSymbolNameLinter",
"lint.jshint.config" : "support/jshint/jshintconfig"
}
diff --git a/.arclint b/.arclint
new file mode 100644
index 0000000000..809327d329
--- /dev/null
+++ b/.arclint
@@ -0,0 +1,61 @@
+{
+ "exclude": [
+ "(^externals/)",
+ "(\\.lint-test$)"
+ ],
+ "linters": {
+ "filename": {
+ "type": "filename"
+ },
+ "javelin": {
+ "type": "javelin",
+ "include": "(\\.js$)",
+ "exclude": [
+ "(^externals/JsShrink/)",
+ "(^support/aphlict/)",
+ "(^webroot/rsrc/externals/raphael/)"
+ ]
+ },
+ "jshint": {
+ "type": "jshint",
+ "include": "(\\.js$)",
+ "exclude": [
+ "(^externals/JsShrink/)",
+ "(^webroot/rsrc/externals/raphael/)"
+ ]
+ },
+ "generated": {
+ "type": "generated"
+ },
+ "merge-conflict": {
+ "type": "merge-conflict"
+ },
+ "nolint": {
+ "type": "nolint"
+ },
+ "phutil-xhpast": {
+ "type": "phutil-xhpast",
+ "include": "(\\.php$)",
+ "phutil-xhpast.deprecated.functions": {
+ "phutil_escape_html": "The phutil_escape_html() function is deprecated. Raw strings passed to phutil_tag() or hsprintf() are escaped automatically."
+ }
+ },
+ "text": {
+ "type": "text"
+ },
+ "spelling": {
+ "type": "spelling"
+ },
+ "xhpast": {
+ "type": "xhpast",
+ "include": "(\\.php$)",
+ "severity": {
+ "16": "advice",
+ "29": "warning",
+ "31": "error",
+ "34": "error",
+ "35": "error"
+ }
+ }
+ }
+}
diff --git a/src/infrastructure/lint/PhabricatorLintEngine.php b/src/infrastructure/lint/PhabricatorLintEngine.php
deleted file mode 100644
index 37722ba991..0000000000
--- a/src/infrastructure/lint/PhabricatorLintEngine.php
+++ /dev/null
@@ -1,66 +0,0 @@
-<?php
-
-/**
- * @concrete-extensible
- */
-class PhabricatorLintEngine extends PhutilLintEngine {
-
- public function buildLinters() {
- $linters = parent::buildLinters();
-
- foreach ($linters as $linter) {
- if ($linter instanceof ArcanistPhutilXHPASTLinter) {
- $linter->setDeprecatedFunctions(array(
- 'phutil_escape_html' =>
- 'The phutil_escape_html() function is deprecated. Raw strings '.
- 'passed to phutil_tag() or hsprintf() are escaped automatically.',
- ));
- }
- }
-
- $paths = $this->getPaths();
-
- foreach ($paths as $key => $path) {
- if (!$this->pathExists($path)) {
- unset($paths[$key]);
- }
- }
-
- $javelin_linter = new PhabricatorJavelinLinter();
- $linters[] = $javelin_linter;
-
- $jshint_linter = new ArcanistJSHintLinter();
- $linters[] = $jshint_linter;
-
- foreach ($paths as $path) {
- if (!preg_match('/\.js$/', $path)) {
- continue;
- }
-
- if (strpos($path, 'externals/JsShrink') !== false) {
- // Ignore warnings in JsShrink tests.
- continue;
- }
-
- if (strpos($path, 'externals/raphael') !== false) {
- // Ignore Raphael.
- continue;
- }
-
- $jshint_linter->addPath($path);
- $jshint_linter->addData($path, $this->loadData($path));
-
- if (strpos($path, 'support/aphlict/') !== false) {
- // This stuff is Node.js, not Javelin, so don't apply the Javelin
- // linter.
- continue;
- }
-
- $javelin_linter->addPath($path);
- $javelin_linter->addData($path, $this->loadData($path));
- }
-
- return $linters;
- }
-
-}
diff --git a/src/infrastructure/lint/linter/PhabricatorJavelinLinter.php b/src/infrastructure/lint/linter/PhabricatorJavelinLinter.php
index 4c73e10d8e..d4d04b555e 100644
--- a/src/infrastructure/lint/linter/PhabricatorJavelinLinter.php
+++ b/src/infrastructure/lint/linter/PhabricatorJavelinLinter.php
@@ -1,265 +1,269 @@
<?php
final class PhabricatorJavelinLinter extends ArcanistLinter {
private $symbols = array();
private $symbolsBinary;
private $haveWarnedAboutBinary;
const LINT_PRIVATE_ACCESS = 1;
const LINT_MISSING_DEPENDENCY = 2;
const LINT_UNNECESSARY_DEPENDENCY = 3;
const LINT_UNKNOWN_DEPENDENCY = 4;
const LINT_MISSING_BINARY = 5;
private function getBinaryPath() {
if ($this->symbolsBinary === null) {
list($err, $stdout) = exec_manual('which javelinsymbols');
$this->symbolsBinary = ($err ? false : rtrim($stdout));
}
return $this->symbolsBinary;
}
public function willLintPaths(array $paths) {
if (!$this->getBinaryPath()) {
return;
}
$root = dirname(phutil_get_library_root('phabricator'));
require_once $root.'/scripts/__init_script__.php';
$futures = array();
foreach ($paths as $path) {
if ($this->shouldIgnorePath($path)) {
continue;
}
$future = $this->newSymbolsFuture($path);
$futures[$path] = $future;
}
foreach (Futures($futures)->limit(8) as $path => $future) {
$this->symbols[$path] = $future->resolvex();
}
}
public function getLinterName() {
return 'JAVELIN';
}
+ public function getLinterConfigurationName() {
+ return 'javelin';
+ }
+
public function getLintSeverityMap() {
return array(
self::LINT_MISSING_BINARY => ArcanistLintSeverity::SEVERITY_WARNING,
);
}
public function getLintNameMap() {
return array(
self::LINT_PRIVATE_ACCESS => 'Private Method/Member Access',
self::LINT_MISSING_DEPENDENCY => 'Missing Javelin Dependency',
self::LINT_UNNECESSARY_DEPENDENCY => 'Unnecessary Javelin Dependency',
self::LINT_UNKNOWN_DEPENDENCY => 'Unknown Javelin Dependency',
self::LINT_MISSING_BINARY => '`javelinsymbols` Not In Path',
);
}
public function getCacheGranularity() {
return ArcanistLinter::GRANULARITY_REPOSITORY;
}
public function getCacheVersion() {
$version = '0';
$binary_path = $this->getBinaryPath();
if ($binary_path) {
$version .= '-'.md5_file($binary_path);
}
return $version;
}
private function shouldIgnorePath($path) {
return preg_match('@/__tests__/|externals/javelin/docs/@', $path);
}
public function lintPath($path) {
if ($this->shouldIgnorePath($path)) {
return;
}
if (!$this->symbolsBinary) {
if (!$this->haveWarnedAboutBinary) {
$this->haveWarnedAboutBinary = true;
// TODO: Write build documentation for the Javelin binaries and point
// the user at it.
$this->raiseLintAtLine(
1,
0,
self::LINT_MISSING_BINARY,
"The 'javelinsymbols' binary in the Javelin project is not ".
"available in \$PATH, so the Javelin linter can't run. This ".
"isn't a big concern, but means some Javelin problems can't be ".
"automatically detected.");
}
return;
}
list($uses, $installs) = $this->getUsedAndInstalledSymbolsForPath($path);
foreach ($uses as $symbol => $line) {
$parts = explode('.', $symbol);
foreach ($parts as $part) {
if ($part[0] == '_' && $part[1] != '_') {
$base = implode('.', array_slice($parts, 0, 2));
if (!array_key_exists($base, $installs)) {
$this->raiseLintAtLine(
$line,
0,
self::LINT_PRIVATE_ACCESS,
"This file accesses private symbol '{$symbol}' across file ".
"boundaries. You may only access private members and methods ".
"from the file where they are defined.");
}
break;
}
}
}
if ($this->getEngine()->getCommitHookMode()) {
// Don't do the dependency checks in commit-hook mode because we won't
// have an available working copy.
return;
}
$external_classes = array();
foreach ($uses as $symbol => $line) {
$parts = explode('.', $symbol);
$class = implode('.', array_slice($parts, 0, 2));
if (!array_key_exists($class, $external_classes) &&
!array_key_exists($class, $installs)) {
$external_classes[$class] = $line;
}
}
$celerity = CelerityResourceMap::getNamedInstance('phabricator');
$path = preg_replace(
'@^externals/javelinjs/src/@',
'webroot/rsrc/js/javelin/',
$path);
$need = $external_classes;
$resource_name = substr($path, strlen('webroot/'));
$requires = $celerity->getRequiredSymbolsForName($resource_name);
if (!$requires) {
$requires = array();
}
foreach ($requires as $key => $requires_symbol) {
$requires_name = $celerity->getResourceNameForSymbol($requires_symbol);
if ($requires_name === null) {
$this->raiseLintAtLine(
0,
0,
self::LINT_UNKNOWN_DEPENDENCY,
"This file @requires component '{$requires_symbol}', but it does ".
"not exist. You may need to rebuild the Celerity map.");
unset($requires[$key]);
continue;
}
if (preg_match('/\\.css$/', $requires_name)) {
// If JS requires CSS, just assume everything is fine.
unset($requires[$key]);
} else {
$symbol_path = 'webroot/'.$requires_name;
list($ignored, $req_install) = $this->getUsedAndInstalledSymbolsForPath(
$symbol_path);
if (array_intersect_key($req_install, $external_classes)) {
$need = array_diff_key($need, $req_install);
unset($requires[$key]);
}
}
}
foreach ($need as $class => $line) {
$this->raiseLintAtLine(
$line,
0,
self::LINT_MISSING_DEPENDENCY,
"This file uses '{$class}' but does not @requires the component ".
"which installs it. You may need to rebuild the Celerity map.");
}
foreach ($requires as $component) {
$this->raiseLintAtLine(
0,
0,
self::LINT_UNNECESSARY_DEPENDENCY,
"This file @requires component '{$component}' but does not use ".
"anything it provides.");
}
}
private function loadSymbols($path) {
if (empty($this->symbols[$path])) {
$this->symbols[$path] = $this->newSymbolsFuture($path)->resolvex();
}
return $this->symbols[$path];
}
private function newSymbolsFuture($path) {
$future = new ExecFuture('javelinsymbols # %s', $path);
$future->write($this->getData($path));
return $future;
}
private function getUsedAndInstalledSymbolsForPath($path) {
list($symbols) = $this->loadSymbols($path);
$symbols = trim($symbols);
$uses = array();
$installs = array();
if (empty($symbols)) {
// This file has no symbols.
return array($uses, $installs);
}
$symbols = explode("\n", trim($symbols));
foreach ($symbols as $line) {
$matches = null;
if (!preg_match('/^([?+\*])([^:]*):(\d+)$/', $line, $matches)) {
throw new Exception(
"Received malformed output from `javelinsymbols`.");
}
$type = $matches[1];
$symbol = $matches[2];
$line = $matches[3];
switch ($type) {
case '?':
$uses[$symbol] = $line;
break;
case '+':
$installs['JX.'.$symbol] = $line;
break;
}
}
$contents = $this->getData($path);
$matches = null;
$count = preg_match_all(
'/@javelin-installs\W+(\S+)/',
$contents,
$matches,
PREG_PATTERN_ORDER);
if ($count) {
foreach ($matches[1] as $symbol) {
$installs[$symbol] = 0;
}
}
return array($uses, $installs);
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Jan 19, 12:36 (3 w, 4 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1124636
Default Alt Text
(11 KB)
Attached To
Mode
rP Phorge
Attached
Detach File
Event Timeline
Log In to Comment