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 "Compiler.h"
12#include "Config.h"
13#include "ConfigProvider.h"
14#include "Feature.h"
15#include "IncludeCleaner.h"
16#include "PathMapping.h"
17#include "Protocol.h"
18#include "TidyProvider.h"
19#include "Transport.h"
20#include "index/Background.h"
21#include "index/Index.h"
22#include "index/MemIndex.h"
23#include "index/Merge.h"
24#include "index/ProjectAware.h"
25#include "index/remote/Client.h"
26#include "support/Path.h"
27#include "support/Shutdown.h"
28#include "support/ThreadCrashReporter.h"
29#include "support/ThreadsafeFS.h"
30#include "support/Trace.h"
31#include "clang/Format/Format.h"
32#include "llvm/ADT/Optional.h"
33#include "llvm/ADT/SmallString.h"
34#include "llvm/ADT/StringRef.h"
35#include "llvm/Support/CommandLine.h"
36#include "llvm/Support/FileSystem.h"
37#include "llvm/Support/Path.h"
38#include "llvm/Support/Process.h"
39#include "llvm/Support/Program.h"
40#include "llvm/Support/Signals.h"
41#include "llvm/Support/TargetSelect.h"
42#include "llvm/Support/raw_ostream.h"
43#include <chrono>
44#include <cstdlib>
45#include <memory>
46#include <mutex>
47#include <string>
48#include <thread>
49#include <utility>
50#include <vector>
51
52#ifndef _WIN32
53#include <unistd.h>
54#endif
55
56#ifdef __GLIBC__
57#include <malloc.h>
58#endif
59
60namespace clang {
61namespace clangd {
62
63// Implemented in Check.cpp.
64bool check(const llvm::StringRef File, llvm::Optional<Range> LineRange,
65 const ThreadsafeFS &TFS, const ClangdLSPServer::Options &Opts,
66 bool EnableCodeCompletion);
67
68namespace {
69
70using llvm::cl::cat;
71using llvm::cl::CommaSeparated;
72using llvm::cl::desc;
73using llvm::cl::Hidden;
74using llvm::cl::init;
75using llvm::cl::list;
76using llvm::cl::opt;
77using llvm::cl::OptionCategory;
78using llvm::cl::ValueOptional;
79using llvm::cl::values;
80
81// All flags must be placed in a category, or they will be shown neither in
82// --help, nor --help-hidden!
83OptionCategory CompileCommands("clangd compilation flags options");
84OptionCategory Features("clangd feature options");
85OptionCategory Misc("clangd miscellaneous options");
86OptionCategory Protocol("clangd protocol and logging options");
87OptionCategory Retired("clangd flags no longer in use");
88const OptionCategory *ClangdCategories[] = {&Features, &Protocol,
89 &CompileCommands, &Misc, &Retired};
90
91template <typename T> class RetiredFlag {
92 opt<T> Option;
93
94public:
95 RetiredFlag(llvm::StringRef Name)
96 : Option(Name, cat(Retired), desc("Obsolete flag, ignored"), Hidden,
97 llvm::cl::callback([Name](const T &) {
98 llvm::errs()
99 << "The flag `-" << Name << "` is obsolete and ignored.\n";
100 })) {}
101};
102
103enum CompileArgsFrom { LSPCompileArgs, FilesystemCompileArgs };
104opt<CompileArgsFrom> CompileArgsFrom{
105 "compile_args_from",
106 cat(CompileCommands),
107 desc("The source of compile commands"),
108 values(clEnumValN(LSPCompileArgs, "lsp",
109 "All compile commands come from LSP and "
110 "'compile_commands.json' files are ignored"),
111 clEnumValN(FilesystemCompileArgs, "filesystem",
112 "All compile commands come from the "
113 "'compile_commands.json' files")),
114 init(FilesystemCompileArgs),
115 Hidden,
116};
117
118opt<Path> CompileCommandsDir{
119 "compile-commands-dir",
120 cat(CompileCommands),
121 desc("Specify a path to look for compile_commands.json. If path "
122 "is invalid, clangd will look in the current directory and "
123 "parent paths of each source file"),
124};
125
126opt<Path> ResourceDir{
127 "resource-dir",
128 cat(CompileCommands),
129 desc("Directory for system clang headers"),
130 init(""),
131 Hidden,
132};
133
134list<std::string> QueryDriverGlobs{
135 "query-driver",
136 cat(CompileCommands),
137 desc(
138 "Comma separated list of globs for white-listing gcc-compatible "
139 "drivers that are safe to execute. Drivers matching any of these globs "
140 "will be used to extract system includes. e.g. "
141 "/usr/bin/**/clang-*,/path/to/repo/**/g++-*"),
142 CommaSeparated,
143};
144
145// FIXME: Flags are the wrong mechanism for user preferences.
146// We should probably read a dotfile or similar.
147opt<bool> AllScopesCompletion{
148 "all-scopes-completion",
149 cat(Features),
150 desc("If set to true, code completion will include index symbols that are "
151 "not defined in the scopes (e.g. "
152 "namespaces) visible from the code completion point. Such completions "
153 "can insert scope qualifiers"),
154 init(true),
155};
156
157opt<bool> ShowOrigins{
158 "debug-origin",
159 cat(Features),
160 desc("Show origins of completion items"),
161 init(CodeCompleteOptions().ShowOrigins),
162 Hidden,
163};
164
165opt<bool> EnableBackgroundIndex{
166 "background-index",
167 cat(Features),
168 desc("Index project code in the background and persist index on disk."),
169 init(true),
170};
171
172opt<llvm::ThreadPriority> BackgroundIndexPriority{
173 "background-index-priority",
174 cat(Features),
175 desc("Thread priority for building the background index. "
176 "The effect of this flag is OS-specific."),
177 values(clEnumValN(llvm::ThreadPriority::Background, "background",
178 "Minimum priority, runs on idle CPUs. "
179 "May leave 'performance' cores unused."),
180 clEnumValN(llvm::ThreadPriority::Low, "low",
181 "Reduced priority compared to interactive work."),
182 clEnumValN(llvm::ThreadPriority::Default, "normal",
183 "Same priority as other clangd work.")),
184 init(llvm::ThreadPriority::Low),
185};
186
187opt<bool> EnableClangTidy{
188 "clang-tidy",
189 cat(Features),
190 desc("Enable clang-tidy diagnostics"),
191 init(true),
192};
193
194opt<CodeCompleteOptions::CodeCompletionParse> CodeCompletionParse{
195 "completion-parse",
196 cat(Features),
197 desc("Whether the clang-parser is used for code-completion"),
198 values(clEnumValN(CodeCompleteOptions::AlwaysParse, "always",
199 "Block until the parser can be used"),
200 clEnumValN(CodeCompleteOptions::ParseIfReady, "auto",
201 "Use text-based completion if the parser "
202 "is not ready"),
203 clEnumValN(CodeCompleteOptions::NeverParse, "never",
204 "Always used text-based completion")),
205 init(CodeCompleteOptions().RunParser),
206 Hidden,
207};
208
209opt<CodeCompleteOptions::CodeCompletionRankingModel> RankingModel{
210 "ranking-model",
211 cat(Features),
212 desc("Model to use to rank code-completion items"),
213 values(clEnumValN(CodeCompleteOptions::Heuristics, "heuristics",
214 "Use hueristics to rank code completion items"),
215 clEnumValN(CodeCompleteOptions::DecisionForest, "decision_forest",
216 "Use Decision Forest model to rank completion items")),
217 init(CodeCompleteOptions().RankingModel),
218 Hidden,
219};
220
221// FIXME: also support "plain" style where signatures are always omitted.
222enum CompletionStyleFlag { Detailed, Bundled };
223opt<CompletionStyleFlag> CompletionStyle{
224 "completion-style",
225 cat(Features),
226 desc("Granularity of code completion suggestions"),
227 values(clEnumValN(Detailed, "detailed",
228 "One completion item for each semantically distinct "
229 "completion, with full type information"),
230 clEnumValN(Bundled, "bundled",
231 "Similar completion items (e.g. function overloads) are "
232 "combined. Type information shown where possible")),
233};
234
235opt<std::string> FallbackStyle{
236 "fallback-style",
237 cat(Features),
238 desc("clang-format style to apply by default when "
239 "no .clang-format file is found"),
240 init(clang::format::DefaultFallbackStyle),
241};
242
243opt<bool> EnableFunctionArgSnippets{
244 "function-arg-placeholders",
245 cat(Features),
246 desc("When disabled, completions contain only parentheses for "
247 "function calls. When enabled, completions also contain "
248 "placeholders for method parameters"),
249 init(CodeCompleteOptions().EnableFunctionArgSnippets),
250};
251
252opt<CodeCompleteOptions::IncludeInsertion> HeaderInsertion{
253 "header-insertion",
254 cat(Features),
255 desc("Add #include directives when accepting code completions"),
256 init(CodeCompleteOptions().InsertIncludes),
257 values(
258 clEnumValN(CodeCompleteOptions::IWYU, "iwyu",
259 "Include what you use. "
260 "Insert the owning header for top-level symbols, unless the "
261 "header is already directly included or the symbol is "
262 "forward-declared"),
263 clEnumValN(
264 CodeCompleteOptions::NeverInsert, "never",
265 "Never insert #include directives as part of code completion")),
266};
267
268opt<bool> IncludeCleanerStdlib{
269 "include-cleaner-stdlib",
270 cat(Features),
271 desc("Apply include-cleaner analysis to standard library headers "
272 "(immature!)"),
273 init(false),
274 Hidden,
275};
276
277opt<bool> HeaderInsertionDecorators{
278 "header-insertion-decorators",
279 cat(Features),
280 desc("Prepend a circular dot or space before the completion "
281 "label, depending on whether "
282 "an include line will be inserted or not"),
283 init(true),
284};
285
286opt<bool> HiddenFeatures{
287 "hidden-features",
288 cat(Features),
289 desc("Enable hidden features mostly useful to clangd developers"),
290 init(false),
291 Hidden,
292};
293
294opt<bool> IncludeIneligibleResults{
295 "include-ineligible-results",
296 cat(Features),
297 desc("Include ineligible completion results (e.g. private members)"),
298 init(CodeCompleteOptions().IncludeIneligibleResults),
299 Hidden,
300};
301
302RetiredFlag<bool> EnableIndex("index");
303RetiredFlag<bool> SuggestMissingIncludes("suggest-missing-includes");
304RetiredFlag<bool> RecoveryAST("recovery-ast");
305RetiredFlag<bool> RecoveryASTType("recovery-ast-type");
306RetiredFlag<bool> AsyncPreamble("async-preamble");
307RetiredFlag<bool> CollectMainFileRefs("collect-main-file-refs");
308RetiredFlag<bool> CrossFileRename("cross-file-rename");
309RetiredFlag<std::string> ClangTidyChecks("clang-tidy-checks");
310RetiredFlag<std::string> InlayHints("inlay-hints");
311
312opt<int> LimitResults{
313 "limit-results",
314 cat(Features),
315 desc("Limit the number of results returned by clangd. "
316 "0 means no limit (default=100)"),
317 init(100),
318};
319
320opt<int> ReferencesLimit{
321 "limit-references",
322 cat(Features),
323 desc("Limit the number of references returned by clangd. "
324 "0 means no limit (default=1000)"),
325 init(1000),
326};
327
328list<std::string> TweakList{
329 "tweaks",
330 cat(Features),
331 desc("Specify a list of Tweaks to enable (only for clangd developers)."),
332 Hidden,
333 CommaSeparated,
334};
335
336opt<bool> FoldingRanges{
337 "folding-ranges",
338 cat(Features),
339 desc("Enable preview of FoldingRanges feature"),
340 init(false),
341 Hidden,
342};
343
344opt<unsigned> WorkerThreadsCount{
345 "j",
346 cat(Misc),
347 desc("Number of async workers used by clangd. Background index also "
348 "uses this many workers."),
349 init(getDefaultAsyncThreadsCount()),
350};
351
352opt<Path> IndexFile{
353 "index-file",
354 cat(Misc),
355 desc(
356 "Index file to build the static index. The file must have been created "
357 "by a compatible clangd-indexer\n"
358 "WARNING: This option is experimental only, and will be removed "
359 "eventually. Don't rely on it"),
360 init(""),
361 Hidden,
362};
363
364opt<bool> Test{
365 "lit-test",
366 cat(Misc),
367 desc("Abbreviation for -input-style=delimited -pretty -sync "
368 "-enable-test-scheme -enable-config=0 -log=verbose -crash-pragmas. "
369 "Intended to simplify lit tests"),
370 init(false),
371 Hidden,
372};
373
374opt<bool> CrashPragmas{
375 "crash-pragmas",
376 cat(Misc),
377 desc("Respect `#pragma clang __debug crash` and friends."),
378 init(false),
379 Hidden,
380};
381
382opt<Path> CheckFile{
383 "check",
384 cat(Misc),
385 desc("Parse one file in isolation instead of acting as a language server. "
386 "Useful to investigate/reproduce crashes or configuration problems. "
387 "With --check=<filename>, attempts to parse a particular file."),
388 init(""),
389 ValueOptional,
390};
391
392opt<std::string> CheckFileLines{
393 "check-lines",
394 cat(Misc),
395 desc("If specified, limits the range of tokens in -check file on which "
396 "various features are tested. Example --check-lines=3-7 restricts "
397 "testing to lines 3 to 7 (inclusive) or --check-lines=5 to restrict "
398 "to one line. Default is testing entire file."),
399 init(""),
400 ValueOptional,
401};
402
403enum PCHStorageFlag { Disk, Memory };
404opt<PCHStorageFlag> PCHStorage{
405 "pch-storage",
406 cat(Misc),
407 desc("Storing PCHs in memory increases memory usages, but may "
408 "improve performance"),
409 values(
410 clEnumValN(PCHStorageFlag::Disk, "disk", "store PCHs on disk"),
411 clEnumValN(PCHStorageFlag::Memory, "memory", "store PCHs in memory")),
412 init(PCHStorageFlag::Disk),
413};
414
415opt<bool> Sync{
416 "sync",
417 cat(Misc),
418 desc("Handle client requests on main thread. Background index still uses "
419 "its own thread."),
420 init(false),
421 Hidden,
422};
423
424opt<JSONStreamStyle> InputStyle{
425 "input-style",
426 cat(Protocol),
427 desc("Input JSON stream encoding"),
428 values(
429 clEnumValN(JSONStreamStyle::Standard, "standard", "usual LSP protocol"),
430 clEnumValN(JSONStreamStyle::Delimited, "delimited",
431 "messages delimited by --- lines, with # comment support")),
432 init(JSONStreamStyle::Standard),
433 Hidden,
434};
435
436opt<bool> EnableTestScheme{
437 "enable-test-uri-scheme",
438 cat(Protocol),
439 desc("Enable 'test:' URI scheme. Only use in lit tests"),
440 init(false),
441 Hidden,
442};
443
444opt<std::string> PathMappingsArg{
445 "path-mappings",
446 cat(Protocol),
447 desc(
448 "Translates between client paths (as seen by a remote editor) and "
449 "server paths (where clangd sees files on disk). "
450 "Comma separated list of '<client_path>=<server_path>' pairs, the "
451 "first entry matching a given path is used. "
452 "e.g. /home/project/incl=/opt/include,/home/project=/workarea/project"),
453 init(""),
454};
455
456opt<Path> InputMirrorFile{
457 "input-mirror-file",
458 cat(Protocol),
459 desc("Mirror all LSP input to the specified file. Useful for debugging"),
460 init(""),
461 Hidden,
462};
463
464opt<Logger::Level> LogLevel{
465 "log",
466 cat(Protocol),
467 desc("Verbosity of log messages written to stderr"),
468 values(clEnumValN(Logger::Error, "error", "Error messages only"),
469 clEnumValN(Logger::Info, "info", "High level execution tracing"),
470 clEnumValN(Logger::Debug, "verbose", "Low level details")),
471 init(Logger::Info),
472};
473
474opt<OffsetEncoding> ForceOffsetEncoding{
475 "offset-encoding",
476 cat(Protocol),
477 desc("Force the offsetEncoding used for character positions. "
478 "This bypasses negotiation via client capabilities"),
479 values(
480 clEnumValN(OffsetEncoding::UTF8, "utf-8", "Offsets are in UTF-8 bytes"),
481 clEnumValN(OffsetEncoding::UTF16, "utf-16",
482 "Offsets are in UTF-16 code units"),
483 clEnumValN(OffsetEncoding::UTF32, "utf-32",
484 "Offsets are in unicode codepoints")),
485 init(OffsetEncoding::UnsupportedEncoding),
486};
487
488opt<bool> PrettyPrint{
489 "pretty",
490 cat(Protocol),
491 desc("Pretty-print JSON output"),
492 init(false),
493};
494
495opt<bool> EnableConfig{
496 "enable-config",
497 cat(Misc),
498 desc(
499 "Read user and project configuration from YAML files.\n"
500 "Project config is from a .clangd file in the project directory.\n"
501 "User config is from clangd/config.yaml in the following directories:\n"
502 "\tWindows: %USERPROFILE%\\AppData\\Local\n"
503 "\tMac OS: ~/Library/Preferences/\n"
504 "\tOthers: $XDG_CONFIG_HOME, usually ~/.config\n"
505 "Configuration is documented at https://clangd.llvm.org/config.html"),
506 init(true),
507};
508
509opt<bool> UseDirtyHeaders{"use-dirty-headers", cat(Misc),
510 desc("Use files open in the editor when parsing "
511 "headers instead of reading from the disk"),
512 Hidden,
513 init(ClangdServer::Options().UseDirtyHeaders)};
514
515opt<bool> PreambleParseForwardingFunctions{
516 "parse-forwarding-functions",
517 cat(Misc),
518 desc("Parse all emplace-like functions in included headers"),
519 Hidden,
520 init(ParseOptions().PreambleParseForwardingFunctions),
521};
522
523#if defined(__GLIBC__) && CLANGD_MALLOC_TRIM
524opt<bool> EnableMallocTrim{
525 "malloc-trim",
526 cat(Misc),
527 desc("Release memory periodically via malloc_trim(3)."),
528 init(true),
529};
530
531std::function<void()> getMemoryCleanupFunction() {
532 if (!EnableMallocTrim)
533 return nullptr;
534 // Leave a few MB at the top of the heap: it is insignificant
535 // and will most likely be needed by the main thread
536 constexpr size_t MallocTrimPad = 20'000'000;
537 return []() {
538 if (malloc_trim(MallocTrimPad))
539 vlog("Released memory via malloc_trim");
540 };
541}
542#else
543std::function<void()> getMemoryCleanupFunction() { return nullptr; }
544#endif
545
546#if CLANGD_ENABLE_REMOTE
547opt<std::string> RemoteIndexAddress{
548 "remote-index-address",
549 cat(Features),
550 desc("Address of the remote index server"),
551};
552
553// FIXME(kirillbobyrev): Should this be the location of compile_commands.json?
554opt<std::string> ProjectRoot{
555 "project-root",
556 cat(Features),
557 desc("Path to the project root. Requires remote-index-address to be set."),
558};
559#endif
560
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.
565class TestScheme : public URIScheme {
566public:
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.startswith("/"))
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);
582 }
583
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);
590
591 return URI("test", /*Authority=*/"",
592 llvm::sys::path::convert_to_slash(Body));
593 }
594
595private:
596 const static char TestDir[];
597};
598
599#ifdef _WIN32
600const char TestScheme::TestDir[] = "C:\\clangd-test";
601#else
602const char TestScheme::TestDir[] = "/clangd-test";
603#endif
604
605std::unique_ptr<SymbolIndex>
606loadExternalIndex(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));
626 };
627 if (Tasks) {
628 Tasks->runAsync("Load-index:" + External.Location,
629 std::move(IndexLoadTask));
630 } else {
631 IndexLoadTask();
632 }
633 return std::move(NewIndex);
634 }
635 llvm_unreachable("Invalid ExternalIndexKind.");
636}
637
638class FlagsConfigProvider : public config::Provider {
639private:
640 config::CompiledFragment Frag;
641
642 std::vector<config::CompiledFragment>
643 getFragments(const config::Params &,
644 config::DiagnosticCallback) const override {
645 return {Frag};
646 }
647
648public:
649 FlagsConfigProvider() {
650 llvm::Optional<Config::CDBSearchSpec> CDBSearch;
651 llvm::Optional<Config::ExternalIndexSpec> IndexSpec;
652 llvm::Optional<Config::BackgroundPolicy> BGPolicy;
653
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()};
670 }
671 } else {
672 elog("Path specified by --compile-commands-dir does not exist. The "
673 "argument will be ignored.");
674 }
675 }
676 if (!IndexFile.empty()) {
677 Config::ExternalIndexSpec Spec;
678 Spec.Kind = Spec.File;
679 Spec.Location = IndexFile;
680 IndexSpec = std::move(Spec);
681 }
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;
691 }
692#endif
693 if (!EnableBackgroundIndex) {
694 BGPolicy = Config::BackgroundPolicy::Skip;
695 }
696
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;
706 return true;
707 };
708 }
709};
710} // namespace
711} // namespace clangd
712} // namespace clang
713
714enum class ErrorResultCode : int {
715 NoShutdownRequest = 1,
716 CantRunAsXPCService = 2,
717 CheckFailed = 3
718};
719
720int main(int argc, char *argv[]) {
721 using namespace clang;
722 using namespace clang::clangd;
723
724 llvm::InitializeAllTargetInfos();
725 llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
726 llvm::sys::AddSignalHandler(
727 [](void *) {
728 ThreadCrashReporter::runCrashHandlers();
729 // Ensure ThreadCrashReporter and PrintStackTrace output is visible.
730 llvm::errs().flush();
731 },
732 nullptr);
733 llvm::sys::SetInterruptFunction(&requestShutdown);
734 llvm::cl::SetVersionPrinter([](llvm::raw_ostream &OS) {
735 OS << versionString() << "\n"
736 << "Features: " << featureString() << "\n"
737 << "Platform: " << platformString() << "\n";
738 });
739 const char *FlagsEnvVar = "CLANGD_FLAGS";
740 const char *Overview =
741 R"(clangd is a language server that provides IDE-like features to editors.
742
743It should be used via an editor plugin rather than invoked directly. For more information, see:
744 https://clangd.llvm.org/
745 https://microsoft.github.io/language-server-protocol/
746
747clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment variable.
748)";
749 llvm::cl::HideUnrelatedOptions(ClangdCategories);
750 llvm::cl::ParseCommandLineOptions(argc, argv, Overview,
751 /*Errs=*/nullptr, FlagsEnvVar);
752 if (Test) {
753 if (!Sync.getNumOccurrences())
754 Sync = true;
755 if (!CrashPragmas.getNumOccurrences())
756 CrashPragmas = true;
757 InputStyle = JSONStreamStyle::Delimited;
758 LogLevel = Logger::Verbose;
759 PrettyPrint = true;
760 // Disable config system by default to avoid external reads.
761 if (!EnableConfig.getNumOccurrences())
762 EnableConfig = false;
763 // Disable background index on lit tests by default to prevent disk writes.
764 if (!EnableBackgroundIndex.getNumOccurrences())
765 EnableBackgroundIndex = false;
766 // Ensure background index makes progress.
767 else if (EnableBackgroundIndex)
768 BackgroundQueue::preventThreadStarvationInTests();
769 }
770 if (Test || EnableTestScheme) {
771 static URISchemeRegistry::Add<TestScheme> X(
772 "test", "Test scheme for clangd lit tests.");
773 }
774 if (CrashPragmas)
775 allowCrashPragmasForTest();
776
777 if (!Sync && WorkerThreadsCount == 0) {
778 llvm::errs() << "A number of worker threads cannot be 0. Did you mean to "
779 "specify -sync?";
780 return 1;
781 }
782
783 if (Sync) {
784 if (WorkerThreadsCount.getNumOccurrences())
785 llvm::errs() << "Ignoring -j because -sync is set.\n";
786 WorkerThreadsCount = 0;
787 }
788 if (FallbackStyle.getNumOccurrences())
789 clang::format::DefaultFallbackStyle = FallbackStyle.c_str();
790
791 // Validate command line arguments.
792 llvm::Optional<llvm::raw_fd_ostream> InputMirrorStream;
793 if (!InputMirrorFile.empty()) {
794 std::error_code EC;
795 InputMirrorStream.emplace(InputMirrorFile, /*ref*/ EC,
796 llvm::sys::fs::FA_Read | llvm::sys::fs::FA_Write);
797 if (EC) {
798 InputMirrorStream.reset();
799 llvm::errs() << "Error while opening an input mirror file: "
800 << EC.message();
801 } else {
802 InputMirrorStream->SetUnbuffered();
803 }
804 }
805
806 // Setup tracing facilities if CLANGD_TRACE is set. In practice enabling a
807 // trace flag in your editor's config is annoying, launching with
808 // `CLANGD_TRACE=trace.json vim` is easier.
809 llvm::Optional<llvm::raw_fd_ostream> TracerStream;
810 std::unique_ptr<trace::EventTracer> Tracer;
811 const char *JSONTraceFile = getenv("CLANGD_TRACE");
812 const char *MetricsCSVFile = getenv("CLANGD_METRICS");
813 const char *TracerFile = JSONTraceFile ? JSONTraceFile : MetricsCSVFile;
814 if (TracerFile) {
815 std::error_code EC;
816 TracerStream.emplace(TracerFile, /*ref*/ EC,
817 llvm::sys::fs::FA_Read | llvm::sys::fs::FA_Write);
818 if (EC) {
819 TracerStream.reset();
820 llvm::errs() << "Error while opening trace file " << TracerFile << ": "
821 << EC.message();
822 } else {
823 Tracer = (TracerFile == JSONTraceFile)
824 ? trace::createJSONTracer(*TracerStream, PrettyPrint)
825 : trace::createCSVMetricTracer(*TracerStream);
826 }
827 }
828
829 llvm::Optional<trace::Session> TracingSession;
830 if (Tracer)
831 TracingSession.emplace(*Tracer);
832
833 // If a user ran `clangd` in a terminal without redirecting anything,
834 // it's somewhat likely they're confused about how to use clangd.
835 // Show them the help overview, which explains.
836 if (llvm::outs().is_displayed() && llvm::errs().is_displayed() &&
837 !CheckFile.getNumOccurrences())
838 llvm::errs() << Overview << "\n";
839 // Use buffered stream to stderr (we still flush each log message). Unbuffered
840 // stream can cause significant (non-deterministic) latency for the logger.
841 llvm::errs().SetBuffered();
842 // Don't flush stdout when logging, this would be both slow and racy!
843 llvm::errs().tie(nullptr);
844 StreamLogger Logger(llvm::errs(), LogLevel);
845 LoggingSession LoggingSession(Logger);
846 // Write some initial logs before we start doing any real work.
847 log("{0}", versionString());
848 log("Features: {0}", featureString());
849 log("PID: {0}", llvm::sys::Process::getProcessId());
850 {
851 SmallString<128> CWD;
852 if (auto Err = llvm::sys::fs::current_path(CWD))
853 log("Working directory unknown: {0}", Err.message());
854 else
855 log("Working directory: {0}", CWD);
856 }
857 for (int I = 0; I < argc; ++I)
858 log("argv[{0}]: {1}", I, argv[I]);
859 if (auto EnvFlags = llvm::sys::Process::GetEnv(FlagsEnvVar))
860 log("{0}: {1}", FlagsEnvVar, *EnvFlags);
861
862 ClangdLSPServer::Options Opts;
863 Opts.UseDirBasedCDB = (CompileArgsFrom == FilesystemCompileArgs);
864
865 switch (PCHStorage) {
866 case PCHStorageFlag::Memory:
867 Opts.StorePreamblesInMemory = true;
868 break;
869 case PCHStorageFlag::Disk:
870 Opts.StorePreamblesInMemory = false;
871 break;
872 }
873 if (!ResourceDir.empty())
874 Opts.ResourceDir = ResourceDir;
875 Opts.BuildDynamicSymbolIndex = true;
876 std::vector<std::unique_ptr<SymbolIndex>> IdxStack;
877 std::unique_ptr<SymbolIndex> StaticIdx;
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 auto PAI = createProjectAwareIndex(loadExternalIndex, Sync);
897 if (StaticIdx) {
898 IdxStack.emplace_back(std::move(StaticIdx));
899 IdxStack.emplace_back(
900 std::make_unique<MergedIndex>(PAI.get(), IdxStack.back().get()));
901 Opts.StaticIndex = IdxStack.back().get();
902 } else {
903 Opts.StaticIndex = PAI.get();
904 }
905 Opts.AsyncThreadsCount = WorkerThreadsCount;
906 Opts.FoldingRanges = FoldingRanges;
907 Opts.MemoryCleanup = getMemoryCleanupFunction();
908
909 Opts.CodeComplete.IncludeIneligibleResults = IncludeIneligibleResults;
910 Opts.CodeComplete.Limit = LimitResults;
911 if (CompletionStyle.getNumOccurrences())
912 Opts.CodeComplete.BundleOverloads = CompletionStyle != Detailed;
913 Opts.CodeComplete.ShowOrigins = ShowOrigins;
914 Opts.CodeComplete.InsertIncludes = HeaderInsertion;
915 if (!HeaderInsertionDecorators) {
916 Opts.CodeComplete.IncludeIndicator.Insert.clear();
917 Opts.CodeComplete.IncludeIndicator.NoInsert.clear();
918 }
919 Opts.CodeComplete.EnableFunctionArgSnippets = EnableFunctionArgSnippets;
920 Opts.CodeComplete.RunParser = CodeCompletionParse;
921 Opts.CodeComplete.RankingModel = RankingModel;
922
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");
937 }
938 }
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();
945
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;
959 }
960 Opts.UseDirtyHeaders = UseDirtyHeaders;
961 Opts.PreambleParseForwardingFunctions = PreambleParseForwardingFunctions;
962 Opts.QueryDriverGlobs = std::move(QueryDriverGlobs);
963 Opts.TweakFilter = [&](const Tweak &T) {
964 if (T.hidden() && !HiddenFeatures)
965 return false;
966 if (TweakList.getNumOccurrences())
967 return llvm::is_contained(TweakList, T.id());
968 return true;
969 };
970 if (ForceOffsetEncoding != OffsetEncoding::UnsupportedEncoding)
971 Opts.Encoding = ForceOffsetEncoding;
972 setIncludeCleanerAnalyzesStdlib(IncludeCleanerStdlib);
973
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;
980 }
981 log("Entering check mode (no LSP server)");
982 llvm::Optional<Range> CheckLineRange;
983 if (!CheckFileLines.empty()) {
984 uint32_t Begin = 0, End = std::numeric_limits<uint32_t>::max();
985 StringRef RangeStr(CheckFileLines);
986 bool ParseError = RangeStr.consumeInteger(0, Begin);
987 if (RangeStr.empty()) {
988 End = Begin;
989 } else {
990 ParseError |= !RangeStr.consume_front("-");
991 ParseError |= RangeStr.consumeInteger(0, End);
992 }
993 if (ParseError || !RangeStr.empty() || Begin <= 0 || End < Begin) {
994 elog(
995 "Invalid --check-lines specified. Use Begin-End format, e.g. 3-17");
996 return 1;
997 }
998 CheckLineRange = Range{Position{static_cast<int>(Begin - 1), 0},
999 Position{static_cast<int>(End), 0}};
1000 }
1001 // For now code completion is enabled any time the range is limited via
1002 // --check-lines. If it turns out to be to slow, we can introduce a
1003 // dedicated flag for that instead.
1004 return check(Path, CheckLineRange, TFS, Opts,
1005 /*EnableCodeCompletion=*/!CheckFileLines.empty())
1006 ? 0
1007 : static_cast<int>(ErrorResultCode::CheckFailed);
1008 }
1009 if (!CheckFileLines.empty()) {
1010 elog("--check-lines requires --check");
1011 return 1;
1012 }
1013
1014 // Initialize and run ClangdLSPServer.
1015 // Change stdin to binary to not lose \r\n on windows.
1016 llvm::sys::ChangeStdinToBinary();
1017 std::unique_ptr<Transport> TransportLayer;
1018 if (getenv("CLANGD_AS_XPC_SERVICE")) {
1019#if CLANGD_BUILD_XPC
1020 log("Starting LSP over XPC service");
1021 TransportLayer = newXPCTransport();
1022#else
1023 llvm::errs() << "This clangd binary wasn't built with XPC support.\n";
1024 return static_cast<int>(ErrorResultCode::CantRunAsXPCService);
1025#endif
1026 } else {
1027 log("Starting LSP over stdin/stdout");
1028 TransportLayer = newJSONTransport(
1029 stdin, llvm::outs(),
1030 InputMirrorStream ? InputMirrorStream.getPointer() : nullptr,
1031 PrettyPrint, InputStyle);
1032 }
1033 if (!PathMappingsArg.empty()) {
1034 auto Mappings = parsePathMappings(PathMappingsArg);
1035 if (!Mappings) {
1036 elog("Invalid -path-mappings: {0}", Mappings.takeError());
1037 return 1;
1038 }
1039 TransportLayer = createPathMappingTransport(std::move(TransportLayer),
1040 std::move(*Mappings));
1041 }
1042
1043 ClangdLSPServer LSPServer(*TransportLayer, TFS, Opts);
1044 llvm::set_thread_name("clangd.main");
1045 int ExitCode = LSPServer.run()
1046 ? 0
1047 : static_cast<int>(ErrorResultCode::NoShutdownRequest);
1048 log("LSP finished, exiting with status {0}", ExitCode);
1049
1050 // There may still be lingering background threads (e.g. slow requests
1051 // whose results will be dropped, background index shutting down).
1052 //
1053 // These should terminate quickly, and ~ClangdLSPServer blocks on them.
1054 // However if a bug causes them to run forever, we want to ensure the process
1055 // eventually exits. As clangd isn't directly user-facing, an editor can
1056 // "leak" clangd processes. Crashing in this case contains the damage.
1057 abortAfterTimeout(std::chrono::minutes(5));
1058
1059 return ExitCode;
1060}
1061

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