in src/__Private/LinterCLI.hack [103:207]
private async function mainImplAsync(): Awaitable<int> {
$terminal = $this->getTerminal();
if ($this->mode === LinterCLIMode::LSP) {
return await (new LSPImpl\Server($terminal))->mainAsync();
}
$err = $this->getStderr();
$roots = $this->getArguments();
$config_file = $this->configFile;
if ($config_file is nonnull) {
$specified_config = LintRunConfig::getFromConfigFile($config_file);
} else {
$specified_config = null;
}
if (C\is_empty($roots)) {
$config = $specified_config ?? LintRunConfig::getForPath(\getcwd());
$roots = $config->getRoots();
if (C\is_empty($roots)) {
await $err->writeAllAsync(
'You must either specify PATH arguments, or provide a configuration'.
"file, in which the `roots` setting is not empty.\n",
);
return 1;
}
} else {
foreach ($roots as $root) {
$path = \realpath($root);
if (\is_dir($path)) {
$config_file = $path.'/hhast-lint.json';
if (\file_exists($config_file)) {
/* HHAST_IGNORE_ERROR[DontAwaitInALoop] */
await $err->writeAllAsync(
'Warning: PATH arguments contain a lint config file at '.$path.
'hhast-lint.json, which modifies the linters used and '.
"customizes behavior. Consider 'cd ".$root.
"; vendor/bin/hhast-lint'\n\n",
);
}
}
}
$config = $specified_config;
}
switch ($this->mode) {
case LinterCLIMode::PLAIN:
$error_handler = new LintRunCLIEventHandler($terminal);
break;
case LinterCLIMode::JSON:
$error_handler = new LintRunJSONEventHandler($terminal);
break;
case LinterCLIMode::LSP:
invariant_violation('should have returned earlier');
}
try {
$result = await (
new LintRun($config, $error_handler, $roots)
)->runAsync();
} catch (LinterException $e) {
$orig = $e->getPrevious() ?? $e;
$err = $terminal->getStderr();
$pos = $e->getPosition();
await $err->writeAllAsync(Str\format(
"A linter threw an exception:\n Linter: %s\n File: %s%s\n",
$e->getLinterClass(),
\realpath($e->getFileBeingLinted()),
$pos === null ? '' : Str\format(':%d:%d', $pos[0], $pos[1] + 1),
));
if ($pos !== null && \is_readable($e->getFileBeingLinted())) {
list($line, $column) = $pos;
await (
\file_get_contents($e->getFileBeingLinted())
|> Str\split($$, "\n")
|> Vec\take($$, $line)
|> Vec\slice($$, Math\maxva($line - 3, 0))
|> Vec\map($$, $line ==> ' > '.$line)
|> Str\join($$, "\n")
|> Str\format("%s\n %s^ HERE\n", $$, Str\repeat(' ', $column))
|> $err->writeAllAsync($$)
);
}
await $err->writeAllAsync(Str\format(
" Exception: %s\n"." Message: %s\n",
\get_class($orig),
$orig->getMessage(),
));
await $err->writeAllAsync(
$orig->getTraceAsString()
|> Str\split($$, "\n")
|> Vec\map($$, $line ==> ' '.$line)
|> Str\join($$, "\n")
|> " Trace:\n".$$."\n\n",
);
return 2;
}
switch ($result) {
case LintRunResult::NO_ERRORS:
case LintRunResult::HAD_AUTOFIXED_ERRORS:
return 0;
case LintRunResult::HAVE_UNFIXED_ERRORS:
return 1;
}
}