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