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 "ClangdLSPServer.h"
10 #include "CodeComplete.h"
11 #include "Features.inc"
12 #include "PathMapping.h"
13 #include "Protocol.h"
14 #include "TidyProvider.h"
15 #include "Transport.h"
16 #include "index/Background.h"
17 #include "index/Index.h"
18 #include "index/Merge.h"
19 #include "index/ProjectAware.h"
20 #include "index/Serialization.h"
21 #include "index/remote/Client.h"
22 #include "refactor/Rename.h"
23 #include "support/Path.h"
24 #include "support/Shutdown.h"
25 #include "support/ThreadsafeFS.h"
26 #include "support/Trace.h"
27 #include "clang/Basic/Version.h"
28 #include "clang/Format/Format.h"
29 #include "llvm/ADT/Optional.h"
30 #include "llvm/ADT/SmallString.h"
31 #include "llvm/ADT/StringRef.h"
32 #include "llvm/Support/CommandLine.h"
33 #include "llvm/Support/FileSystem.h"
34 #include "llvm/Support/Path.h"
35 #include "llvm/Support/Process.h"
36 #include "llvm/Support/Program.h"
37 #include "llvm/Support/Signals.h"
38 #include "llvm/Support/TargetSelect.h"
39 #include "llvm/Support/raw_ostream.h"
40 #include <chrono>
41 #include <cstdlib>
42 #include <iostream>
43 #include <memory>
44 #include <mutex>
45 #include <string>
46 #include <thread>
47 #include <vector>
48
49 #ifndef _WIN32
50 #include <unistd.h>
51 #endif
52
53 namespace clang {
54 namespace clangd {
55
56 // Implemented in Check.cpp.
57 bool check(const llvm::StringRef File, const ThreadsafeFS &TFS,
58 const ClangdLSPServer::Options &Opts);
59
60 namespace {
61
62 using llvm::cl::cat;
63 using llvm::cl::CommaSeparated;
64 using llvm::cl::desc;
65 using llvm::cl::Hidden;
66 using llvm::cl::init;
67 using llvm::cl::list;
68 using llvm::cl::opt;
69 using llvm::cl::OptionCategory;
70 using llvm::cl::ValueOptional;
71 using llvm::cl::values;
72
73 // All flags must be placed in a category, or they will be shown neither in
74 // --help, nor --help-hidden!
75 OptionCategory CompileCommands("clangd compilation flags options");
76 OptionCategory Features("clangd feature options");
77 OptionCategory Misc("clangd miscellaneous options");
78 OptionCategory Protocol("clangd protocol and logging options");
79 const OptionCategory *ClangdCategories[] = {&Features, &Protocol,
80 &CompileCommands, &Misc};
81
82 enum CompileArgsFrom { LSPCompileArgs, FilesystemCompileArgs };
83 opt<CompileArgsFrom> CompileArgsFrom{
84 "compile_args_from",
85 cat(CompileCommands),
86 desc("The source of compile commands"),
87 values(clEnumValN(LSPCompileArgs, "lsp",
88 "All compile commands come from LSP and "
89 "'compile_commands.json' files are ignored"),
90 clEnumValN(FilesystemCompileArgs, "filesystem",
91 "All compile commands come from the "
92 "'compile_commands.json' files")),
93 init(FilesystemCompileArgs),
94 Hidden,
95 };
96
97 opt<Path> CompileCommandsDir{
98 "compile-commands-dir",
99 cat(CompileCommands),
100 desc("Specify a path to look for compile_commands.json. If path "
101 "is invalid, clangd will look in the current directory and "
102 "parent paths of each source file"),
103 };
104
105 opt<Path> ResourceDir{
106 "resource-dir",
107 cat(CompileCommands),
108 desc("Directory for system clang headers"),
109 init(""),
110 Hidden,
111 };
112
113 list<std::string> QueryDriverGlobs{
114 "query-driver",
115 cat(CompileCommands),
116 desc(
117 "Comma separated list of globs for white-listing gcc-compatible "
118 "drivers that are safe to execute. Drivers matching any of these globs "
119 "will be used to extract system includes. e.g. "
120 "/usr/bin/**/clang-*,/path/to/repo/**/g++-*"),
121 CommaSeparated,
122 };
123
124 // FIXME: Flags are the wrong mechanism for user preferences.
125 // We should probably read a dotfile or similar.
126 opt<bool> AllScopesCompletion{
127 "all-scopes-completion",
128 cat(Features),
129 desc("If set to true, code completion will include index symbols that are "
130 "not defined in the scopes (e.g. "
131 "namespaces) visible from the code completion point. Such completions "
132 "can insert scope qualifiers"),
133 init(true),
134 };
135
136 opt<bool> ShowOrigins{
137 "debug-origin",
138 cat(Features),
139 desc("Show origins of completion items"),
140 init(CodeCompleteOptions().ShowOrigins),
141 Hidden,
142 };
143
144 opt<bool> EnableBackgroundIndex{
145 "background-index",
146 cat(Features),
147 desc("Index project code in the background and persist index on disk."),
148 init(true),
149 };
150
151 opt<bool> EnableClangTidy{
152 "clang-tidy",
153 cat(Features),
154 desc("Enable clang-tidy diagnostics"),
155 init(true),
156 };
157
158 opt<std::string> ClangTidyChecks{
159 "clang-tidy-checks",
160 cat(Features),
161 desc("List of clang-tidy checks to run (this will override "
162 ".clang-tidy files). Only meaningful when -clang-tidy flag is on"),
163 init(""),
164 };
165
166 opt<CodeCompleteOptions::CodeCompletionParse> CodeCompletionParse{
167 "completion-parse",
168 cat(Features),
169 desc("Whether the clang-parser is used for code-completion"),
170 values(clEnumValN(CodeCompleteOptions::AlwaysParse, "always",
171 "Block until the parser can be used"),
172 clEnumValN(CodeCompleteOptions::ParseIfReady, "auto",
173 "Use text-based completion if the parser "
174 "is not ready"),
175 clEnumValN(CodeCompleteOptions::NeverParse, "never",
176 "Always used text-based completion")),
177 init(CodeCompleteOptions().RunParser),
178 Hidden,
179 };
180
181 opt<CodeCompleteOptions::CodeCompletionRankingModel> RankingModel{
182 "ranking-model",
183 cat(Features),
184 desc("Model to use to rank code-completion items"),
185 values(clEnumValN(CodeCompleteOptions::Heuristics, "heuristics",
186 "Use hueristics to rank code completion items"),
187 clEnumValN(CodeCompleteOptions::DecisionForest, "decision_forest",
188 "Use Decision Forest model to rank completion items")),
189 init(CodeCompleteOptions().RankingModel),
190 Hidden,
191 };
192
193 opt<float> DecisionForestBase{
194 "decision-forest-base",
195 cat(Features),
196 desc("Base for exponentiating the prediction from DecisionForest."),
197 init(CodeCompleteOptions().DecisionForestBase),
198 Hidden,
199 };
200
201 // FIXME: also support "plain" style where signatures are always omitted.
202 enum CompletionStyleFlag { Detailed, Bundled };
203 opt<CompletionStyleFlag> CompletionStyle{
204 "completion-style",
205 cat(Features),
206 desc("Granularity of code completion suggestions"),
207 values(clEnumValN(Detailed, "detailed",
208 "One completion item for each semantically distinct "
209 "completion, with full type information"),
210 clEnumValN(Bundled, "bundled",
211 "Similar completion items (e.g. function overloads) are "
212 "combined. Type information shown where possible")),
213 };
214
215 opt<std::string> FallbackStyle{
216 "fallback-style",
217 cat(Features),
218 desc("clang-format style to apply by default when "
219 "no .clang-format file is found"),
220 init(clang::format::DefaultFallbackStyle),
221 };
222
223 opt<bool> EnableFunctionArgSnippets{
224 "function-arg-placeholders",
225 cat(Features),
226 desc("When disabled, completions contain only parentheses for "
227 "function calls. When enabled, completions also contain "
228 "placeholders for method parameters"),
229 init(CodeCompleteOptions().EnableFunctionArgSnippets),
230 Hidden,
231 };
232
233 opt<CodeCompleteOptions::IncludeInsertion> HeaderInsertion{
234 "header-insertion",
235 cat(Features),
236 desc("Add #include directives when accepting code completions"),
237 init(CodeCompleteOptions().InsertIncludes),
238 values(
239 clEnumValN(CodeCompleteOptions::IWYU, "iwyu",
240 "Include what you use. "
241 "Insert the owning header for top-level symbols, unless the "
242 "header is already directly included or the symbol is "
243 "forward-declared"),
244 clEnumValN(
245 CodeCompleteOptions::NeverInsert, "never",
246 "Never insert #include directives as part of code completion")),
247 };
248
249 opt<bool> HeaderInsertionDecorators{
250 "header-insertion-decorators",
251 cat(Features),
252 desc("Prepend a circular dot or space before the completion "
253 "label, depending on whether "
254 "an include line will be inserted or not"),
255 init(true),
256 };
257
258 opt<bool> HiddenFeatures{
259 "hidden-features",
260 cat(Features),
261 desc("Enable hidden features mostly useful to clangd developers"),
262 init(false),
263 Hidden,
264 };
265
266 opt<bool> IncludeIneligibleResults{
267 "include-ineligible-results",
268 cat(Features),
269 desc("Include ineligible completion results (e.g. private members)"),
270 init(CodeCompleteOptions().IncludeIneligibleResults),
271 Hidden,
272 };
273
274 opt<bool> EnableIndex{
275 "index",
276 cat(Features),
277 desc("Enable index-based features. By default, clangd maintains an index "
278 "built from symbols in opened files. Global index support needs to "
279 "enabled separatedly"),
280 init(true),
281 Hidden,
282 };
283
284 opt<int> LimitResults{
285 "limit-results",
286 cat(Features),
287 desc("Limit the number of results returned by clangd. "
288 "0 means no limit (default=100)"),
289 init(100),
290 };
291
292 opt<bool> SuggestMissingIncludes{
293 "suggest-missing-includes",
294 cat(Features),
295 desc("Attempts to fix diagnostic errors caused by missing "
296 "includes using index"),
297 init(true),
298 };
299
300 list<std::string> TweakList{
301 "tweaks",
302 cat(Features),
303 desc("Specify a list of Tweaks to enable (only for clangd developers)."),
304 Hidden,
305 CommaSeparated,
306 };
307
308 opt<bool> CrossFileRename{
309 "cross-file-rename",
310 cat(Features),
311 desc("Enable cross-file rename feature."),
312 init(true),
313 };
314
315 opt<bool> RecoveryAST{
316 "recovery-ast",
317 cat(Features),
318 desc("Preserve expressions in AST for broken code."),
319 init(ClangdServer::Options().BuildRecoveryAST),
320 };
321
322 opt<bool> RecoveryASTType{
323 "recovery-ast-type",
324 cat(Features),
325 desc("Preserve the type for recovery AST."),
326 init(ClangdServer::Options().PreserveRecoveryASTType),
327 Hidden,
328 };
329
330 opt<bool> FoldingRanges{
331 "folding-ranges",
332 cat(Features),
333 desc("Enable preview of FoldingRanges feature"),
334 init(false),
335 Hidden,
336 };
337
338 opt<unsigned> WorkerThreadsCount{
339 "j",
340 cat(Misc),
341 desc("Number of async workers used by clangd. Background index also "
342 "uses this many workers."),
343 init(getDefaultAsyncThreadsCount()),
344 };
345
346 opt<Path> IndexFile{
347 "index-file",
348 cat(Misc),
349 desc(
350 "Index file to build the static index. The file must have been created "
351 "by a compatible clangd-indexer\n"
352 "WARNING: This option is experimental only, and will be removed "
353 "eventually. Don't rely on it"),
354 init(""),
355 Hidden,
356 };
357
358 opt<bool> Test{
359 "lit-test",
360 cat(Misc),
361 desc("Abbreviation for -input-style=delimited -pretty -sync "
362 "-enable-test-scheme -enable-config=0 -log=verbose. "
363 "Intended to simplify lit tests"),
364 init(false),
365 Hidden,
366 };
367
368 opt<Path> CheckFile{
369 "check",
370 cat(Misc),
371 desc("Parse one file in isolation instead of acting as a language server. "
372 "Useful to investigate/reproduce crashes or configuration problems. "
373 "With --check=<filename>, attempts to parse a particular file."),
374 init(""),
375 ValueOptional,
376 };
377
378 enum PCHStorageFlag { Disk, Memory };
379 opt<PCHStorageFlag> PCHStorage{
380 "pch-storage",
381 cat(Misc),
382 desc("Storing PCHs in memory increases memory usages, but may "
383 "improve performance"),
384 values(
385 clEnumValN(PCHStorageFlag::Disk, "disk", "store PCHs on disk"),
386 clEnumValN(PCHStorageFlag::Memory, "memory", "store PCHs in memory")),
387 init(PCHStorageFlag::Disk),
388 };
389
390 opt<bool> Sync{
391 "sync",
392 cat(Misc),
393 desc("Handle client requests on main thread. Background index still uses "
394 "its own thread."),
395 init(false),
396 Hidden,
397 };
398
399 opt<JSONStreamStyle> InputStyle{
400 "input-style",
401 cat(Protocol),
402 desc("Input JSON stream encoding"),
403 values(
404 clEnumValN(JSONStreamStyle::Standard, "standard", "usual LSP protocol"),
405 clEnumValN(JSONStreamStyle::Delimited, "delimited",
406 "messages delimited by --- lines, with # comment support")),
407 init(JSONStreamStyle::Standard),
408 Hidden,
409 };
410
411 opt<bool> EnableTestScheme{
412 "enable-test-uri-scheme",
413 cat(Protocol),
414 desc("Enable 'test:' URI scheme. Only use in lit tests"),
415 init(false),
416 Hidden,
417 };
418
419 opt<std::string> PathMappingsArg{
420 "path-mappings",
421 cat(Protocol),
422 desc(
423 "Translates between client paths (as seen by a remote editor) and "
424 "server paths (where clangd sees files on disk). "
425 "Comma separated list of '<client_path>=<server_path>' pairs, the "
426 "first entry matching a given path is used. "
427 "e.g. /home/project/incl=/opt/include,/home/project=/workarea/project"),
428 init(""),
429 };
430
431 opt<Path> InputMirrorFile{
432 "input-mirror-file",
433 cat(Protocol),
434 desc("Mirror all LSP input to the specified file. Useful for debugging"),
435 init(""),
436 Hidden,
437 };
438
439 opt<Logger::Level> LogLevel{
440 "log",
441 cat(Protocol),
442 desc("Verbosity of log messages written to stderr"),
443 values(clEnumValN(Logger::Error, "error", "Error messages only"),
444 clEnumValN(Logger::Info, "info", "High level execution tracing"),
445 clEnumValN(Logger::Debug, "verbose", "Low level details")),
446 init(Logger::Info),
447 };
448
449 opt<OffsetEncoding> ForceOffsetEncoding{
450 "offset-encoding",
451 cat(Protocol),
452 desc("Force the offsetEncoding used for character positions. "
453 "This bypasses negotiation via client capabilities"),
454 values(
455 clEnumValN(OffsetEncoding::UTF8, "utf-8", "Offsets are in UTF-8 bytes"),
456 clEnumValN(OffsetEncoding::UTF16, "utf-16",
457 "Offsets are in UTF-16 code units"),
458 clEnumValN(OffsetEncoding::UTF32, "utf-32",
459 "Offsets are in unicode codepoints")),
460 init(OffsetEncoding::UnsupportedEncoding),
461 };
462
463 opt<bool> PrettyPrint{
464 "pretty",
465 cat(Protocol),
466 desc("Pretty-print JSON output"),
467 init(false),
468 };
469
470 opt<bool> AsyncPreamble{
471 "async-preamble",
472 cat(Misc),
473 desc("Reuse even stale preambles, and rebuild them in the background. This "
474 "improves latency at the cost of accuracy."),
475 init(ClangdServer::Options().AsyncPreambleBuilds),
476 Hidden,
477 };
478
479 opt<bool> EnableConfig{
480 "enable-config",
481 cat(Misc),
482 desc(
483 "Read user and project configuration from YAML files.\n"
484 "Project config is from a .clangd file in the project directory.\n"
485 "User config is from clangd/config.yaml in the following directories:\n"
486 "\tWindows: %USERPROFILE%\\AppData\\Local\n"
487 "\tMac OS: ~/Library/Preferences/\n"
488 "\tOthers: $XDG_CONFIG_HOME, usually ~/.config\n"
489 "Configuration is documented at https://clangd.llvm.org/config.html"),
490 init(true),
491 };
492
493 opt<bool> CollectMainFileRefs{
494 "collect-main-file-refs",
495 cat(Misc),
496 desc("Store references to main-file-only symbols in the index"),
497 init(ClangdServer::Options().CollectMainFileRefs),
498 };
499
500 #if CLANGD_ENABLE_REMOTE
501 opt<std::string> RemoteIndexAddress{
502 "remote-index-address",
503 cat(Features),
504 desc("Address of the remote index server"),
505 };
506
507 // FIXME(kirillbobyrev): Should this be the location of compile_commands.json?
508 opt<std::string> ProjectRoot{
509 "project-root",
510 cat(Features),
511 desc("Path to the project root. Requires remote-index-address to be set."),
512 };
513 #endif
514
515 /// Supports a test URI scheme with relaxed constraints for lit tests.
516 /// The path in a test URI will be combined with a platform-specific fake
517 /// directory to form an absolute path. For example, test:///a.cpp is resolved
518 /// C:\clangd-test\a.cpp on Windows and /clangd-test/a.cpp on Unix.
519 class TestScheme : public URIScheme {
520 public:
521 llvm::Expected<std::string>
getAbsolutePath(llvm::StringRef,llvm::StringRef Body,llvm::StringRef) const522 getAbsolutePath(llvm::StringRef /*Authority*/, llvm::StringRef Body,
523 llvm::StringRef /*HintPath*/) const override {
524 using namespace llvm::sys;
525 // Still require "/" in body to mimic file scheme, as we want lengths of an
526 // equivalent URI in both schemes to be the same.
527 if (!Body.startswith("/"))
528 return error(
529 "Expect URI body to be an absolute path starting with '/': {0}",
530 Body);
531 Body = Body.ltrim('/');
532 llvm::SmallString<16> Path(Body);
533 path::native(Path);
534 fs::make_absolute(TestScheme::TestDir, Path);
535 return std::string(Path);
536 }
537
538 llvm::Expected<URI>
uriFromAbsolutePath(llvm::StringRef AbsolutePath) const539 uriFromAbsolutePath(llvm::StringRef AbsolutePath) const override {
540 llvm::StringRef Body = AbsolutePath;
541 if (!Body.consume_front(TestScheme::TestDir))
542 return error("Path {0} doesn't start with root {1}", AbsolutePath,
543 TestDir);
544
545 return URI("test", /*Authority=*/"",
546 llvm::sys::path::convert_to_slash(Body));
547 }
548
549 private:
550 const static char TestDir[];
551 };
552
553 #ifdef _WIN32
554 const char TestScheme::TestDir[] = "C:\\clangd-test";
555 #else
556 const char TestScheme::TestDir[] = "/clangd-test";
557 #endif
558
559 std::unique_ptr<SymbolIndex>
loadExternalIndex(const Config::ExternalIndexSpec & External,AsyncTaskRunner & Tasks)560 loadExternalIndex(const Config::ExternalIndexSpec &External,
561 AsyncTaskRunner &Tasks) {
562 switch (External.Kind) {
563 case Config::ExternalIndexSpec::Server:
564 log("Associating {0} with remote index at {1}.", External.MountPoint,
565 External.Location);
566 return remote::getClient(External.Location, External.MountPoint);
567 case Config::ExternalIndexSpec::File:
568 log("Associating {0} with monolithic index at {1}.", External.MountPoint,
569 External.Location);
570 auto NewIndex = std::make_unique<SwapIndex>(std::make_unique<MemIndex>());
571 Tasks.runAsync("Load-index:" + External.Location,
572 [File = External.Location, PlaceHolder = NewIndex.get()] {
573 if (auto Idx = loadIndex(File, /*UseDex=*/true))
574 PlaceHolder->reset(std::move(Idx));
575 });
576 return std::move(NewIndex);
577 }
578 llvm_unreachable("Invalid ExternalIndexKind.");
579 }
580 } // namespace
581 } // namespace clangd
582 } // namespace clang
583
584 enum class ErrorResultCode : int {
585 NoShutdownRequest = 1,
586 CantRunAsXPCService = 2,
587 CheckFailed = 3
588 };
589
main(int argc,char * argv[])590 int main(int argc, char *argv[]) {
591 using namespace clang;
592 using namespace clang::clangd;
593
594 llvm::InitializeAllTargetInfos();
595 llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
596 llvm::sys::SetInterruptFunction(&requestShutdown);
597 llvm::cl::SetVersionPrinter([](llvm::raw_ostream &OS) {
598 OS << clang::getClangToolFullVersion("clangd") << "\n";
599 });
600 const char *FlagsEnvVar = "CLANGD_FLAGS";
601 const char *Overview =
602 R"(clangd is a language server that provides IDE-like features to editors.
603
604 It should be used via an editor plugin rather than invoked directly. For more information, see:
605 https://clangd.llvm.org/
606 https://microsoft.github.io/language-server-protocol/
607
608 clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment variable.
609 )";
610 llvm::cl::HideUnrelatedOptions(ClangdCategories);
611 llvm::cl::ParseCommandLineOptions(argc, argv, Overview,
612 /*Errs=*/nullptr, FlagsEnvVar);
613 if (Test) {
614 Sync = true;
615 InputStyle = JSONStreamStyle::Delimited;
616 LogLevel = Logger::Verbose;
617 PrettyPrint = true;
618 // Disable config system by default to avoid external reads.
619 if (!EnableConfig.getNumOccurrences())
620 EnableConfig = false;
621 // Disable background index on lit tests by default to prevent disk writes.
622 if (!EnableBackgroundIndex.getNumOccurrences())
623 EnableBackgroundIndex = false;
624 // Ensure background index makes progress.
625 else if (EnableBackgroundIndex)
626 BackgroundQueue::preventThreadStarvationInTests();
627 }
628 if (Test || EnableTestScheme) {
629 static URISchemeRegistry::Add<TestScheme> X(
630 "test", "Test scheme for clangd lit tests.");
631 }
632
633 if (!Sync && WorkerThreadsCount == 0) {
634 llvm::errs() << "A number of worker threads cannot be 0. Did you mean to "
635 "specify -sync?";
636 return 1;
637 }
638
639 if (Sync) {
640 if (WorkerThreadsCount.getNumOccurrences())
641 llvm::errs() << "Ignoring -j because -sync is set.\n";
642 WorkerThreadsCount = 0;
643 }
644 if (FallbackStyle.getNumOccurrences())
645 clang::format::DefaultFallbackStyle = FallbackStyle.c_str();
646
647 // Validate command line arguments.
648 llvm::Optional<llvm::raw_fd_ostream> InputMirrorStream;
649 if (!InputMirrorFile.empty()) {
650 std::error_code EC;
651 InputMirrorStream.emplace(InputMirrorFile, /*ref*/ EC,
652 llvm::sys::fs::FA_Read | llvm::sys::fs::FA_Write);
653 if (EC) {
654 InputMirrorStream.reset();
655 llvm::errs() << "Error while opening an input mirror file: "
656 << EC.message();
657 } else {
658 InputMirrorStream->SetUnbuffered();
659 }
660 }
661
662 // Setup tracing facilities if CLANGD_TRACE is set. In practice enabling a
663 // trace flag in your editor's config is annoying, launching with
664 // `CLANGD_TRACE=trace.json vim` is easier.
665 llvm::Optional<llvm::raw_fd_ostream> TracerStream;
666 std::unique_ptr<trace::EventTracer> Tracer;
667 const char *JSONTraceFile = getenv("CLANGD_TRACE");
668 const char *MetricsCSVFile = getenv("CLANGD_METRICS");
669 const char *TracerFile = JSONTraceFile ? JSONTraceFile : MetricsCSVFile;
670 if (TracerFile) {
671 std::error_code EC;
672 TracerStream.emplace(TracerFile, /*ref*/ EC,
673 llvm::sys::fs::FA_Read | llvm::sys::fs::FA_Write);
674 if (EC) {
675 TracerStream.reset();
676 llvm::errs() << "Error while opening trace file " << TracerFile << ": "
677 << EC.message();
678 } else {
679 Tracer = (TracerFile == JSONTraceFile)
680 ? trace::createJSONTracer(*TracerStream, PrettyPrint)
681 : trace::createCSVMetricTracer(*TracerStream);
682 }
683 }
684
685 llvm::Optional<trace::Session> TracingSession;
686 if (Tracer)
687 TracingSession.emplace(*Tracer);
688
689 // If a user ran `clangd` in a terminal without redirecting anything,
690 // it's somewhat likely they're confused about how to use clangd.
691 // Show them the help overview, which explains.
692 if (llvm::outs().is_displayed() && llvm::errs().is_displayed() &&
693 !CheckFile.getNumOccurrences())
694 llvm::errs() << Overview << "\n";
695 // Use buffered stream to stderr (we still flush each log message). Unbuffered
696 // stream can cause significant (non-deterministic) latency for the logger.
697 llvm::errs().SetBuffered();
698 // Don't flush stdout when logging, this would be both slow and racy!
699 llvm::errs().tie(nullptr);
700 StreamLogger Logger(llvm::errs(), LogLevel);
701 LoggingSession LoggingSession(Logger);
702 // Write some initial logs before we start doing any real work.
703 log("{0}", clang::getClangToolFullVersion("clangd"));
704 log("PID: {0}", llvm::sys::Process::getProcessId());
705 {
706 SmallString<128> CWD;
707 if (auto Err = llvm::sys::fs::current_path(CWD))
708 log("Working directory unknown: {0}", Err.message());
709 else
710 log("Working directory: {0}", CWD);
711 }
712 for (int I = 0; I < argc; ++I)
713 log("argv[{0}]: {1}", I, argv[I]);
714 if (auto EnvFlags = llvm::sys::Process::GetEnv(FlagsEnvVar))
715 log("{0}: {1}", FlagsEnvVar, *EnvFlags);
716
717 ClangdLSPServer::Options Opts;
718 Opts.UseDirBasedCDB = (CompileArgsFrom == FilesystemCompileArgs);
719
720 // If --compile-commands-dir arg was invoked, check value and override default
721 // path.
722 if (!CompileCommandsDir.empty()) {
723 if (llvm::sys::fs::exists(CompileCommandsDir)) {
724 // We support passing both relative and absolute paths to the
725 // --compile-commands-dir argument, but we assume the path is absolute in
726 // the rest of clangd so we make sure the path is absolute before
727 // continuing.
728 llvm::SmallString<128> Path(CompileCommandsDir);
729 if (std::error_code EC = llvm::sys::fs::make_absolute(Path)) {
730 elog("Error while converting the relative path specified by "
731 "--compile-commands-dir to an absolute path: {0}. The argument "
732 "will be ignored.",
733 EC.message());
734 } else {
735 Opts.CompileCommandsDir = std::string(Path.str());
736 }
737 } else {
738 elog("Path specified by --compile-commands-dir does not exist. The "
739 "argument will be ignored.");
740 }
741 }
742
743 switch (PCHStorage) {
744 case PCHStorageFlag::Memory:
745 Opts.StorePreamblesInMemory = true;
746 break;
747 case PCHStorageFlag::Disk:
748 Opts.StorePreamblesInMemory = false;
749 break;
750 }
751 if (!ResourceDir.empty())
752 Opts.ResourceDir = ResourceDir;
753 Opts.BuildDynamicSymbolIndex = EnableIndex;
754 Opts.CollectMainFileRefs = CollectMainFileRefs;
755 std::vector<std::unique_ptr<SymbolIndex>> IdxStack;
756 std::unique_ptr<SymbolIndex> StaticIdx;
757 std::future<void> AsyncIndexLoad; // Block exit while loading the index.
758 if (EnableIndex && !IndexFile.empty()) {
759 // Load the index asynchronously. Meanwhile SwapIndex returns no results.
760 SwapIndex *Placeholder;
761 StaticIdx.reset(Placeholder = new SwapIndex(std::make_unique<MemIndex>()));
762 AsyncIndexLoad = runAsync<void>([Placeholder] {
763 if (auto Idx = loadIndex(IndexFile, /*UseDex=*/true))
764 Placeholder->reset(std::move(Idx));
765 });
766 if (Sync)
767 AsyncIndexLoad.wait();
768 }
769 #if CLANGD_ENABLE_REMOTE
770 if (RemoteIndexAddress.empty() != ProjectRoot.empty()) {
771 llvm::errs() << "remote-index-address and project-path have to be "
772 "specified at the same time.";
773 return 1;
774 }
775 if (!RemoteIndexAddress.empty()) {
776 if (IndexFile.empty()) {
777 log("Connecting to remote index at {0}", RemoteIndexAddress);
778 StaticIdx = remote::getClient(RemoteIndexAddress, ProjectRoot);
779 EnableBackgroundIndex = false;
780 } else {
781 elog("When enabling remote index, IndexFile should not be specified. "
782 "Only one can be used at time. Remote index will ignored.");
783 }
784 }
785 #endif
786 Opts.BackgroundIndex = EnableBackgroundIndex;
787 auto PAI = createProjectAwareIndex(loadExternalIndex);
788 if (StaticIdx) {
789 IdxStack.emplace_back(std::move(StaticIdx));
790 IdxStack.emplace_back(
791 std::make_unique<MergedIndex>(PAI.get(), IdxStack.back().get()));
792 Opts.StaticIndex = IdxStack.back().get();
793 } else {
794 Opts.StaticIndex = PAI.get();
795 }
796 Opts.AsyncThreadsCount = WorkerThreadsCount;
797 Opts.BuildRecoveryAST = RecoveryAST;
798 Opts.PreserveRecoveryASTType = RecoveryASTType;
799 Opts.FoldingRanges = FoldingRanges;
800
801 Opts.CodeComplete.IncludeIneligibleResults = IncludeIneligibleResults;
802 Opts.CodeComplete.Limit = LimitResults;
803 if (CompletionStyle.getNumOccurrences())
804 Opts.CodeComplete.BundleOverloads = CompletionStyle != Detailed;
805 Opts.CodeComplete.ShowOrigins = ShowOrigins;
806 Opts.CodeComplete.InsertIncludes = HeaderInsertion;
807 if (!HeaderInsertionDecorators) {
808 Opts.CodeComplete.IncludeIndicator.Insert.clear();
809 Opts.CodeComplete.IncludeIndicator.NoInsert.clear();
810 }
811 Opts.CodeComplete.SpeculativeIndexRequest = Opts.StaticIndex;
812 Opts.CodeComplete.EnableFunctionArgSnippets = EnableFunctionArgSnippets;
813 Opts.CodeComplete.AllScopes = AllScopesCompletion;
814 Opts.CodeComplete.RunParser = CodeCompletionParse;
815 Opts.CodeComplete.RankingModel = RankingModel;
816 Opts.CodeComplete.DecisionForestBase = DecisionForestBase;
817
818 RealThreadsafeFS TFS;
819 std::vector<std::unique_ptr<config::Provider>> ProviderStack;
820 std::unique_ptr<config::Provider> Config;
821 if (EnableConfig) {
822 ProviderStack.push_back(
823 config::Provider::fromAncestorRelativeYAMLFiles(".clangd", TFS));
824 llvm::SmallString<256> UserConfig;
825 if (llvm::sys::path::user_config_directory(UserConfig)) {
826 llvm::sys::path::append(UserConfig, "clangd", "config.yaml");
827 vlog("User config file is {0}", UserConfig);
828 ProviderStack.push_back(
829 config::Provider::fromYAMLFile(UserConfig, /*Directory=*/"", TFS));
830 } else {
831 elog("Couldn't determine user config file, not loading");
832 }
833 std::vector<const config::Provider *> ProviderPointers;
834 for (const auto &P : ProviderStack)
835 ProviderPointers.push_back(P.get());
836 Config = config::Provider::combine(std::move(ProviderPointers));
837 Opts.ConfigProvider = Config.get();
838 }
839
840 // Create an empty clang-tidy option.
841 TidyProvider ClangTidyOptProvider;
842 if (EnableClangTidy) {
843 std::vector<TidyProvider> Providers;
844 Providers.reserve(4 + EnableConfig);
845 Providers.push_back(provideEnvironment());
846 Providers.push_back(provideClangTidyFiles(TFS));
847 if (EnableConfig)
848 Providers.push_back(provideClangdConfig());
849 if (!ClangTidyChecks.empty())
850 Providers.push_back(addTidyChecks(ClangTidyChecks));
851 else
852 Providers.push_back(provideDefaultChecks());
853 Providers.push_back(disableUnusableChecks());
854 ClangTidyOptProvider = combine(std::move(Providers));
855 Opts.ClangTidyProvider = ClangTidyOptProvider;
856 }
857 Opts.AsyncPreambleBuilds = AsyncPreamble;
858 Opts.SuggestMissingIncludes = SuggestMissingIncludes;
859 Opts.QueryDriverGlobs = std::move(QueryDriverGlobs);
860 Opts.TweakFilter = [&](const Tweak &T) {
861 if (T.hidden() && !HiddenFeatures)
862 return false;
863 if (TweakList.getNumOccurrences())
864 return llvm::is_contained(TweakList, T.id());
865 return true;
866 };
867 if (ForceOffsetEncoding != OffsetEncoding::UnsupportedEncoding)
868 Opts.Encoding = ForceOffsetEncoding;
869
870 // Shall we allow to customize the file limit?
871 Opts.Rename.AllowCrossFile = CrossFileRename;
872
873 if (CheckFile.getNumOccurrences()) {
874 llvm::SmallString<256> Path;
875 llvm::sys::fs::real_path(CheckFile, Path, /*expand_tilde=*/true);
876 log("Entering check mode (no LSP server)");
877 return check(Path, TFS, Opts)
878 ? 0
879 : static_cast<int>(ErrorResultCode::CheckFailed);
880 }
881
882 // Initialize and run ClangdLSPServer.
883 // Change stdin to binary to not lose \r\n on windows.
884 llvm::sys::ChangeStdinToBinary();
885 std::unique_ptr<Transport> TransportLayer;
886 if (getenv("CLANGD_AS_XPC_SERVICE")) {
887 #if CLANGD_BUILD_XPC
888 log("Starting LSP over XPC service");
889 TransportLayer = newXPCTransport();
890 #else
891 llvm::errs() << "This clangd binary wasn't built with XPC support.\n";
892 return static_cast<int>(ErrorResultCode::CantRunAsXPCService);
893 #endif
894 } else {
895 log("Starting LSP over stdin/stdout");
896 TransportLayer = newJSONTransport(
897 stdin, llvm::outs(),
898 InputMirrorStream ? InputMirrorStream.getPointer() : nullptr,
899 PrettyPrint, InputStyle);
900 }
901 if (!PathMappingsArg.empty()) {
902 auto Mappings = parsePathMappings(PathMappingsArg);
903 if (!Mappings) {
904 elog("Invalid -path-mappings: {0}", Mappings.takeError());
905 return 1;
906 }
907 TransportLayer = createPathMappingTransport(std::move(TransportLayer),
908 std::move(*Mappings));
909 }
910
911 ClangdLSPServer LSPServer(*TransportLayer, TFS, Opts);
912 llvm::set_thread_name("clangd.main");
913 int ExitCode = LSPServer.run()
914 ? 0
915 : static_cast<int>(ErrorResultCode::NoShutdownRequest);
916 log("LSP finished, exiting with status {0}", ExitCode);
917
918 // There may still be lingering background threads (e.g. slow requests
919 // whose results will be dropped, background index shutting down).
920 //
921 // These should terminate quickly, and ~ClangdLSPServer blocks on them.
922 // However if a bug causes them to run forever, we want to ensure the process
923 // eventually exits. As clangd isn't directly user-facing, an editor can
924 // "leak" clangd processes. Crashing in this case contains the damage.
925 abortAfterTimeout(std::chrono::minutes(5));
926
927 return ExitCode;
928 }
929