private async function mainImplAsync()

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;
    }
  }