4
* @task sharing Sharing Parse Trees
6
abstract class ArcanistBaseXHPASTLinter extends ArcanistFutureLinter {
8
private $futures = array();
9
private $trees = array();
10
private $exceptions = array();
12
final public function getCacheVersion() {
15
$parts[] = $this->getVersion();
17
$path = xhpast_get_binary_path();
18
if (Filesystem::pathExists($path)) {
19
$parts[] = md5_file($path);
22
return implode('-', $parts);
25
final protected function raiseLintAtToken(
30
return $this->raiseLintAtOffset(
38
final protected function raiseLintAtNode(
43
return $this->raiseLintAtOffset(
47
$node->getConcreteString(),
51
final protected function buildFutures(array $paths) {
52
return $this->getXHPASTLinter()->buildSharedFutures($paths);
56
/* -( Sharing Parse Trees )------------------------------------------------ */
59
* Get the linter object which is responsible for building parse trees.
61
* When the engine specifies that several XHPAST linters should execute,
62
* we designate one of them as the one which will actually build parse trees.
63
* The other linters share trees, so they don't have to recompute them.
65
* Roughly, the first linter to execute elects itself as the builder.
66
* Subsequent linters request builds and retrieve results from it.
68
* @return ArcanistBaseXHPASTLinter Responsible linter.
71
final protected function getXHPASTLinter() {
72
$resource_key = 'xhpast.linter';
74
// If we're the first linter to run, share ourselves. Otherwise, grab the
75
// previously shared linter.
77
$engine = $this->getEngine();
78
$linter = $engine->getLinterResource($resource_key);
81
$engine->setLinterResource($resource_key, $linter);
84
$base_class = __CLASS__;
85
if (!($linter instanceof $base_class)) {
88
'Expected resource "%s" to be an instance of "%s"!',
97
* Build futures on this linter, for use and to share with other linters.
99
* @param list<string> Paths to build futures for.
100
* @return list<ExecFuture> Futures.
103
final protected function buildSharedFutures(array $paths) {
104
foreach ($paths as $path) {
105
if (!isset($this->futures[$path])) {
106
$this->futures[$path] = xhpast_get_parser_future($this->getData($path));
109
return array_select_keys($this->futures, $paths);
113
* Get a path's tree from the responsible linter.
115
* @param string Path to retrieve tree for.
116
* @return XHPASTTree|null Tree, or null if unparseable.
119
final protected function getXHPASTTreeForPath($path) {
121
// If we aren't the linter responsible for actually building the parse
122
// trees, go get the tree from that linter.
123
if ($this->getXHPASTLinter() !== $this) {
124
return $this->getXHPASTLinter()->getXHPASTTreeForPath($path);
127
if (!array_key_exists($path, $this->trees)) {
128
$this->trees[$path] = null;
130
$this->trees[$path] = XHPASTTree::newFromDataAndResolvedExecFuture(
131
$this->getData($path),
132
$this->futures[$path]->resolve());
133
$root = $this->trees[$path]->getRootNode();
134
$root->buildSelectCache();
135
$root->buildTokenCache();
136
} catch (Exception $ex) {
137
$this->exceptions[$path] = $ex;
141
return $this->trees[$path];
145
* Get a path's parse exception from the responsible linter.
147
* @param string Path to retrieve exception for.
148
* @return Exeption|null Parse exception, if available.
151
final protected function getXHPASTExceptionForPath($path) {
152
if ($this->getXHPASTLinter() !== $this) {
153
return $this->getXHPASTLinter()->getXHPASTExceptionForPath($path);
156
return idx($this->exceptions, $path);