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//===----------------------------------------------------------------------===//
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 "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>
54
55#ifndef _WIN32
56#include <unistd.h>
57#endif
58
59#ifdef __GLIBC__
60#include <malloc.h>
61#endif
62
63namespace clang {
64namespace clangd {
65
66// Implemented in Check.cpp.
67bool check(const llvm::StringRef File, const ThreadsafeFS &TFS,
68 const ClangdLSPServer::Options &Opts);
69
70namespace {
71
72using llvm::cl::cat;
73using llvm::cl::CommaSeparated;
74using llvm::cl::desc;
75using llvm::cl::Hidden;
76using llvm::cl::init;
77using llvm::cl::list;
78using llvm::cl::opt;
79using llvm::cl::OptionCategory;
80using llvm::cl::ValueOptional;
81using llvm::cl::values;
82
83// All flags must be placed in a category, or they will be shown neither in
84// --help, nor --help-hidden!
85OptionCategory CompileCommands("clangd compilation flags options");
86OptionCategory Features("clangd feature options");
87OptionCategory Misc("clangd miscellaneous options");
88OptionCategory Protocol("clangd protocol and logging options");
89OptionCategory Retired("clangd flags no longer in use");
90const OptionCategory *ClangdCategories[] = {&Features, &Protocol,
91 &CompileCommands, &Misc, &Retired};
92
93template <typename T> class RetiredFlag {
94 opt<T> Option;
95
96public:
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 })) {}
103};
104
105enum CompileArgsFrom { LSPCompileArgs, FilesystemCompileArgs };
106opt<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(Val: FilesystemCompileArgs),
117 Hidden,
118};
119
120opt<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"),
126};
127
128opt<Path> ResourceDir{
129 "resource-dir",
130 cat(CompileCommands),
131 desc("Directory for system clang headers"),
132 init(Val: ""),
133 Hidden,
134};
135
136list<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,
145};
146
147// FIXME: Flags are the wrong mechanism for user preferences.
148// We should probably read a dotfile or similar.
149opt<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(Val: true),
157};
158
159opt<bool> ShowOrigins{
160 "debug-origin",
161 cat(Features),
162 desc("Show origins of completion items"),
163 init(Val: CodeCompleteOptions().ShowOrigins),
164 Hidden,
165};
166
167opt<bool> EnableBackgroundIndex{
168 "background-index",
169 cat(Features),
170 desc("Index project code in the background and persist index on disk."),
171 init(Val: true),
172};
173
174opt<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(Val: llvm::ThreadPriority::Low),
187};
188
189opt<bool> EnableClangTidy{
190 "clang-tidy",
191 cat(Features),
192 desc("Enable clang-tidy diagnostics"),
193 init(Val: true),
194};
195
196opt<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(Val: CodeCompleteOptions().RunParser),
208 Hidden,
209};
210
211opt<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(Val: CodeCompleteOptions().RankingModel),
220 Hidden,
221};
222
223// FIXME: also support "plain" style where signatures are always omitted.
224enum CompletionStyleFlag { Detailed, Bundled };
225opt<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")),
235};
236
237opt<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(Val: clang::format::DefaultFallbackStyle),
243};
244
245opt<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(Val: CodeCompleteOptions().EnableFunctionArgSnippets),
252};
253
254opt<CodeCompleteOptions::IncludeInsertion> HeaderInsertion{
255 "header-insertion",
256 cat(Features),
257 desc("Add #include directives when accepting code completions"),
258 init(Val: 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")),
268};
269
270opt<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(Val: CodeCompleteOptions().ImportInsertions),
276};
277
278opt<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(Val: true),
285};
286
287opt<bool> HiddenFeatures{
288 "hidden-features",
289 cat(Features),
290 desc("Enable hidden features mostly useful to clangd developers"),
291 init(Val: false),
292 Hidden,
293};
294
295opt<bool> IncludeIneligibleResults{
296 "include-ineligible-results",
297 cat(Features),
298 desc("Include ineligible completion results (e.g. private members)"),
299 init(Val: CodeCompleteOptions().IncludeIneligibleResults),
300 Hidden,
301};
302
303RetiredFlag<bool> EnableIndex("index");
304RetiredFlag<bool> SuggestMissingIncludes("suggest-missing-includes");
305RetiredFlag<bool> RecoveryAST("recovery-ast");
306RetiredFlag<bool> RecoveryASTType("recovery-ast-type");
307RetiredFlag<bool> AsyncPreamble("async-preamble");
308RetiredFlag<bool> CollectMainFileRefs("collect-main-file-refs");
309RetiredFlag<bool> CrossFileRename("cross-file-rename");
310RetiredFlag<std::string> ClangTidyChecks("clang-tidy-checks");
311RetiredFlag<bool> InlayHints("inlay-hints");
312RetiredFlag<bool> FoldingRanges("folding-ranges");
313RetiredFlag<bool> IncludeCleanerStdlib("include-cleaner-stdlib");
314
315opt<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(Val: 100),
321};
322
323opt<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(Val: 1000),
329};
330
331opt<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(Val: 50),
337};
338
339list<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,
345};
346
347opt<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(Val: getDefaultAsyncThreadsCount()),
353};
354
355opt<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(Val: ""),
364 Hidden,
365};
366
367opt<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(Val: false),
375 Hidden,
376};
377
378opt<bool> CrashPragmas{
379 "crash-pragmas",
380 cat(Misc),
381 desc("Respect `#pragma clang __debug crash` and friends."),
382 init(Val: false),
383 Hidden,
384};
385
386opt<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(Val: ""),
393 ValueOptional,
394};
395
396enum PCHStorageFlag { Disk, Memory };
397opt<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(Val: PCHStorageFlag::Disk),
406};
407
408opt<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(Val: false),
414 Hidden,
415};
416
417opt<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(Val: JSONStreamStyle::Standard),
426 Hidden,
427};
428
429opt<bool> EnableTestScheme{
430 "enable-test-uri-scheme",
431 cat(Protocol),
432 desc("Enable 'test:' URI scheme. Only use in lit tests"),
433 init(Val: false),
434 Hidden,
435};
436
437opt<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(Val: ""),
447};
448
449opt<Path> InputMirrorFile{
450 "input-mirror-file",
451 cat(Protocol),
452 desc("Mirror all LSP input to the specified file. Useful for debugging"),
453 init(Val: ""),
454 Hidden,
455};
456
457opt<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(Val: Logger::Info),
465};
466
467opt<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(Val: OffsetEncoding::UnsupportedEncoding),
479};
480
481opt<bool> PrettyPrint{
482 "pretty",
483 cat(Protocol),
484 desc("Pretty-print JSON output"),
485 init(Val: false),
486};
487
488opt<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(Val: true),
500};
501
502opt<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(Val: ClangdServer::Options().UseDirtyHeaders)};
507
508opt<bool> PreambleParseForwardingFunctions{
509 "parse-forwarding-functions",
510 cat(Misc),
511 desc("Parse all emplace-like functions in included headers"),
512 Hidden,
513 init(Val: ParseOptions().PreambleParseForwardingFunctions),
514};
515
516#if defined(__GLIBC__) && CLANGD_MALLOC_TRIM
517opt<bool> EnableMallocTrim{
518 "malloc-trim",
519 cat(Misc),
520 desc("Release memory periodically via malloc_trim(3)."),
521 init(Val: true),
522};
523
524std::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(pad: MallocTrimPad))
532 vlog(Fmt: "Released memory via malloc_trim");
533 };
534}
535#else
536std::function<void()> getMemoryCleanupFunction() { return nullptr; }
537#endif
538
539#if CLANGD_ENABLE_REMOTE
540opt<std::string> RemoteIndexAddress{
541 "remote-index-address",
542 cat(Features),
543 desc("Address of the remote index server"),
544};
545
546// FIXME(kirillbobyrev): Should this be the location of compile_commands.json?
547opt<std::string> ProjectRoot{
548 "project-root",
549 cat(Features),
550 desc("Path to the project root. Requires remote-index-address to be set."),
551};
552#endif
553
554/// Supports a test URI scheme with relaxed constraints for lit tests.
555/// The path in a test URI will be combined with a platform-specific fake
556/// directory to form an absolute path. For example, test:///a.cpp is resolved
557/// C:\clangd-test\a.cpp on Windows and /clangd-test/a.cpp on Unix.
558class TestScheme : public URIScheme {
559public:
560 llvm::Expected<std::string>
561 getAbsolutePath(llvm::StringRef /*Authority*/, llvm::StringRef Body,
562 llvm::StringRef /*HintPath*/) const override {
563 using namespace llvm::sys;
564 // Still require "/" in body to mimic file scheme, as we want lengths of an
565 // equivalent URI in both schemes to be the same.
566 if (!Body.starts_with(Prefix: "/"))
567 return error(
568 Fmt: "Expect URI body to be an absolute path starting with '/': {0}",
569 Vals&: Body);
570 Body = Body.ltrim(Char: '/');
571 llvm::SmallString<16> Path(Body);
572 path::native(path&: Path);
573 fs::make_absolute(current_directory: TestScheme::TestDir, path&: Path);
574 return std::string(Path);
575 }
576
577 llvm::Expected<URI>
578 uriFromAbsolutePath(llvm::StringRef AbsolutePath) const override {
579 llvm::StringRef Body = AbsolutePath;
580 if (!Body.consume_front(Prefix: TestScheme::TestDir))
581 return error(Fmt: "Path {0} doesn't start with root {1}", Vals&: AbsolutePath,
582 Vals: TestDir);
583
584 return URI("test", /*Authority=*/"",
585 llvm::sys::path::convert_to_slash(path: Body));
586 }
587
588private:
589 const static char TestDir[];
590};
591
592#ifdef _WIN32
593const char TestScheme::TestDir[] = "C:\\clangd-test";
594#else
595const char TestScheme::TestDir[] = "/clangd-test";
596#endif
597
598std::unique_ptr<SymbolIndex>
599loadExternalIndex(const Config::ExternalIndexSpec &External,
600 AsyncTaskRunner *Tasks) {
601 static const trace::Metric RemoteIndexUsed("used_remote_index",
602 trace::Metric::Value, "address");
603 switch (External.Kind) {
604 case Config::ExternalIndexSpec::None:
605 break;
606 case Config::ExternalIndexSpec::Server:
607 RemoteIndexUsed.record(Value: 1, Label: External.Location);
608 log(Fmt: "Associating {0} with remote index at {1}.", Vals: External.MountPoint,
609 Vals: External.Location);
610 return remote::getClient(Address: External.Location, IndexRoot: External.MountPoint);
611 case Config::ExternalIndexSpec::File:
612 log(Fmt: "Associating {0} with monolithic index at {1}.", Vals: External.MountPoint,
613 Vals: External.Location);
614 auto NewIndex = std::make_unique<SwapIndex>(args: std::make_unique<MemIndex>());
615 auto IndexLoadTask = [File = External.Location,
616 PlaceHolder = NewIndex.get()] {
617 if (auto Idx = loadIndex(Filename: File, Origin: SymbolOrigin::Static, /*UseDex=*/true))
618 PlaceHolder->reset(std::move(Idx));
619 };
620 if (Tasks) {
621 Tasks->runAsync(Name: "Load-index:" + External.Location,
622 Action: std::move(IndexLoadTask));
623 } else {
624 IndexLoadTask();
625 }
626 return std::move(NewIndex);
627 }
628 llvm_unreachable("Invalid ExternalIndexKind.");
629}
630
631class FlagsConfigProvider : public config::Provider {
632private:
633 config::CompiledFragment Frag;
634
635 std::vector<config::CompiledFragment>
636 getFragments(const config::Params &,
637 config::DiagnosticCallback) const override {
638 return {Frag};
639 }
640
641public:
642 FlagsConfigProvider() {
643 std::optional<Config::CDBSearchSpec> CDBSearch;
644 std::optional<Config::ExternalIndexSpec> IndexSpec;
645 std::optional<Config::BackgroundPolicy> BGPolicy;
646
647 // If --compile-commands-dir arg was invoked, check value and override
648 // default path.
649 if (!CompileCommandsDir.empty()) {
650 if (llvm::sys::fs::exists(Path: CompileCommandsDir)) {
651 // We support passing both relative and absolute paths to the
652 // --compile-commands-dir argument, but we assume the path is absolute
653 // in the rest of clangd so we make sure the path is absolute before
654 // continuing.
655 llvm::SmallString<128> Path(CompileCommandsDir);
656 if (std::error_code EC = llvm::sys::fs::make_absolute(path&: Path)) {
657 elog(Fmt: "Error while converting the relative path specified by "
658 "--compile-commands-dir to an absolute path: {0}. The argument "
659 "will be ignored.",
660 Vals: EC.message());
661 } else {
662 CDBSearch = {.Policy: Config::CDBSearchSpec::FixedDir, .FixedCDBPath: Path.str().str()};
663 }
664 } else {
665 elog(Fmt: "Path specified by --compile-commands-dir does not exist. The "
666 "argument will be ignored.");
667 }
668 }
669 if (!IndexFile.empty()) {
670 Config::ExternalIndexSpec Spec;
671 Spec.Kind = Spec.File;
672 Spec.Location = IndexFile;
673 IndexSpec = std::move(Spec);
674 }
675#if CLANGD_ENABLE_REMOTE
676 if (!RemoteIndexAddress.empty()) {
677 assert(!ProjectRoot.empty() && IndexFile.empty());
678 Config::ExternalIndexSpec Spec;
679 Spec.Kind = Spec.Server;
680 Spec.Location = RemoteIndexAddress;
681 Spec.MountPoint = ProjectRoot;
682 IndexSpec = std::move(Spec);
683 BGPolicy = Config::BackgroundPolicy::Skip;
684 }
685#endif
686 if (!EnableBackgroundIndex) {
687 BGPolicy = Config::BackgroundPolicy::Skip;
688 }
689
690 Frag = [=](const config::Params &, Config &C) {
691 if (CDBSearch)
692 C.CompileFlags.CDBSearch = *CDBSearch;
693 if (IndexSpec)
694 C.Index.External = *IndexSpec;
695 if (BGPolicy)
696 C.Index.Background = *BGPolicy;
697 if (AllScopesCompletion.getNumOccurrences())
698 C.Completion.AllScopes = AllScopesCompletion;
699
700 if (Test)
701 C.Index.StandardLibrary = false;
702 return true;
703 };
704 }
705};
706} // namespace
707
708enum class ErrorResultCode : int {
709 NoShutdownRequest = 1,
710 CantRunAsXPCService = 2,
711 CheckFailed = 3
712};
713
714int clangdMain(int argc, char *argv[]) {
715 // Clang could run on the main thread. e.g., when the flag '-check' or '-sync'
716 // is enabled.
717 clang::noteBottomOfStack();
718 llvm::InitLLVM X(argc, argv);
719 llvm::InitializeAllTargetInfos();
720 llvm::sys::AddSignalHandler(
721 FnPtr: [](void *) {
722 ThreadCrashReporter::runCrashHandlers();
723 // Ensure ThreadCrashReporter and PrintStackTrace output is visible.
724 llvm::errs().flush();
725 },
726 Cookie: nullptr);
727 llvm::sys::SetInterruptFunction(&requestShutdown);
728 llvm::cl::SetVersionPrinter([](llvm::raw_ostream &OS) {
729 OS << versionString() << "\n"
730 << "Features: " << featureString() << "\n"
731 << "Platform: " << platformString() << "\n";
732 });
733 const char *FlagsEnvVar = "CLANGD_FLAGS";
734 const char *Overview =
735 R"(clangd is a language server that provides IDE-like features to editors.
736
737It should be used via an editor plugin rather than invoked directly. For more information, see:
738 https://clangd.llvm.org/
739 https://microsoft.github.io/language-server-protocol/
740
741clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment variable.
742)";
743 llvm::cl::HideUnrelatedOptions(Categories: ClangdCategories);
744 llvm::cl::ParseCommandLineOptions(argc, argv, Overview,
745 /*Errs=*/nullptr, EnvVar: FlagsEnvVar);
746 if (Test) {
747 if (!Sync.getNumOccurrences())
748 Sync = true;
749 if (!CrashPragmas.getNumOccurrences())
750 CrashPragmas = true;
751 InputStyle = JSONStreamStyle::Delimited;
752 LogLevel = Logger::Verbose;
753 PrettyPrint = true;
754 // Disable config system by default to avoid external reads.
755 if (!EnableConfig.getNumOccurrences())
756 EnableConfig = false;
757 // Disable background index on lit tests by default to prevent disk writes.
758 if (!EnableBackgroundIndex.getNumOccurrences())
759 EnableBackgroundIndex = false;
760 // Ensure background index makes progress.
761 else if (EnableBackgroundIndex)
762 BackgroundQueue::preventThreadStarvationInTests();
763 }
764 if (Test || EnableTestScheme) {
765 static URISchemeRegistry::Add<TestScheme> X(
766 "test", "Test scheme for clangd lit tests.");
767 }
768 if (CrashPragmas)
769 allowCrashPragmasForTest();
770
771 if (!Sync && WorkerThreadsCount == 0) {
772 llvm::errs() << "A number of worker threads cannot be 0. Did you mean to "
773 "specify -sync?";
774 return 1;
775 }
776
777 if (Sync) {
778 if (WorkerThreadsCount.getNumOccurrences())
779 llvm::errs() << "Ignoring -j because -sync is set.\n";
780 WorkerThreadsCount = 0;
781 }
782 if (FallbackStyle.getNumOccurrences())
783 clang::format::DefaultFallbackStyle = FallbackStyle.c_str();
784
785 // Validate command line arguments.
786 std::optional<llvm::raw_fd_ostream> InputMirrorStream;
787 if (!InputMirrorFile.empty()) {
788 std::error_code EC;
789 InputMirrorStream.emplace(args&: InputMirrorFile, /*ref*/ args&: EC,
790 args: llvm::sys::fs::FA_Read | llvm::sys::fs::FA_Write);
791 if (EC) {
792 InputMirrorStream.reset();
793 llvm::errs() << "Error while opening an input mirror file: "
794 << EC.message();
795 } else {
796 InputMirrorStream->SetUnbuffered();
797 }
798 }
799
800#if !CLANGD_DECISION_FOREST
801 if (RankingModel == clangd::CodeCompleteOptions::DecisionForest) {
802 llvm::errs() << "Clangd was compiled without decision forest support.\n";
803 return 1;
804 }
805#endif
806
807 // Setup tracing facilities if CLANGD_TRACE is set. In practice enabling a
808 // trace flag in your editor's config is annoying, launching with
809 // `CLANGD_TRACE=trace.json vim` is easier.
810 std::optional<llvm::raw_fd_ostream> TracerStream;
811 std::unique_ptr<trace::EventTracer> Tracer;
812 const char *JSONTraceFile = getenv(name: "CLANGD_TRACE");
813 const char *MetricsCSVFile = getenv(name: "CLANGD_METRICS");
814 const char *TracerFile = JSONTraceFile ? JSONTraceFile : MetricsCSVFile;
815 if (TracerFile) {
816 std::error_code EC;
817 TracerStream.emplace(args&: TracerFile, /*ref*/ args&: EC,
818 args: llvm::sys::fs::FA_Read | llvm::sys::fs::FA_Write);
819 if (EC) {
820 TracerStream.reset();
821 llvm::errs() << "Error while opening trace file " << TracerFile << ": "
822 << EC.message();
823 } else {
824 Tracer = (TracerFile == JSONTraceFile)
825 ? trace::createJSONTracer(OS&: *TracerStream, Pretty: PrettyPrint)
826 : trace::createCSVMetricTracer(OS&: *TracerStream);
827 }
828 }
829
830 std::optional<trace::Session> TracingSession;
831 if (Tracer)
832 TracingSession.emplace(args&: *Tracer);
833
834 // If a user ran `clangd` in a terminal without redirecting anything,
835 // it's somewhat likely they're confused about how to use clangd.
836 // Show them the help overview, which explains.
837 if (llvm::outs().is_displayed() && llvm::errs().is_displayed() &&
838 !CheckFile.getNumOccurrences())
839 llvm::errs() << Overview << "\n";
840 // Use buffered stream to stderr (we still flush each log message). Unbuffered
841 // stream can cause significant (non-deterministic) latency for the logger.
842 llvm::errs().SetBuffered();
843 // Don't flush stdout when logging, this would be both slow and racy!
844 llvm::errs().tie(TieTo: nullptr);
845 StreamLogger Logger(llvm::errs(), LogLevel);
846 LoggingSession LoggingSession(Logger);
847 // Write some initial logs before we start doing any real work.
848 log(Fmt: "{0}", Vals: versionString());
849 log(Fmt: "Features: {0}", Vals: featureString());
850 log(Fmt: "PID: {0}", Vals: llvm::sys::Process::getProcessId());
851 {
852 SmallString<128> CWD;
853 if (auto Err = llvm::sys::fs::current_path(result&: CWD))
854 log(Fmt: "Working directory unknown: {0}", Vals: Err.message());
855 else
856 log(Fmt: "Working directory: {0}", Vals&: CWD);
857 }
858 for (int I = 0; I < argc; ++I)
859 log(Fmt: "argv[{0}]: {1}", Vals&: I, Vals&: argv[I]);
860 if (auto EnvFlags = llvm::sys::Process::GetEnv(name: FlagsEnvVar))
861 log(Fmt: "{0}: {1}", Vals&: FlagsEnvVar, Vals&: *EnvFlags);
862
863 ClangdLSPServer::Options Opts;
864 Opts.UseDirBasedCDB = (CompileArgsFrom == FilesystemCompileArgs);
865
866 switch (PCHStorage) {
867 case PCHStorageFlag::Memory:
868 Opts.StorePreamblesInMemory = true;
869 break;
870 case PCHStorageFlag::Disk:
871 Opts.StorePreamblesInMemory = false;
872 break;
873 }
874 if (!ResourceDir.empty())
875 Opts.ResourceDir = ResourceDir;
876 Opts.BuildDynamicSymbolIndex = true;
877 std::vector<std::unique_ptr<SymbolIndex>> IdxStack;
878#if CLANGD_ENABLE_REMOTE
879 if (RemoteIndexAddress.empty() != ProjectRoot.empty()) {
880 llvm::errs() << "remote-index-address and project-path have to be "
881 "specified at the same time.";
882 return 1;
883 }
884 if (!RemoteIndexAddress.empty()) {
885 if (IndexFile.empty()) {
886 log("Connecting to remote index at {0}", RemoteIndexAddress);
887 } else {
888 elog("When enabling remote index, IndexFile should not be specified. "
889 "Only one can be used at time. Remote index will ignored.");
890 }
891 }
892#endif
893 Opts.BackgroundIndex = EnableBackgroundIndex;
894 Opts.BackgroundIndexPriority = BackgroundIndexPriority;
895 Opts.ReferencesLimit = ReferencesLimit;
896 Opts.Rename.LimitFiles = RenameFileLimit;
897 auto PAI = createProjectAwareIndex(loadExternalIndex, Sync);
898 Opts.StaticIndex = PAI.get();
899 Opts.AsyncThreadsCount = WorkerThreadsCount;
900 Opts.MemoryCleanup = getMemoryCleanupFunction();
901
902 Opts.CodeComplete.IncludeIneligibleResults = IncludeIneligibleResults;
903 Opts.CodeComplete.Limit = LimitResults;
904 if (CompletionStyle.getNumOccurrences())
905 Opts.CodeComplete.BundleOverloads = CompletionStyle != Detailed;
906 Opts.CodeComplete.ShowOrigins = ShowOrigins;
907 Opts.CodeComplete.InsertIncludes = HeaderInsertion;
908 Opts.CodeComplete.ImportInsertions = ImportInsertions;
909 if (!HeaderInsertionDecorators) {
910 Opts.CodeComplete.IncludeIndicator.Insert.clear();
911 Opts.CodeComplete.IncludeIndicator.NoInsert.clear();
912 }
913 Opts.CodeComplete.EnableFunctionArgSnippets = EnableFunctionArgSnippets;
914 Opts.CodeComplete.RunParser = CodeCompletionParse;
915 Opts.CodeComplete.RankingModel = RankingModel;
916
917 RealThreadsafeFS TFS;
918 std::vector<std::unique_ptr<config::Provider>> ProviderStack;
919 std::unique_ptr<config::Provider> Config;
920 if (EnableConfig) {
921 ProviderStack.push_back(
922 x: config::Provider::fromAncestorRelativeYAMLFiles(RelPath: ".clangd", TFS));
923 llvm::SmallString<256> UserConfig;
924 if (llvm::sys::path::user_config_directory(result&: UserConfig)) {
925 llvm::sys::path::append(path&: UserConfig, a: "clangd", b: "config.yaml");
926 vlog(Fmt: "User config file is {0}", Vals&: UserConfig);
927 ProviderStack.push_back(x: config::Provider::fromYAMLFile(
928 AbsPath: UserConfig, /*Directory=*/"", TFS, /*Trusted=*/true));
929 } else {
930 elog(Fmt: "Couldn't determine user config file, not loading");
931 }
932 }
933 ProviderStack.push_back(x: std::make_unique<FlagsConfigProvider>());
934 std::vector<const config::Provider *> ProviderPointers;
935 for (const auto &P : ProviderStack)
936 ProviderPointers.push_back(x: P.get());
937 Config = config::Provider::combine(std::move(ProviderPointers));
938 Opts.ConfigProvider = Config.get();
939
940 // Create an empty clang-tidy option.
941 TidyProvider ClangTidyOptProvider;
942 if (EnableClangTidy) {
943 std::vector<TidyProvider> Providers;
944 Providers.reserve(n: 4 + EnableConfig);
945 Providers.push_back(x: provideEnvironment());
946 Providers.push_back(x: provideClangTidyFiles(TFS));
947 if (EnableConfig)
948 Providers.push_back(x: provideClangdConfig());
949 Providers.push_back(x: provideDefaultChecks());
950 Providers.push_back(x: disableUnusableChecks());
951 ClangTidyOptProvider = combine(Providers: std::move(Providers));
952 Opts.ClangTidyProvider = ClangTidyOptProvider;
953 }
954 Opts.UseDirtyHeaders = UseDirtyHeaders;
955 Opts.PreambleParseForwardingFunctions = PreambleParseForwardingFunctions;
956 Opts.ImportInsertions = ImportInsertions;
957 Opts.QueryDriverGlobs = std::move(QueryDriverGlobs);
958 Opts.TweakFilter = [&](const Tweak &T) {
959 if (T.hidden() && !HiddenFeatures)
960 return false;
961 if (TweakList.getNumOccurrences())
962 return llvm::is_contained(Range&: TweakList, Element: T.id());
963 return true;
964 };
965 if (ForceOffsetEncoding != OffsetEncoding::UnsupportedEncoding)
966 Opts.Encoding = ForceOffsetEncoding;
967
968 if (CheckFile.getNumOccurrences()) {
969 llvm::SmallString<256> Path;
970 if (auto Error =
971 llvm::sys::fs::real_path(path: CheckFile, output&: Path, /*expand_tilde=*/true)) {
972 elog(Fmt: "Failed to resolve path {0}: {1}", Vals&: CheckFile, Vals: Error.message());
973 return 1;
974 }
975 log(Fmt: "Entering check mode (no LSP server)");
976 return check(File: Path, TFS, Opts)
977 ? 0
978 : static_cast<int>(ErrorResultCode::CheckFailed);
979 }
980
981 // Initialize and run ClangdLSPServer.
982 // Change stdin to binary to not lose \r\n on windows.
983 llvm::sys::ChangeStdinToBinary();
984 std::unique_ptr<Transport> TransportLayer;
985 if (getenv(name: "CLANGD_AS_XPC_SERVICE")) {
986#if CLANGD_BUILD_XPC
987 log("Starting LSP over XPC service");
988 TransportLayer = newXPCTransport();
989#else
990 llvm::errs() << "This clangd binary wasn't built with XPC support.\n";
991 return static_cast<int>(ErrorResultCode::CantRunAsXPCService);
992#endif
993 } else {
994 log(Fmt: "Starting LSP over stdin/stdout");
995 TransportLayer = newJSONTransport(
996 stdin, Out&: llvm::outs(), InMirror: InputMirrorStream ? &*InputMirrorStream : nullptr,
997 Pretty: PrettyPrint, InputStyle);
998 }
999 if (!PathMappingsArg.empty()) {
1000 auto Mappings = parsePathMappings(RawPathMappings: PathMappingsArg);
1001 if (!Mappings) {
1002 elog(Fmt: "Invalid -path-mappings: {0}", Vals: Mappings.takeError());
1003 return 1;
1004 }
1005 TransportLayer = createPathMappingTransport(Transp: std::move(TransportLayer),
1006 Mappings: std::move(*Mappings));
1007 }
1008
1009 ClangdLSPServer LSPServer(*TransportLayer, TFS, Opts);
1010 llvm::set_thread_name("clangd.main");
1011 int ExitCode = LSPServer.run()
1012 ? 0
1013 : static_cast<int>(ErrorResultCode::NoShutdownRequest);
1014 log(Fmt: "LSP finished, exiting with status {0}", Vals&: ExitCode);
1015
1016 // There may still be lingering background threads (e.g. slow requests
1017 // whose results will be dropped, background index shutting down).
1018 //
1019 // These should terminate quickly, and ~ClangdLSPServer blocks on them.
1020 // However if a bug causes them to run forever, we want to ensure the process
1021 // eventually exits. As clangd isn't directly user-facing, an editor can
1022 // "leak" clangd processes. Crashing in this case contains the damage.
1023 abortAfterTimeout(Timeout: std::chrono::minutes(5));
1024
1025 return ExitCode;
1026}
1027
1028} // namespace clangd
1029} // namespace clang
1030

source code of clang-tools-extra/clangd/tool/ClangdMain.cpp