brintos

brintos / llvm-project-archived public Read only

0
0
Text · 36.7 KiB · 4a990f8 Raw
1074 lines · cpp
1//===--- ClangdMain.cpp - clangd server loop ------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "ClangdMain.h"10#include "ClangdLSPServer.h"11#include "CodeComplete.h"12#include "Compiler.h"13#include "Config.h"14#include "ConfigProvider.h"15#include "Feature.h"16#include "FeatureModule.h"17#include "IncludeCleaner.h"18#include "PathMapping.h"19#include "Protocol.h"20#include "TidyProvider.h"21#include "Transport.h"22#include "index/Background.h"23#include "index/Index.h"24#include "index/MemIndex.h"25#include "index/Merge.h"26#include "index/ProjectAware.h"27#include "index/remote/Client.h"28#include "support/Path.h"29#include "support/Shutdown.h"30#include "support/ThreadCrashReporter.h"31#include "support/ThreadsafeFS.h"32#include "support/Trace.h"33#include "clang/Basic/Stack.h"34#include "clang/Format/Format.h"35#include "llvm/ADT/SmallString.h"36#include "llvm/ADT/StringRef.h"37#include "llvm/Support/CommandLine.h"38#include "llvm/Support/FileSystem.h"39#include "llvm/Support/InitLLVM.h"40#include "llvm/Support/Path.h"41#include "llvm/Support/Process.h"42#include "llvm/Support/Program.h"43#include "llvm/Support/Signals.h"44#include "llvm/Support/TargetSelect.h"45#include "llvm/Support/raw_ostream.h"46#include <chrono>47#include <cstdlib>48#include <memory>49#include <mutex>50#include <optional>51#include <string>52#include <thread>53#include <utility>54#include <vector>55 56#ifndef _WIN3257#include <unistd.h>58#endif59 60#ifdef __GLIBC__61#include <malloc.h>62#endif63 64namespace clang {65namespace clangd {66 67// Implemented in Check.cpp.68bool check(const llvm::StringRef File, const ThreadsafeFS &TFS,69           const ClangdLSPServer::Options &Opts);70 71namespace {72 73using llvm::cl::cat;74using llvm::cl::CommaSeparated;75using llvm::cl::desc;76using llvm::cl::Hidden;77using llvm::cl::init;78using llvm::cl::list;79using llvm::cl::opt;80using llvm::cl::OptionCategory;81using llvm::cl::ValueOptional;82using llvm::cl::values;83 84// All flags must be placed in a category, or they will be shown neither in85// --help, nor --help-hidden!86OptionCategory CompileCommands("clangd compilation flags options");87OptionCategory Features("clangd feature options");88OptionCategory Misc("clangd miscellaneous options");89OptionCategory Protocol("clangd protocol and logging options");90OptionCategory Retired("clangd flags no longer in use");91const OptionCategory *ClangdCategories[] = {&Features, &Protocol,92                                            &CompileCommands, &Misc, &Retired};93 94template <typename T> class RetiredFlag {95  opt<T> Option;96 97public:98  RetiredFlag(llvm::StringRef Name)99      : Option(Name, cat(Retired), desc("Obsolete flag, ignored"), Hidden,100               llvm::cl::callback([Name](const T &) {101                 llvm::errs()102                     << "The flag `-" << Name << "` is obsolete and ignored.\n";103               })) {}104};105 106enum CompileArgsFrom { LSPCompileArgs, FilesystemCompileArgs };107opt<CompileArgsFrom> CompileArgsFrom{108    "compile_args_from",109    cat(CompileCommands),110    desc("The source of compile commands"),111    values(clEnumValN(LSPCompileArgs, "lsp",112                      "All compile commands come from LSP and "113                      "'compile_commands.json' files are ignored"),114           clEnumValN(FilesystemCompileArgs, "filesystem",115                      "All compile commands come from the "116                      "'compile_commands.json' files")),117    init(FilesystemCompileArgs),118    Hidden,119};120 121opt<Path> CompileCommandsDir{122    "compile-commands-dir",123    cat(CompileCommands),124    desc("Specify a path to look for compile_commands.json. If path "125         "is invalid, clangd will look in the current directory and "126         "parent paths of each source file"),127};128 129opt<Path> ResourceDir{130    "resource-dir",131    cat(CompileCommands),132    desc("Directory for system clang headers"),133    init(""),134    Hidden,135};136 137list<std::string> QueryDriverGlobs{138    "query-driver",139    cat(CompileCommands),140    desc(141        "Comma separated list of globs for white-listing gcc-compatible "142        "drivers that are safe to execute. Drivers matching any of these globs "143        "will be used to extract system includes. e.g. "144        "/usr/bin/**/clang-*,/path/to/repo/**/g++-*"),145    CommaSeparated,146};147 148// FIXME: Flags are the wrong mechanism for user preferences.149// We should probably read a dotfile or similar.150opt<bool> AllScopesCompletion{151    "all-scopes-completion",152    cat(Features),153    desc("If set to true, code completion will include index symbols that are "154         "not defined in the scopes (e.g. "155         "namespaces) visible from the code completion point. Such completions "156         "can insert scope qualifiers"),157    init(true),158};159 160opt<bool> ShowOrigins{161    "debug-origin",162    cat(Features),163    desc("Show origins of completion items"),164    init(CodeCompleteOptions().ShowOrigins),165    Hidden,166};167 168opt<bool> EnableBackgroundIndex{169    "background-index",170    cat(Features),171    desc("Index project code in the background and persist index on disk."),172    init(true),173};174 175opt<llvm::ThreadPriority> BackgroundIndexPriority{176    "background-index-priority",177    cat(Features),178    desc("Thread priority for building the background index. "179         "The effect of this flag is OS-specific."),180    values(clEnumValN(llvm::ThreadPriority::Background, "background",181                      "Minimum priority, runs on idle CPUs. "182                      "May leave 'performance' cores unused."),183           clEnumValN(llvm::ThreadPriority::Low, "low",184                      "Reduced priority compared to interactive work."),185           clEnumValN(llvm::ThreadPriority::Default, "normal",186                      "Same priority as other clangd work.")),187    init(llvm::ThreadPriority::Low),188};189 190opt<bool> EnableClangTidy{191    "clang-tidy",192    cat(Features),193    desc("Enable clang-tidy diagnostics"),194    init(true),195};196 197opt<CodeCompleteOptions::CodeCompletionParse> CodeCompletionParse{198    "completion-parse",199    cat(Features),200    desc("Whether the clang-parser is used for code-completion"),201    values(clEnumValN(CodeCompleteOptions::AlwaysParse, "always",202                      "Block until the parser can be used"),203           clEnumValN(CodeCompleteOptions::ParseIfReady, "auto",204                      "Use text-based completion if the parser "205                      "is not ready"),206           clEnumValN(CodeCompleteOptions::NeverParse, "never",207                      "Always used text-based completion")),208    init(CodeCompleteOptions().RunParser),209    Hidden,210};211 212opt<CodeCompleteOptions::CodeCompletionRankingModel> RankingModel{213    "ranking-model",214    cat(Features),215    desc("Model to use to rank code-completion items"),216    values(clEnumValN(CodeCompleteOptions::Heuristics, "heuristics",217                      "Use heuristics to rank code completion items"),218           clEnumValN(CodeCompleteOptions::DecisionForest, "decision_forest",219                      "Use Decision Forest model to rank completion items")),220    init(CodeCompleteOptions().RankingModel),221    Hidden,222};223 224// FIXME: also support "plain" style where signatures are always omitted.225enum CompletionStyleFlag { Detailed, Bundled };226opt<CompletionStyleFlag> CompletionStyle{227    "completion-style",228    cat(Features),229    desc("Granularity of code completion suggestions"),230    values(clEnumValN(Detailed, "detailed",231                      "One completion item for each semantically distinct "232                      "completion, with full type information"),233           clEnumValN(Bundled, "bundled",234                      "Similar completion items (e.g. function overloads) are "235                      "combined. Type information shown where possible")),236};237 238opt<std::string> FallbackStyle{239    "fallback-style",240    cat(Features),241    desc("clang-format style to apply by default when "242         "no .clang-format file is found"),243    init(clang::format::DefaultFallbackStyle),244};245 246opt<std::string> EnableFunctionArgSnippets{247    "function-arg-placeholders",248    cat(Features),249    desc("When disabled (0), completions contain only parentheses for "250         "function calls. When enabled (1), completions also contain "251         "placeholders for method parameters"),252    init("-1"),253};254 255opt<Config::HeaderInsertionPolicy> HeaderInsertion{256    "header-insertion",257    cat(Features),258    desc("Add #include directives when accepting code completions"),259    init(CodeCompleteOptions().InsertIncludes),260    values(261        clEnumValN(Config::HeaderInsertionPolicy::IWYU, "iwyu",262                   "Include what you use. "263                   "Insert the owning header for top-level symbols, unless the "264                   "header is already directly included or the symbol is "265                   "forward-declared"),266        clEnumValN(267            Config::HeaderInsertionPolicy::NeverInsert, "never",268            "Never insert #include directives as part of code completion")),269};270 271opt<bool> ImportInsertions{272    "import-insertions",273    cat(Features),274    desc("If header insertion is enabled, add #import directives when "275         "accepting code completions or fixing includes in Objective-C code"),276    init(CodeCompleteOptions().ImportInsertions),277};278 279opt<bool> HeaderInsertionDecorators{280    "header-insertion-decorators",281    cat(Features),282    desc("Prepend a circular dot or space before the completion "283         "label, depending on whether "284         "an include line will be inserted or not"),285    init(true),286};287 288opt<bool> HiddenFeatures{289    "hidden-features",290    cat(Features),291    desc("Enable hidden features mostly useful to clangd developers"),292    init(false),293    Hidden,294};295 296opt<bool> IncludeIneligibleResults{297    "include-ineligible-results",298    cat(Features),299    desc("Include ineligible completion results (e.g. private members)"),300    init(CodeCompleteOptions().IncludeIneligibleResults),301    Hidden,302};303 304RetiredFlag<bool> EnableIndex("index");305RetiredFlag<bool> SuggestMissingIncludes("suggest-missing-includes");306RetiredFlag<bool> RecoveryAST("recovery-ast");307RetiredFlag<bool> RecoveryASTType("recovery-ast-type");308RetiredFlag<bool> AsyncPreamble("async-preamble");309RetiredFlag<bool> CollectMainFileRefs("collect-main-file-refs");310RetiredFlag<bool> CrossFileRename("cross-file-rename");311RetiredFlag<std::string> ClangTidyChecks("clang-tidy-checks");312RetiredFlag<bool> InlayHints("inlay-hints");313RetiredFlag<bool> FoldingRanges("folding-ranges");314RetiredFlag<bool> IncludeCleanerStdlib("include-cleaner-stdlib");315 316opt<int> LimitResults{317    "limit-results",318    cat(Features),319    desc("Limit the number of results returned by clangd. "320         "0 means no limit (default=100)"),321    init(100),322};323 324opt<int> ReferencesLimit{325    "limit-references",326    cat(Features),327    desc("Limit the number of references returned by clangd. "328         "0 means no limit (default=1000)"),329    init(1000),330};331 332opt<int> RenameFileLimit{333    "rename-file-limit",334    cat(Features),335    desc("Limit the number of files to be affected by symbol renaming. "336         "0 means no limit (default=50)"),337    init(50),338};339 340list<std::string> TweakList{341    "tweaks",342    cat(Features),343    desc("Specify a list of Tweaks to enable (only for clangd developers)."),344    Hidden,345    CommaSeparated,346};347 348opt<unsigned> WorkerThreadsCount{349    "j",350    cat(Misc),351    desc("Number of async workers used by clangd. Background index also "352         "uses this many workers."),353    init(getDefaultAsyncThreadsCount()),354};355 356opt<Path> IndexFile{357    "index-file",358    cat(Misc),359    desc(360        "Index file to build the static index. The file must have been created "361        "by a compatible clangd-indexer\n"362        "WARNING: This option is experimental only, and will be removed "363        "eventually. Don't rely on it"),364    init(""),365    Hidden,366};367 368opt<bool> Test{369    "lit-test",370    cat(Misc),371    desc("Abbreviation for -input-style=delimited -pretty -sync "372         "-enable-test-scheme -enable-config=0 -log=verbose -crash-pragmas. "373         "Also sets config options: Index.StandardLibrary=false. "374         "Intended to simplify lit tests"),375    init(false),376    Hidden,377};378 379opt<bool> CrashPragmas{380    "crash-pragmas",381    cat(Misc),382    desc("Respect `#pragma clang __debug crash` and friends."),383    init(false),384    Hidden,385};386 387opt<Path> CheckFile{388    "check",389    cat(Misc),390    desc("Parse one file in isolation instead of acting as a language server. "391         "Useful to investigate/reproduce crashes or configuration problems. "392         "With --check=<filename>, attempts to parse a particular file."),393    init(""),394    ValueOptional,395};396 397enum PCHStorageFlag { Disk, Memory };398opt<PCHStorageFlag> PCHStorage{399    "pch-storage",400    cat(Misc),401    desc("Storing PCHs in memory increases memory usages, but may "402         "improve performance"),403    values(404        clEnumValN(PCHStorageFlag::Disk, "disk", "store PCHs on disk"),405        clEnumValN(PCHStorageFlag::Memory, "memory", "store PCHs in memory")),406    init(PCHStorageFlag::Disk),407};408 409opt<bool> Sync{410    "sync",411    cat(Misc),412    desc("Handle client requests on main thread. Background index still uses "413         "its own thread."),414    init(false),415    Hidden,416};417 418opt<JSONStreamStyle> InputStyle{419    "input-style",420    cat(Protocol),421    desc("Input JSON stream encoding"),422    values(423        clEnumValN(JSONStreamStyle::Standard, "standard", "usual LSP protocol"),424        clEnumValN(JSONStreamStyle::Delimited, "delimited",425                   "messages delimited by --- lines, with # comment support")),426    init(JSONStreamStyle::Standard),427    Hidden,428};429 430opt<bool> EnableTestScheme{431    "enable-test-uri-scheme",432    cat(Protocol),433    desc("Enable 'test:' URI scheme. Only use in lit tests"),434    init(false),435    Hidden,436};437 438opt<std::string> PathMappingsArg{439    "path-mappings",440    cat(Protocol),441    desc(442        "Translates between client paths (as seen by a remote editor) and "443        "server paths (where clangd sees files on disk). "444        "Comma separated list of '<client_path>=<server_path>' pairs, the "445        "first entry matching a given path is used. "446        "e.g. /home/project/incl=/opt/include,/home/project=/workarea/project"),447    init(""),448};449 450opt<Path> InputMirrorFile{451    "input-mirror-file",452    cat(Protocol),453    desc("Mirror all LSP input to the specified file. Useful for debugging"),454    init(""),455    Hidden,456};457 458opt<Logger::Level> LogLevel{459    "log",460    cat(Protocol),461    desc("Verbosity of log messages written to stderr"),462    values(clEnumValN(Logger::Error, "error", "Error messages only"),463           clEnumValN(Logger::Info, "info", "High level execution tracing"),464           clEnumValN(Logger::Debug, "verbose", "Low level details")),465    init(Logger::Info),466};467 468opt<OffsetEncoding> ForceOffsetEncoding{469    "offset-encoding",470    cat(Protocol),471    desc("Force the offsetEncoding used for character positions. "472         "This bypasses negotiation via client capabilities"),473    values(474        clEnumValN(OffsetEncoding::UTF8, "utf-8", "Offsets are in UTF-8 bytes"),475        clEnumValN(OffsetEncoding::UTF16, "utf-16",476                   "Offsets are in UTF-16 code units"),477        clEnumValN(OffsetEncoding::UTF32, "utf-32",478                   "Offsets are in unicode codepoints")),479    init(OffsetEncoding::UnsupportedEncoding),480};481 482opt<bool> PrettyPrint{483    "pretty",484    cat(Protocol),485    desc("Pretty-print JSON output"),486    init(false),487};488 489opt<bool> EnableConfig{490    "enable-config",491    cat(Misc),492    desc(493        "Read user and project configuration from YAML files.\n"494        "Project config is from a .clangd file in the project directory.\n"495        "User config is from clangd/config.yaml in the following directories:\n"496        "\tWindows: %USERPROFILE%\\AppData\\Local\n"497        "\tMac OS: ~/Library/Preferences/\n"498        "\tOthers: $XDG_CONFIG_HOME, usually ~/.config\n"499        "Configuration is documented at https://clangd.llvm.org/config.html"),500    init(true),501};502 503opt<bool> UseDirtyHeaders{"use-dirty-headers", cat(Misc),504                          desc("Use files open in the editor when parsing "505                               "headers instead of reading from the disk"),506                          Hidden,507                          init(ClangdServer::Options().UseDirtyHeaders)};508 509opt<bool> PreambleParseForwardingFunctions{510    "parse-forwarding-functions",511    cat(Misc),512    desc("Parse all emplace-like functions in included headers"),513    Hidden,514    init(ParseOptions().PreambleParseForwardingFunctions),515};516 517#if defined(__GLIBC__) && CLANGD_MALLOC_TRIM518opt<bool> EnableMallocTrim{519    "malloc-trim",520    cat(Misc),521    desc("Release memory periodically via malloc_trim(3)."),522    init(true),523};524 525std::function<void()> getMemoryCleanupFunction() {526  if (!EnableMallocTrim)527    return nullptr;528  // Leave a few MB at the top of the heap: it is insignificant529  // and will most likely be needed by the main thread530  constexpr size_t MallocTrimPad = 20'000'000;531  return []() {532    if (malloc_trim(MallocTrimPad))533      vlog("Released memory via malloc_trim");534  };535}536#else537std::function<void()> getMemoryCleanupFunction() { return nullptr; }538#endif539 540#if CLANGD_ENABLE_REMOTE541opt<std::string> RemoteIndexAddress{542    "remote-index-address",543    cat(Features),544    desc("Address of the remote index server"),545};546 547// FIXME(kirillbobyrev): Should this be the location of compile_commands.json?548opt<std::string> ProjectRoot{549    "project-root",550    cat(Features),551    desc("Path to the project root. Requires remote-index-address to be set."),552};553#endif554 555opt<bool> ExperimentalModulesSupport{556    "experimental-modules-support",557    cat(Features),558    desc("Experimental support for standard c++ modules"),559    init(false),560};561 562/// Supports a test URI scheme with relaxed constraints for lit tests.563/// The path in a test URI will be combined with a platform-specific fake564/// directory to form an absolute path. For example, test:///a.cpp is resolved565/// C:\clangd-test\a.cpp on Windows and /clangd-test/a.cpp on Unix.566class TestScheme : public URIScheme {567public:568  llvm::Expected<std::string>569  getAbsolutePath(llvm::StringRef /*Authority*/, llvm::StringRef Body,570                  llvm::StringRef /*HintPath*/) const override {571    using namespace llvm::sys;572    // Still require "/" in body to mimic file scheme, as we want lengths of an573    // equivalent URI in both schemes to be the same.574    if (!Body.starts_with("/"))575      return error(576          "Expect URI body to be an absolute path starting with '/': {0}",577          Body);578    Body = Body.ltrim('/');579    llvm::SmallString<16> Path(Body);580    path::native(Path);581    path::make_absolute(TestScheme::TestDir, Path);582    return std::string(Path);583  }584 585  llvm::Expected<URI>586  uriFromAbsolutePath(llvm::StringRef AbsolutePath) const override {587    llvm::StringRef Body = AbsolutePath;588    if (!Body.consume_front(TestScheme::TestDir))589      return error("Path {0} doesn't start with root {1}", AbsolutePath,590                   TestDir);591 592    return URI("test", /*Authority=*/"",593               llvm::sys::path::convert_to_slash(Body));594  }595 596private:597  const static char TestDir[];598};599 600#ifdef _WIN32601const char TestScheme::TestDir[] = "C:\\clangd-test";602#else603const char TestScheme::TestDir[] = "/clangd-test";604#endif605 606std::unique_ptr<SymbolIndex>607loadExternalIndex(const Config::ExternalIndexSpec &External,608                  AsyncTaskRunner *Tasks, bool SupportContainedRefs) {609  static const trace::Metric RemoteIndexUsed("used_remote_index",610                                             trace::Metric::Value, "address");611  switch (External.Kind) {612  case Config::ExternalIndexSpec::None:613    break;614  case Config::ExternalIndexSpec::Server:615    RemoteIndexUsed.record(1, External.Location);616    log("Associating {0} with remote index at {1}.", External.MountPoint,617        External.Location);618    return remote::getClient(External.Location, External.MountPoint);619  case Config::ExternalIndexSpec::File:620    log("Associating {0} with monolithic index at {1}.", External.MountPoint,621        External.Location);622    auto NewIndex = std::make_unique<SwapIndex>(std::make_unique<MemIndex>());623    auto IndexLoadTask = [File = External.Location,624                          PlaceHolder = NewIndex.get(), SupportContainedRefs] {625      if (auto Idx = loadIndex(File, SymbolOrigin::Static, /*UseDex=*/true,626                               SupportContainedRefs))627        PlaceHolder->reset(std::move(Idx));628    };629    if (Tasks) {630      Tasks->runAsync("Load-index:" + External.Location,631                      std::move(IndexLoadTask));632    } else {633      IndexLoadTask();634    }635    return std::move(NewIndex);636  }637  llvm_unreachable("Invalid ExternalIndexKind.");638}639 640std::optional<bool> shouldEnableFunctionArgSnippets() {641  std::string Val = EnableFunctionArgSnippets;642  // Accept the same values that a bool option parser would, but also accept643  // -1 to indicate "unspecified", in which case the ArgumentListsPolicy644  // config option will be respected.645  if (Val == "1" || Val == "true" || Val == "True" || Val == "TRUE")646    return true;647  if (Val == "0" || Val == "false" || Val == "False" || Val == "FALSE")648    return false;649  if (Val != "-1")650    elog("Value specified by --function-arg-placeholders is invalid. Provide a "651         "boolean value or leave unspecified to use ArgumentListsPolicy from "652         "config instead.");653  return std::nullopt;654}655 656class FlagsConfigProvider : public config::Provider {657private:658  config::CompiledFragment Frag;659 660  std::vector<config::CompiledFragment>661  getFragments(const config::Params &,662               config::DiagnosticCallback) const override {663    return {Frag};664  }665 666public:667  FlagsConfigProvider() {668    std::optional<Config::CDBSearchSpec> CDBSearch;669    std::optional<Config::ExternalIndexSpec> IndexSpec;670    std::optional<Config::BackgroundPolicy> BGPolicy;671    std::optional<Config::ArgumentListsPolicy> ArgumentLists;672 673    // If --compile-commands-dir arg was invoked, check value and override674    // default path.675    if (!CompileCommandsDir.empty()) {676      if (llvm::sys::fs::exists(CompileCommandsDir)) {677        // We support passing both relative and absolute paths to the678        // --compile-commands-dir argument, but we assume the path is absolute679        // in the rest of clangd so we make sure the path is absolute before680        // continuing.681        llvm::SmallString<128> Path(CompileCommandsDir);682        if (std::error_code EC = llvm::sys::fs::make_absolute(Path)) {683          elog("Error while converting the relative path specified by "684               "--compile-commands-dir to an absolute path: {0}. The argument "685               "will be ignored.",686               EC.message());687        } else {688          CDBSearch = {Config::CDBSearchSpec::FixedDir, Path.str().str()};689        }690      } else {691        elog("Path specified by --compile-commands-dir does not exist. The "692             "argument will be ignored.");693      }694    }695    if (!IndexFile.empty()) {696      Config::ExternalIndexSpec Spec;697      Spec.Kind = Spec.File;698      Spec.Location = IndexFile;699      IndexSpec = std::move(Spec);700    }701#if CLANGD_ENABLE_REMOTE702    if (!RemoteIndexAddress.empty()) {703      assert(!ProjectRoot.empty() && IndexFile.empty());704      Config::ExternalIndexSpec Spec;705      Spec.Kind = Spec.Server;706      Spec.Location = RemoteIndexAddress;707      Spec.MountPoint = ProjectRoot;708      IndexSpec = std::move(Spec);709      BGPolicy = Config::BackgroundPolicy::Skip;710    }711#endif712    if (!EnableBackgroundIndex) {713      BGPolicy = Config::BackgroundPolicy::Skip;714    }715 716    if (std::optional<bool> Enable = shouldEnableFunctionArgSnippets()) {717      ArgumentLists = *Enable ? Config::ArgumentListsPolicy::FullPlaceholders718                              : Config::ArgumentListsPolicy::Delimiters;719    }720 721    Frag = [=](const config::Params &, Config &C) {722      if (CDBSearch)723        C.CompileFlags.CDBSearch = *CDBSearch;724      if (IndexSpec)725        C.Index.External = *IndexSpec;726      if (BGPolicy)727        C.Index.Background = *BGPolicy;728      if (ArgumentLists)729        C.Completion.ArgumentLists = *ArgumentLists;730      if (HeaderInsertion.getNumOccurrences())731        C.Completion.HeaderInsertion = HeaderInsertion;732      if (AllScopesCompletion.getNumOccurrences())733        C.Completion.AllScopes = AllScopesCompletion;734 735      if (Test)736        C.Index.StandardLibrary = false;737      return true;738    };739  }740};741} // namespace742 743enum class ErrorResultCode : int {744  NoShutdownRequest = 1,745  CantRunAsXPCService = 2,746  CheckFailed = 3747};748 749int clangdMain(int argc, char *argv[]) {750  // Clang could run on the main thread. e.g., when the flag '-check' or '-sync'751  // is enabled.752  clang::noteBottomOfStack();753  llvm::InitLLVM X(argc, argv);754  llvm::InitializeAllTargetInfos();755  llvm::sys::AddSignalHandler(756      [](void *) {757        ThreadCrashReporter::runCrashHandlers();758        // Ensure ThreadCrashReporter and PrintStackTrace output is visible.759        llvm::errs().flush();760      },761      nullptr);762  llvm::sys::SetInterruptFunction(&requestShutdown);763  llvm::cl::SetVersionPrinter([](llvm::raw_ostream &OS) {764    OS << versionString() << "\n"765       << "Features: " << featureString() << "\n"766       << "Platform: " << platformString() << "\n";767  });768  const char *FlagsEnvVar = "CLANGD_FLAGS";769  const char *Overview =770      R"(clangd is a language server that provides IDE-like features to editors.771 772It should be used via an editor plugin rather than invoked directly. For more information, see:773	https://clangd.llvm.org/774	https://microsoft.github.io/language-server-protocol/775 776clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment variable.777)";778  llvm::cl::HideUnrelatedOptions(ClangdCategories);779  llvm::cl::ParseCommandLineOptions(argc, argv, Overview, /*Errs=*/nullptr,780                                    /*VFS=*/nullptr, FlagsEnvVar);781  if (Test) {782    if (!Sync.getNumOccurrences())783      Sync = true;784    if (!CrashPragmas.getNumOccurrences())785      CrashPragmas = true;786    InputStyle = JSONStreamStyle::Delimited;787    LogLevel = Logger::Verbose;788    PrettyPrint = true;789    // Disable config system by default to avoid external reads.790    if (!EnableConfig.getNumOccurrences())791      EnableConfig = false;792    // Disable background index on lit tests by default to prevent disk writes.793    if (!EnableBackgroundIndex.getNumOccurrences())794      EnableBackgroundIndex = false;795    // Ensure background index makes progress.796    else if (EnableBackgroundIndex)797      BackgroundQueue::preventThreadStarvationInTests();798  }799  if (Test || EnableTestScheme) {800    static URISchemeRegistry::Add<TestScheme> X(801        "test", "Test scheme for clangd lit tests.");802  }803  if (CrashPragmas)804    allowCrashPragmasForTest();805 806  if (!Sync && WorkerThreadsCount == 0) {807    llvm::errs() << "A number of worker threads cannot be 0. Did you mean to "808                    "specify -sync?";809    return 1;810  }811 812  if (Sync) {813    if (WorkerThreadsCount.getNumOccurrences())814      llvm::errs() << "Ignoring -j because -sync is set.\n";815    WorkerThreadsCount = 0;816  }817  if (FallbackStyle.getNumOccurrences())818    clang::format::DefaultFallbackStyle = FallbackStyle.c_str();819 820  // Validate command line arguments.821  std::optional<llvm::raw_fd_ostream> InputMirrorStream;822  if (!InputMirrorFile.empty()) {823    std::error_code EC;824    InputMirrorStream.emplace(InputMirrorFile, /*ref*/ EC,825                              llvm::sys::fs::FA_Read | llvm::sys::fs::FA_Write);826    if (EC) {827      InputMirrorStream.reset();828      llvm::errs() << "Error while opening an input mirror file: "829                   << EC.message();830    } else {831      InputMirrorStream->SetUnbuffered();832    }833  }834 835#if !CLANGD_DECISION_FOREST836  if (RankingModel == clangd::CodeCompleteOptions::DecisionForest) {837    llvm::errs() << "Clangd was compiled without decision forest support.\n";838    return 1;839  }840#endif841 842  // Setup tracing facilities if CLANGD_TRACE is set. In practice enabling a843  // trace flag in your editor's config is annoying, launching with844  // `CLANGD_TRACE=trace.json vim` is easier.845  std::optional<llvm::raw_fd_ostream> TracerStream;846  std::unique_ptr<trace::EventTracer> Tracer;847  const char *JSONTraceFile = getenv("CLANGD_TRACE");848  const char *MetricsCSVFile = getenv("CLANGD_METRICS");849  const char *TracerFile = JSONTraceFile ? JSONTraceFile : MetricsCSVFile;850  if (TracerFile) {851    std::error_code EC;852    TracerStream.emplace(TracerFile, /*ref*/ EC,853                         llvm::sys::fs::FA_Read | llvm::sys::fs::FA_Write);854    if (EC) {855      TracerStream.reset();856      llvm::errs() << "Error while opening trace file " << TracerFile << ": "857                   << EC.message();858    } else {859      Tracer = (TracerFile == JSONTraceFile)860                   ? trace::createJSONTracer(*TracerStream, PrettyPrint)861                   : trace::createCSVMetricTracer(*TracerStream);862    }863  }864 865  std::optional<trace::Session> TracingSession;866  if (Tracer)867    TracingSession.emplace(*Tracer);868 869  // If a user ran `clangd` in a terminal without redirecting anything,870  // it's somewhat likely they're confused about how to use clangd.871  // Show them the help overview, which explains.872  if (llvm::outs().is_displayed() && llvm::errs().is_displayed() &&873      !CheckFile.getNumOccurrences())874    llvm::errs() << Overview << "\n";875  // Use buffered stream to stderr (we still flush each log message). Unbuffered876  // stream can cause significant (non-deterministic) latency for the logger.877  llvm::errs().SetBuffered();878  StreamLogger Logger(llvm::errs(), LogLevel);879  LoggingSession LoggingSession(Logger);880  // Write some initial logs before we start doing any real work.881  log("{0}", versionString());882  log("Features: {0}", featureString());883  log("PID: {0}", llvm::sys::Process::getProcessId());884  {885    SmallString<128> CWD;886    if (auto Err = llvm::sys::fs::current_path(CWD))887      log("Working directory unknown: {0}", Err.message());888    else889      log("Working directory: {0}", CWD);890  }891  for (int I = 0; I < argc; ++I)892    log("argv[{0}]: {1}", I, argv[I]);893  if (auto EnvFlags = llvm::sys::Process::GetEnv(FlagsEnvVar))894    log("{0}: {1}", FlagsEnvVar, *EnvFlags);895 896  ClangdLSPServer::Options Opts;897  Opts.UseDirBasedCDB = (CompileArgsFrom == FilesystemCompileArgs);898  Opts.EnableExperimentalModulesSupport = ExperimentalModulesSupport;899 900  switch (PCHStorage) {901  case PCHStorageFlag::Memory:902    Opts.StorePreamblesInMemory = true;903    break;904  case PCHStorageFlag::Disk:905    Opts.StorePreamblesInMemory = false;906    break;907  }908  if (!ResourceDir.empty())909    Opts.ResourceDir = ResourceDir;910  Opts.BuildDynamicSymbolIndex = true;911#if CLANGD_ENABLE_REMOTE912  if (RemoteIndexAddress.empty() != ProjectRoot.empty()) {913    llvm::errs() << "remote-index-address and project-path have to be "914                    "specified at the same time.";915    return 1;916  }917  if (!RemoteIndexAddress.empty()) {918    if (IndexFile.empty()) {919      log("Connecting to remote index at {0}", RemoteIndexAddress);920    } else {921      elog("When enabling remote index, IndexFile should not be specified. "922           "Only one can be used at time. Remote index will ignored.");923    }924  }925#endif926  Opts.BackgroundIndex = EnableBackgroundIndex;927  Opts.BackgroundIndexPriority = BackgroundIndexPriority;928  Opts.ReferencesLimit = ReferencesLimit;929  Opts.Rename.LimitFiles = RenameFileLimit;930  auto PAI = createProjectAwareIndex(931      [SupportContainedRefs = Opts.EnableOutgoingCalls](932          const Config::ExternalIndexSpec &External, AsyncTaskRunner *Tasks) {933        return loadExternalIndex(External, Tasks, SupportContainedRefs);934      },935      Sync);936  Opts.StaticIndex = PAI.get();937  Opts.AsyncThreadsCount = WorkerThreadsCount;938  Opts.MemoryCleanup = getMemoryCleanupFunction();939 940  Opts.CodeComplete.IncludeIneligibleResults = IncludeIneligibleResults;941  Opts.CodeComplete.Limit = LimitResults;942  if (CompletionStyle.getNumOccurrences())943    Opts.CodeComplete.BundleOverloads = CompletionStyle != Detailed;944  Opts.CodeComplete.ShowOrigins = ShowOrigins;945  Opts.CodeComplete.InsertIncludes = HeaderInsertion;946  Opts.CodeComplete.ImportInsertions = ImportInsertions;947  if (!HeaderInsertionDecorators) {948    Opts.CodeComplete.IncludeIndicator.Insert.clear();949    Opts.CodeComplete.IncludeIndicator.NoInsert.clear();950  }951  Opts.CodeComplete.RunParser = CodeCompletionParse;952  Opts.CodeComplete.RankingModel = RankingModel;953  // FIXME: If we're using C++20 modules, force the lookup process to load954  // external decls, since currently the index doesn't support C++20 modules.955  Opts.CodeComplete.ForceLoadPreamble = ExperimentalModulesSupport;956 957  RealThreadsafeFS TFS;958  std::vector<std::unique_ptr<config::Provider>> ProviderStack;959  std::unique_ptr<config::Provider> Config;960  if (EnableConfig) {961    ProviderStack.push_back(962        config::Provider::fromAncestorRelativeYAMLFiles(".clangd", TFS));963    llvm::SmallString<256> UserConfig;964    if (llvm::sys::path::user_config_directory(UserConfig)) {965      llvm::sys::path::append(UserConfig, "clangd", "config.yaml");966      vlog("User config file is {0}", UserConfig);967      ProviderStack.push_back(config::Provider::fromYAMLFile(968          UserConfig, /*Directory=*/"", TFS, /*Trusted=*/true));969    } else {970      elog("Couldn't determine user config file, not loading");971    }972  }973  ProviderStack.push_back(std::make_unique<FlagsConfigProvider>());974  std::vector<const config::Provider *> ProviderPointers;975  for (const auto &P : ProviderStack)976    ProviderPointers.push_back(P.get());977  Config = config::Provider::combine(std::move(ProviderPointers));978  Opts.ConfigProvider = Config.get();979 980  // Create an empty clang-tidy option.981  TidyProvider ClangTidyOptProvider;982  if (EnableClangTidy) {983    std::vector<TidyProvider> Providers;984    Providers.reserve(4 + EnableConfig);985    Providers.push_back(provideEnvironment());986    Providers.push_back(provideClangTidyFiles(TFS));987    if (EnableConfig)988      Providers.push_back(provideClangdConfig());989    Providers.push_back(provideDefaultChecks());990    Providers.push_back(disableUnusableChecks());991    ClangTidyOptProvider = combine(std::move(Providers));992    Opts.ClangTidyProvider = ClangTidyOptProvider;993  }994  Opts.UseDirtyHeaders = UseDirtyHeaders;995  Opts.PreambleParseForwardingFunctions = PreambleParseForwardingFunctions;996  Opts.ImportInsertions = ImportInsertions;997  Opts.QueryDriverGlobs = std::move(QueryDriverGlobs);998  Opts.TweakFilter = [&](const Tweak &T) {999    if (T.hidden() && !HiddenFeatures)1000      return false;1001    if (TweakList.getNumOccurrences())1002      return llvm::is_contained(TweakList, T.id());1003    return true;1004  };1005  if (ForceOffsetEncoding != OffsetEncoding::UnsupportedEncoding)1006    Opts.Encoding = ForceOffsetEncoding;1007 1008  if (CheckFile.getNumOccurrences()) {1009    llvm::SmallString<256> Path;1010    if (auto Error =1011            llvm::sys::fs::real_path(CheckFile, Path, /*expand_tilde=*/true)) {1012      elog("Failed to resolve path {0}: {1}", CheckFile, Error.message());1013      return 1;1014    }1015    log("Entering check mode (no LSP server)");1016    return check(Path, TFS, Opts)1017               ? 01018               : static_cast<int>(ErrorResultCode::CheckFailed);1019  }1020 1021  FeatureModuleSet ModuleSet = FeatureModuleSet::fromRegistry();1022  if (ModuleSet.begin() != ModuleSet.end())1023    Opts.FeatureModules = &ModuleSet;1024 1025  // Initialize and run ClangdLSPServer.1026  // Change stdin to binary to not lose \r\n on windows.1027  llvm::sys::ChangeStdinToBinary();1028  std::unique_ptr<Transport> TransportLayer;1029  if (getenv("CLANGD_AS_XPC_SERVICE")) {1030#if CLANGD_BUILD_XPC1031    log("Starting LSP over XPC service");1032    TransportLayer = newXPCTransport();1033#else1034    llvm::errs() << "This clangd binary wasn't built with XPC support.\n";1035    return static_cast<int>(ErrorResultCode::CantRunAsXPCService);1036#endif1037  } else {1038    log("Starting LSP over stdin/stdout");1039    TransportLayer = newJSONTransport(1040        stdin, llvm::outs(), InputMirrorStream ? &*InputMirrorStream : nullptr,1041        PrettyPrint, InputStyle);1042  }1043  if (!PathMappingsArg.empty()) {1044    auto Mappings = parsePathMappings(PathMappingsArg);1045    if (!Mappings) {1046      elog("Invalid -path-mappings: {0}", Mappings.takeError());1047      return 1;1048    }1049    TransportLayer = createPathMappingTransport(std::move(TransportLayer),1050                                                std::move(*Mappings));1051  }1052 1053  ClangdLSPServer LSPServer(*TransportLayer, TFS, Opts);1054  llvm::set_thread_name("clangd.main");1055  int ExitCode = LSPServer.run()1056                     ? 01057                     : static_cast<int>(ErrorResultCode::NoShutdownRequest);1058  log("LSP finished, exiting with status {0}", ExitCode);1059 1060  // There may still be lingering background threads (e.g. slow requests1061  // whose results will be dropped, background index shutting down).1062  //1063  // These should terminate quickly, and ~ClangdLSPServer blocks on them.1064  // However if a bug causes them to run forever, we want to ensure the process1065  // eventually exits. As clangd isn't directly user-facing, an editor can1066  // "leak" clangd processes. Crashing in this case contains the damage.1067  abortAfterTimeout(std::chrono::minutes(5));1068 1069  return ExitCode;1070}1071 1072} // namespace clangd1073} // namespace clang1074