Page MenuHomePhabricator

D9059.diff
No OneTemporary

D9059.diff

diff --git a/src/lint/engine/ArcanistLintEngine.php b/src/lint/engine/ArcanistLintEngine.php
--- a/src/lint/engine/ArcanistLintEngine.php
+++ b/src/lint/engine/ArcanistLintEngine.php
@@ -65,6 +65,8 @@
private $postponedLinters = array();
private $configurationManager;
+ private $linterResources = array();
+
public function __construct() {
}
@@ -548,4 +550,38 @@
}
+ /**
+ * Get a named linter resource shared by another linter.
+ *
+ * This mechanism allows linters to share arbitrary resources, like the
+ * results of computation. If several linters need to perform the same
+ * expensive computation step, they can use a named resource to synchronize
+ * construction of the result so it doesn't need to be built multiple
+ * times.
+ *
+ * @param string Resource identifier.
+ * @param wild Optionally, default value to return if resource does not
+ * exist.
+ * @return wild Resource, or default value if not present.
+ */
+ public function getLinterResource($key, $default = null) {
+ return idx($this->linterResources, $key, $default);
+ }
+
+
+ /**
+ * Set a linter resource that other linters can accesss.
+ *
+ * See @{method:getLinterResource} for a description of this mechanism.
+ *
+ * @param string Resource identifier.
+ * @param wild Resource.
+ * @return this
+ */
+ public function setLinterResource($key, $value) {
+ $this->linterResources[$key] = $value;
+ return $this;
+ }
+
+
}
diff --git a/src/lint/engine/PhutilLintEngine.php b/src/lint/engine/PhutilLintEngine.php
--- a/src/lint/engine/PhutilLintEngine.php
+++ b/src/lint/engine/PhutilLintEngine.php
@@ -58,7 +58,6 @@
$linters[] = $xhpast_linter;
$linters[] = id(new ArcanistPhutilXHPASTLinter())
- ->setXHPASTLinter($xhpast_linter)
->setPaths($php_paths);
$merge_conflict_linter = id(new ArcanistMergeConflictLinter());
diff --git a/src/lint/linter/ArcanistBaseXHPASTLinter.php b/src/lint/linter/ArcanistBaseXHPASTLinter.php
--- a/src/lint/linter/ArcanistBaseXHPASTLinter.php
+++ b/src/lint/linter/ArcanistBaseXHPASTLinter.php
@@ -1,10 +1,14 @@
<?php
/**
- * @group linter
+ * @task sharing Sharing Parse Trees
*/
abstract class ArcanistBaseXHPASTLinter extends ArcanistFutureLinter {
+ private $futures = array();
+ private $trees = array();
+ private $exceptions = array();
+
protected final function raiseLintAtToken(
XHPASTToken $token,
$code,
@@ -31,4 +35,117 @@
$replace);
}
+ protected function buildFutures(array $paths) {
+ return $this->getXHPASTLinter()->buildSharedFutures($paths);
+ }
+
+
+/* -( Sharing Parse Trees )------------------------------------------------ */
+
+
+ /**
+ * Get the linter object which is responsible for building parse trees.
+ *
+ * When the engine specifies that several XHPAST linters should execute,
+ * we designate one of them as the one which will actually build parse trees.
+ * The other linters share trees, so they don't have to recompute them.
+ *
+ * Roughly, the first linter to execute elects itself as the builder.
+ * Subsequent linters request builds and retrieve results from it.
+ *
+ * @return ArcanistBaseXHPASTLinter Responsible linter.
+ * @task sharing
+ */
+ protected function getXHPASTLinter() {
+ $resource_key = 'xhpast.linter';
+
+ // If we're the first linter to run, share ourselves. Otherwise, grab the
+ // previously shared linter.
+
+ $engine = $this->getEngine();
+ $linter = $engine->getLinterResource($resource_key);
+ if (!$linter) {
+ $linter = $this;
+ $engine->setLinterResource($resource_key, $linter);
+ }
+
+ $base_class = __CLASS__;
+ if (!($linter instanceof $base_class)) {
+ throw new Exception(
+ pht(
+ 'Expected resource "%s" to be an instance of "%s"!',
+ $resource_key,
+ $base_class));
+ }
+
+ return $linter;
+ }
+
+
+ /**
+ * Build futures on this linter, for use and to share with other linters.
+ *
+ * @param list<string> Paths to build futures for.
+ * @return list<ExecFuture> Futures.
+ * @task sharing
+ */
+ protected function buildSharedFutures(array $paths) {
+ foreach ($paths as $path) {
+ if (!isset($this->futures[$path])) {
+ $this->futures[$path] = xhpast_get_parser_future($this->getData($path));
+ }
+ }
+ return array_select_keys($this->futures, $paths);
+ }
+
+
+ /**
+ * Get a path's tree from the responsible linter.
+ *
+ * @param string Path to retrieve tree for.
+ * @return XHPASTTree|null Tree, or null if unparseable.
+ * @task sharing
+ */
+ protected function getXHPASTTreeForPath($path) {
+
+ // If we aren't the linter responsible for actually building the parse
+ // trees, go get the tree from that linter.
+ if ($this->getXHPASTLinter() !== $this) {
+ return $this->getXHPASTLinter()->getXHPASTTreeForPath($path);
+ }
+
+ if (!array_key_exists($path, $this->trees)) {
+ $this->trees[$path] = null;
+ try {
+ $this->trees[$path] = XHPASTTree::newFromDataAndResolvedExecFuture(
+ $this->getData($path),
+ $this->futures[$path]->resolve());
+ $root = $this->trees[$path]->getRootNode();
+ $root->buildSelectCache();
+ $root->buildTokenCache();
+ } catch (Exception $ex) {
+ $this->exceptions[$path] = $ex;
+ }
+ }
+
+ return $this->trees[$path];
+ }
+
+
+ /**
+ * Get a path's parse exception from the responsible linter.
+ *
+ * @param string Path to retrieve exception for.
+ * @return Exeption|null Parse exception, if available.
+ * @task sharing
+ */
+ protected function getXHPASTExceptionForPath($path) {
+ if ($this->getXHPASTLinter() !== $this) {
+ return $this->getXHPASTLinter()->getXHPASTExceptionForPath($path);
+ }
+
+ return idx($this->exceptions, $path);
+ }
+
+
}
diff --git a/src/lint/linter/ArcanistPhutilXHPASTLinter.php b/src/lint/linter/ArcanistPhutilXHPASTLinter.php
--- a/src/lint/linter/ArcanistPhutilXHPASTLinter.php
+++ b/src/lint/linter/ArcanistPhutilXHPASTLinter.php
@@ -1,24 +1,15 @@
<?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;
@@ -34,15 +25,6 @@
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',
@@ -73,17 +55,8 @@
return $version;
}
- protected function buildFutures(array $paths) {
- return $this->xhpastLinter->buildFutures($paths);
- }
-
- public function willLintPath($path) {
- $this->xhpastLinter->willLintPath($path);
- return parent::willLintPath($path);
- }
-
protected function resolveFuture($path, Future $future) {
- $tree = $this->xhpastLinter->getXHPASTTreeForPath($path);
+ $tree = $this->getXHPASTLinter()->getXHPASTTreeForPath($path);
if (!$tree) {
return;
}
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
@@ -2,14 +2,9 @@
/**
* Uses XHPAST to apply lint rules to PHP.
- *
- * @group linter
*/
final class ArcanistXHPASTLinter extends ArcanistBaseXHPASTLinter {
- private $futures = array();
- private $trees = array();
-
const LINT_PHP_SYNTAX_ERROR = 1;
const LINT_UNABLE_TO_PARSE = 2;
const LINT_VARIABLE_VARIABLE = 3;
@@ -130,38 +125,6 @@
);
}
- protected function buildFutures(array $paths) {
- foreach ($paths as $path) {
- if (!isset($this->futures[$path])) {
- $this->futures[$path] = xhpast_get_parser_future($this->getData($path));
- }
- }
- return array_select_keys($this->futures, $paths);
- }
-
- public function getXHPASTTreeForPath($path) {
- if (!array_key_exists($path, $this->trees)) {
- $this->trees[$path] = null;
- try {
- $this->trees[$path] = XHPASTTree::newFromDataAndResolvedExecFuture(
- $this->getData($path),
- $this->futures[$path]->resolve());
- $root = $this->trees[$path]->getRootNode();
- $root->buildSelectCache();
- $root->buildTokenCache();
- } catch (XHPASTSyntaxErrorException $ex) {
- $this->raiseLintAtLine(
- $ex->getErrorLine(),
- 1,
- self::LINT_PHP_SYNTAX_ERROR,
- 'This file contains a syntax error: '.$ex->getMessage());
- } catch (Exception $ex) {
- $this->raiseLintAtPath(self::LINT_UNABLE_TO_PARSE, $ex->getMessage());
- }
- }
- return $this->trees[$path];
- }
-
public function getCacheVersion() {
$version = '4';
$path = xhpast_get_binary_path();
@@ -174,6 +137,16 @@
protected function resolveFuture($path, Future $future) {
$tree = $this->getXHPASTTreeForPath($path);
if (!$tree) {
+ $ex = $this->getXHPASTExceptionForPath($path);
+ if ($ex instanceof XHPASTSyntaxErrorException) {
+ $this->raiseLintAtLine(
+ $ex->getErrorLine(),
+ 1,
+ self::LINT_PHP_SYNTAX_ERROR,
+ 'This file contains a syntax error: '.$ex->getMessage());
+ } else if ($ex instanceof Exception) {
+ $this->raiseLintAtPath(self::LINT_UNABLE_TO_PARSE, $ex->getMessage());
+ }
return;
}
diff --git a/src/lint/linter/__tests__/ArcanistPhutilXHPASTLinterTestCase.php b/src/lint/linter/__tests__/ArcanistPhutilXHPASTLinterTestCase.php
--- a/src/lint/linter/__tests__/ArcanistPhutilXHPASTLinterTestCase.php
+++ b/src/lint/linter/__tests__/ArcanistPhutilXHPASTLinterTestCase.php
@@ -8,7 +8,6 @@
public function testPhutilXHPASTLint() {
$linter = new ArcanistPhutilXHPASTLinter();
- $linter->setXHPASTLinter(new ArcanistXHPASTLinter());
$linter->setDeprecatedFunctions(array(
'deprecated_function' => 'This function is most likely deprecated.',
));

File Metadata

Mime Type
text/plain
Expires
Sun, May 12, 5:34 AM (1 w, 4 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6290023
Default Alt Text
D9059.diff (10 KB)

Event Timeline