clang-tools 20.0.0git
ClangdMain.cpp
Go to the documentation of this file.
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"
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(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(""),
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(true),
157};
158
159opt<bool> ShowOrigins{
160 "debug-origin",
161 cat(Features),
162 desc("Show origins of completion items"),
163 init(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(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(llvm::ThreadPriority::Low),
187};
188
189opt<bool> EnableClangTidy{
190 "clang-tidy",
191 cat(Features),
192 desc("Enable clang-tidy diagnostics"),
193 init(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(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(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(clang::format::DefaultFallbackStyle),
243};
244
245opt<std::string> EnableFunctionArgSnippets{
246 "function-arg-placeholders",
247 cat(Features),
248 desc("When disabled (0), completions contain only parentheses for "
249 "function calls. When enabled (1), completions also contain "
250 "placeholders for method parameters"),
251 init("-1"),
252};
253
254opt<CodeCompleteOptions::IncludeInsertion> HeaderInsertion{
255 "header-insertion",
256 cat(Features),
257 desc("Add #include directives when accepting code completions"),
258 init(CodeCompleteOptions().InsertIncludes),
259 values(
260 clEnumValN(CodeCompleteOptions::IWYU, "iwyu",
261 "Include what you use. "
262 "Insert the owning header for top-level symbols, unless the "
263 "header is already directly included or the symbol is "
264 "forward-declared"),
265 clEnumValN(
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(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(true),
285};
286
287opt<bool> HiddenFeatures{
288 "hidden-features",
289 cat(Features),
290 desc("Enable hidden features mostly useful to clangd developers"),
291 init(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(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(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(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(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."),
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(""),
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(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(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(""),
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(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(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")),
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(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(""),
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(""),
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(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")),
479};
480
481opt<bool> PrettyPrint{
482 "pretty",
483 cat(Protocol),
484 desc("Pretty-print JSON output"),
485 init(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(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(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(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(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(MallocTrimPad))
532 vlog("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
554opt<bool> ExperimentalModulesSupport{
555 "experimental-modules-support",
556 cat(Features),
557 desc("Experimental support for standard c++ modules"),
558 init(false),
559};
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.starts_with("/"))
574 return error(
575 "Expect URI body to be an absolute path starting with '/': {0}",
576 Body);
577 Body = Body.ltrim('/');
578 llvm::SmallString<16> Path(Body);
579 path::native(Path);
580 fs::make_absolute(TestScheme::TestDir, Path);
581 return std::string(Path);
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, bool SupportContainedRefs) {
608 static const trace::Metric RemoteIndexUsed("used_remote_index",
609 trace::Metric::Value, "address");
610 switch (External.Kind) {
612 break;
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);
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(), SupportContainedRefs] {
624 if (auto Idx = loadIndex(File, SymbolOrigin::Static, /*UseDex=*/true,
625 SupportContainedRefs))
626 PlaceHolder->reset(std::move(Idx));
627 };
628 if (Tasks) {
629 Tasks->runAsync("Load-index:" + External.Location,
630 std::move(IndexLoadTask));
631 } else {
632 IndexLoadTask();
633 }
634 return std::move(NewIndex);
635 }
636 llvm_unreachable("Invalid ExternalIndexKind.");
637}
638
639std::optional<bool> shouldEnableFunctionArgSnippets() {
640 std::string Val = EnableFunctionArgSnippets;
641 // Accept the same values that a bool option parser would, but also accept
642 // -1 to indicate "unspecified", in which case the ArgumentListsPolicy
643 // config option will be respected.
644 if (Val == "1" || Val == "true" || Val == "True" || Val == "TRUE")
645 return true;
646 if (Val == "0" || Val == "false" || Val == "False" || Val == "FALSE")
647 return false;
648 if (Val != "-1")
649 elog("Value specified by --function-arg-placeholders is invalid. Provide a "
650 "boolean value or leave unspecified to use ArgumentListsPolicy from "
651 "config instead.");
652 return std::nullopt;
653}
654
655class FlagsConfigProvider : public config::Provider {
656private:
658
659 std::vector<config::CompiledFragment>
660 getFragments(const config::Params &,
661 config::DiagnosticCallback) const override {
662 return {Frag};
663 }
664
665public:
666 FlagsConfigProvider() {
667 std::optional<Config::CDBSearchSpec> CDBSearch;
668 std::optional<Config::ExternalIndexSpec> IndexSpec;
669 std::optional<Config::BackgroundPolicy> BGPolicy;
670 std::optional<Config::ArgumentListsPolicy> ArgumentLists;
671
672 // If --compile-commands-dir arg was invoked, check value and override
673 // default path.
674 if (!CompileCommandsDir.empty()) {
675 if (llvm::sys::fs::exists(CompileCommandsDir)) {
676 // We support passing both relative and absolute paths to the
677 // --compile-commands-dir argument, but we assume the path is absolute
678 // in the rest of clangd so we make sure the path is absolute before
679 // continuing.
680 llvm::SmallString<128> Path(CompileCommandsDir);
681 if (std::error_code EC = llvm::sys::fs::make_absolute(Path)) {
682 elog("Error while converting the relative path specified by "
683 "--compile-commands-dir to an absolute path: {0}. The argument "
684 "will be ignored.",
685 EC.message());
686 } else {
687 CDBSearch = {Config::CDBSearchSpec::FixedDir, Path.str().str()};
688 }
689 } else {
690 elog("Path specified by --compile-commands-dir does not exist. The "
691 "argument will be ignored.");
692 }
693 }
694 if (!IndexFile.empty()) {
695 Config::ExternalIndexSpec Spec;
696 Spec.Kind = Spec.File;
697 Spec.Location = IndexFile;
698 IndexSpec = std::move(Spec);
699 }
700#if CLANGD_ENABLE_REMOTE
701 if (!RemoteIndexAddress.empty()) {
702 assert(!ProjectRoot.empty() && IndexFile.empty());
703 Config::ExternalIndexSpec Spec;
704 Spec.Kind = Spec.Server;
705 Spec.Location = RemoteIndexAddress;
706 Spec.MountPoint = ProjectRoot;
707 IndexSpec = std::move(Spec);
709 }
710#endif
711 if (!EnableBackgroundIndex) {
713 }
714
715 if (std::optional<bool> Enable = shouldEnableFunctionArgSnippets()) {
716 ArgumentLists = *Enable ? Config::ArgumentListsPolicy::FullPlaceholders
718 }
719
720 Frag = [=](const config::Params &, Config &C) {
721 if (CDBSearch)
722 C.CompileFlags.CDBSearch = *CDBSearch;
723 if (IndexSpec)
724 C.Index.External = *IndexSpec;
725 if (BGPolicy)
726 C.Index.Background = *BGPolicy;
727 if (ArgumentLists)
728 C.Completion.ArgumentLists = *ArgumentLists;
729 if (AllScopesCompletion.getNumOccurrences())
730 C.Completion.AllScopes = AllScopesCompletion;
731
732 if (Test)
733 C.Index.StandardLibrary = false;
734 return true;
735 };
736 }
737};
738} // namespace
739
740enum class ErrorResultCode : int {
743 CheckFailed = 3
744};
745
746int clangdMain(int argc, char *argv[]) {
747 // Clang could run on the main thread. e.g., when the flag '-check' or '-sync'
748 // is enabled.
749 clang::noteBottomOfStack();
750 llvm::InitLLVM X(argc, argv);
751 llvm::InitializeAllTargetInfos();
752 llvm::sys::AddSignalHandler(
753 [](void *) {
755 // Ensure ThreadCrashReporter and PrintStackTrace output is visible.
756 llvm::errs().flush();
757 },
758 nullptr);
759 llvm::sys::SetInterruptFunction(&requestShutdown);
760 llvm::cl::SetVersionPrinter([](llvm::raw_ostream &OS) {
761 OS << versionString() << "\n"
762 << "Features: " << featureString() << "\n"
763 << "Platform: " << platformString() << "\n";
764 });
765 const char *FlagsEnvVar = "CLANGD_FLAGS";
766 const char *Overview =
767 R"(clangd is a language server that provides IDE-like features to editors.
768
769It should be used via an editor plugin rather than invoked directly. For more information, see:
770 https://clangd.llvm.org/
771 https://microsoft.github.io/language-server-protocol/
772
773clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment variable.
774)";
775 llvm::cl::HideUnrelatedOptions(ClangdCategories);
776 llvm::cl::ParseCommandLineOptions(argc, argv, Overview,
777 /*Errs=*/nullptr, FlagsEnvVar);
778 if (Test) {
779 if (!Sync.getNumOccurrences())
780 Sync = true;
781 if (!CrashPragmas.getNumOccurrences())
782 CrashPragmas = true;
783 InputStyle = JSONStreamStyle::Delimited;
784 LogLevel = Logger::Verbose;
785 PrettyPrint = true;
786 // Disable config system by default to avoid external reads.
787 if (!EnableConfig.getNumOccurrences())
788 EnableConfig = false;
789 // Disable background index on lit tests by default to prevent disk writes.
790 if (!EnableBackgroundIndex.getNumOccurrences())
791 EnableBackgroundIndex = false;
792 // Ensure background index makes progress.
793 else if (EnableBackgroundIndex)
795 }
796 if (Test || EnableTestScheme) {
797 static URISchemeRegistry::Add<TestScheme> X(
798 "test", "Test scheme for clangd lit tests.");
799 }
800 if (CrashPragmas)
802
803 if (!Sync && WorkerThreadsCount == 0) {
804 llvm::errs() << "A number of worker threads cannot be 0. Did you mean to "
805 "specify -sync?";
806 return 1;
807 }
808
809 if (Sync) {
810 if (WorkerThreadsCount.getNumOccurrences())
811 llvm::errs() << "Ignoring -j because -sync is set.\n";
812 WorkerThreadsCount = 0;
813 }
814 if (FallbackStyle.getNumOccurrences())
815 clang::format::DefaultFallbackStyle = FallbackStyle.c_str();
816
817 // Validate command line arguments.
818 std::optional<llvm::raw_fd_ostream> InputMirrorStream;
819 if (!InputMirrorFile.empty()) {
820 std::error_code EC;
821 InputMirrorStream.emplace(InputMirrorFile, /*ref*/ EC,
822 llvm::sys::fs::FA_Read | llvm::sys::fs::FA_Write);
823 if (EC) {
824 InputMirrorStream.reset();
825 llvm::errs() << "Error while opening an input mirror file: "
826 << EC.message();
827 } else {
828 InputMirrorStream->SetUnbuffered();
829 }
830 }
831
832#if !CLANGD_DECISION_FOREST
834 llvm::errs() << "Clangd was compiled without decision forest support.\n";
835 return 1;
836 }
837#endif
838
839 // Setup tracing facilities if CLANGD_TRACE is set. In practice enabling a
840 // trace flag in your editor's config is annoying, launching with
841 // `CLANGD_TRACE=trace.json vim` is easier.
842 std::optional<llvm::raw_fd_ostream> TracerStream;
843 std::unique_ptr<trace::EventTracer> Tracer;
844 const char *JSONTraceFile = getenv("CLANGD_TRACE");
845 const char *MetricsCSVFile = getenv("CLANGD_METRICS");
846 const char *TracerFile = JSONTraceFile ? JSONTraceFile : MetricsCSVFile;
847 if (TracerFile) {
848 std::error_code EC;
849 TracerStream.emplace(TracerFile, /*ref*/ EC,
850 llvm::sys::fs::FA_Read | llvm::sys::fs::FA_Write);
851 if (EC) {
852 TracerStream.reset();
853 llvm::errs() << "Error while opening trace file " << TracerFile << ": "
854 << EC.message();
855 } else {
856 Tracer = (TracerFile == JSONTraceFile)
857 ? trace::createJSONTracer(*TracerStream, PrettyPrint)
858 : trace::createCSVMetricTracer(*TracerStream);
859 }
860 }
861
862 std::optional<trace::Session> TracingSession;
863 if (Tracer)
864 TracingSession.emplace(*Tracer);
865
866 // If a user ran `clangd` in a terminal without redirecting anything,
867 // it's somewhat likely they're confused about how to use clangd.
868 // Show them the help overview, which explains.
869 if (llvm::outs().is_displayed() && llvm::errs().is_displayed() &&
870 !CheckFile.getNumOccurrences())
871 llvm::errs() << Overview << "\n";
872 // Use buffered stream to stderr (we still flush each log message). Unbuffered
873 // stream can cause significant (non-deterministic) latency for the logger.
874 llvm::errs().SetBuffered();
875 StreamLogger Logger(llvm::errs(), LogLevel);
877 // Write some initial logs before we start doing any real work.
878 log("{0}", versionString());
879 log("Features: {0}", featureString());
880 log("PID: {0}", llvm::sys::Process::getProcessId());
881 {
882 SmallString<128> CWD;
883 if (auto Err = llvm::sys::fs::current_path(CWD))
884 log("Working directory unknown: {0}", Err.message());
885 else
886 log("Working directory: {0}", CWD);
887 }
888 for (int I = 0; I < argc; ++I)
889 log("argv[{0}]: {1}", I, argv[I]);
890 if (auto EnvFlags = llvm::sys::Process::GetEnv(FlagsEnvVar))
891 log("{0}: {1}", FlagsEnvVar, *EnvFlags);
892
894 Opts.UseDirBasedCDB = (CompileArgsFrom == FilesystemCompileArgs);
895 Opts.EnableExperimentalModulesSupport = ExperimentalModulesSupport;
896
897 switch (PCHStorage) {
898 case PCHStorageFlag::Memory:
899 Opts.StorePreamblesInMemory = true;
900 break;
901 case PCHStorageFlag::Disk:
902 Opts.StorePreamblesInMemory = false;
903 break;
904 }
905 if (!ResourceDir.empty())
906 Opts.ResourceDir = ResourceDir;
907 Opts.BuildDynamicSymbolIndex = true;
908 std::vector<std::unique_ptr<SymbolIndex>> IdxStack;
909#if CLANGD_ENABLE_REMOTE
910 if (RemoteIndexAddress.empty() != ProjectRoot.empty()) {
911 llvm::errs() << "remote-index-address and project-path have to be "
912 "specified at the same time.";
913 return 1;
914 }
915 if (!RemoteIndexAddress.empty()) {
916 if (IndexFile.empty()) {
917 log("Connecting to remote index at {0}", RemoteIndexAddress);
918 } else {
919 elog("When enabling remote index, IndexFile should not be specified. "
920 "Only one can be used at time. Remote index will ignored.");
921 }
922 }
923#endif
924 Opts.BackgroundIndex = EnableBackgroundIndex;
925 Opts.BackgroundIndexPriority = BackgroundIndexPriority;
926 Opts.ReferencesLimit = ReferencesLimit;
927 Opts.Rename.LimitFiles = RenameFileLimit;
928 auto PAI = createProjectAwareIndex(
929 [SupportContainedRefs = Opts.EnableOutgoingCalls](
930 const Config::ExternalIndexSpec &External, AsyncTaskRunner *Tasks) {
931 return loadExternalIndex(External, Tasks, SupportContainedRefs);
932 },
933 Sync);
934 Opts.StaticIndex = PAI.get();
935 Opts.AsyncThreadsCount = WorkerThreadsCount;
936 Opts.MemoryCleanup = getMemoryCleanupFunction();
937
938 Opts.CodeComplete.IncludeIneligibleResults = IncludeIneligibleResults;
939 Opts.CodeComplete.Limit = LimitResults;
940 if (CompletionStyle.getNumOccurrences())
941 Opts.CodeComplete.BundleOverloads = CompletionStyle != Detailed;
942 Opts.CodeComplete.ShowOrigins = ShowOrigins;
943 Opts.CodeComplete.InsertIncludes = HeaderInsertion;
944 Opts.CodeComplete.ImportInsertions = ImportInsertions;
945 if (!HeaderInsertionDecorators) {
946 Opts.CodeComplete.IncludeIndicator.Insert.clear();
947 Opts.CodeComplete.IncludeIndicator.NoInsert.clear();
948 }
949 Opts.CodeComplete.RunParser = CodeCompletionParse;
950 Opts.CodeComplete.RankingModel = RankingModel;
951 // FIXME: If we're using C++20 modules, force the lookup process to load
952 // external decls, since currently the index doesn't support C++20 modules.
953 Opts.CodeComplete.ForceLoadPreamble = ExperimentalModulesSupport;
954
956 std::vector<std::unique_ptr<config::Provider>> ProviderStack;
957 std::unique_ptr<config::Provider> Config;
958 if (EnableConfig) {
959 ProviderStack.push_back(
961 llvm::SmallString<256> UserConfig;
962 if (llvm::sys::path::user_config_directory(UserConfig)) {
963 llvm::sys::path::append(UserConfig, "clangd", "config.yaml");
964 vlog("User config file is {0}", UserConfig);
965 ProviderStack.push_back(config::Provider::fromYAMLFile(
966 UserConfig, /*Directory=*/"", TFS, /*Trusted=*/true));
967 } else {
968 elog("Couldn't determine user config file, not loading");
969 }
970 }
971 ProviderStack.push_back(std::make_unique<FlagsConfigProvider>());
972 std::vector<const config::Provider *> ProviderPointers;
973 for (const auto &P : ProviderStack)
974 ProviderPointers.push_back(P.get());
975 Config = config::Provider::combine(std::move(ProviderPointers));
976 Opts.ConfigProvider = Config.get();
977
978 // Create an empty clang-tidy option.
979 TidyProvider ClangTidyOptProvider;
980 if (EnableClangTidy) {
981 std::vector<TidyProvider> Providers;
982 Providers.reserve(4 + EnableConfig);
983 Providers.push_back(provideEnvironment());
984 Providers.push_back(provideClangTidyFiles(TFS));
985 if (EnableConfig)
986 Providers.push_back(provideClangdConfig());
987 Providers.push_back(provideDefaultChecks());
988 Providers.push_back(disableUnusableChecks());
989 ClangTidyOptProvider = combine(std::move(Providers));
990 Opts.ClangTidyProvider = ClangTidyOptProvider;
991 }
992 Opts.UseDirtyHeaders = UseDirtyHeaders;
993 Opts.PreambleParseForwardingFunctions = PreambleParseForwardingFunctions;
994 Opts.ImportInsertions = ImportInsertions;
995 Opts.QueryDriverGlobs = std::move(QueryDriverGlobs);
996 Opts.TweakFilter = [&](const Tweak &T) {
997 if (T.hidden() && !HiddenFeatures)
998 return false;
999 if (TweakList.getNumOccurrences())
1000 return llvm::is_contained(TweakList, T.id());
1001 return true;
1002 };
1003 if (ForceOffsetEncoding != OffsetEncoding::UnsupportedEncoding)
1004 Opts.Encoding = ForceOffsetEncoding;
1005
1006 if (CheckFile.getNumOccurrences()) {
1007 llvm::SmallString<256> Path;
1008 if (auto Error =
1009 llvm::sys::fs::real_path(CheckFile, Path, /*expand_tilde=*/true)) {
1010 elog("Failed to resolve path {0}: {1}", CheckFile, Error.message());
1011 return 1;
1012 }
1013 log("Entering check mode (no LSP server)");
1014 return check(Path, TFS, Opts)
1015 ? 0
1016 : static_cast<int>(ErrorResultCode::CheckFailed);
1017 }
1018
1019 // Initialize and run ClangdLSPServer.
1020 // Change stdin to binary to not lose \r\n on windows.
1021 llvm::sys::ChangeStdinToBinary();
1022 std::unique_ptr<Transport> TransportLayer;
1023 if (getenv("CLANGD_AS_XPC_SERVICE")) {
1024#if CLANGD_BUILD_XPC
1025 log("Starting LSP over XPC service");
1026 TransportLayer = newXPCTransport();
1027#else
1028 llvm::errs() << "This clangd binary wasn't built with XPC support.\n";
1029 return static_cast<int>(ErrorResultCode::CantRunAsXPCService);
1030#endif
1031 } else {
1032 log("Starting LSP over stdin/stdout");
1033 TransportLayer = newJSONTransport(
1034 stdin, llvm::outs(), InputMirrorStream ? &*InputMirrorStream : nullptr,
1035 PrettyPrint, InputStyle);
1036 }
1037 if (!PathMappingsArg.empty()) {
1038 auto Mappings = parsePathMappings(PathMappingsArg);
1039 if (!Mappings) {
1040 elog("Invalid -path-mappings: {0}", Mappings.takeError());
1041 return 1;
1042 }
1043 TransportLayer = createPathMappingTransport(std::move(TransportLayer),
1044 std::move(*Mappings));
1045 }
1046
1047 ClangdLSPServer LSPServer(*TransportLayer, TFS, Opts);
1048 llvm::set_thread_name("clangd.main");
1049 int ExitCode = LSPServer.run()
1050 ? 0
1051 : static_cast<int>(ErrorResultCode::NoShutdownRequest);
1052 log("LSP finished, exiting with status {0}", ExitCode);
1053
1054 // There may still be lingering background threads (e.g. slow requests
1055 // whose results will be dropped, background index shutting down).
1056 //
1057 // These should terminate quickly, and ~ClangdLSPServer blocks on them.
1058 // However if a bug causes them to run forever, we want to ensure the process
1059 // eventually exits. As clangd isn't directly user-facing, an editor can
1060 // "leak" clangd processes. Crashing in this case contains the damage.
1061 abortAfterTimeout(std::chrono::minutes(5));
1062
1063 return ExitCode;
1064}
1065
1066} // namespace clangd
1067} // namespace clang
llvm::SmallString< 256U > Name
llvm::raw_ostream & OS
const Criteria C
Include Cleaner is clangd functionality for providing diagnostics for misuse of transitive headers an...
int X
Runs tasks on separate (detached) threads and wait for all tasks to finish.
Definition: Threading.h:108
void runAsync(const llvm::Twine &Name, llvm::unique_function< void()> Action)
Definition: Threading.cpp:81
static void preventThreadStarvationInTests()
This class exposes ClangdServer's capabilities via Language Server Protocol.
bool run()
Run LSP server loop, communicating with the Transport provided in the constructor.
Interface to allow custom logging in clangd.
Definition: Logger.h:22
Only one LoggingSession can be active at a time.
Definition: Logger.h:106
llvm::Expected< std::string > getAbsolutePath(llvm::StringRef, llvm::StringRef Body, llvm::StringRef HintPath) const override
Returns the absolute path of the file corresponding to the URI authority+body in the file system.
Definition: TestFS.cpp:111
llvm::Expected< URI > uriFromAbsolutePath(llvm::StringRef AbsolutePath) const override
Definition: TestFS.cpp:124
static void runCrashHandlers()
Calls all currently-active ThreadCrashReporters for the current thread.
An interface base for small context-sensitive refactoring actions.
Definition: Tweak.h:46
static std::unique_ptr< Provider > fromYAMLFile(llvm::StringRef AbsPath, llvm::StringRef Directory, const ThreadsafeFS &, bool Trusted=false)
Reads fragments from a single YAML file with a fixed path.
static std::unique_ptr< Provider > fromAncestorRelativeYAMLFiles(llvm::StringRef RelPath, const ThreadsafeFS &, bool Trusted=false)
static std::unique_ptr< Provider > combine(std::vector< const Provider * >)
A provider that includes fragments from all the supplied providers.
std::function< bool(const Params &, Config &)> CompiledFragment
A chunk of configuration that has been fully analyzed and is ready to apply.
llvm::function_ref< void(const llvm::SMDiagnostic &)> DiagnosticCallback
Used to report problems in parsing or interpreting a config.
std::unique_ptr< clangd::SymbolIndex > getClient(llvm::StringRef Address, llvm::StringRef ProjectRoot)
Returns an SymbolIndex client that passes requests to remote index located at Address.
Definition: Client.cpp:192
std::unique_ptr< EventTracer > createJSONTracer(llvm::raw_ostream &OS, bool Pretty)
Create an instance of EventTracer that produces an output in the Trace Event format supported by Chro...
Definition: Trace.cpp:268
std::unique_ptr< EventTracer > createCSVMetricTracer(llvm::raw_ostream &OS)
Create an instance of EventTracer that outputs metric measurements as CSV.
Definition: Trace.cpp:273
@ Error
An error message.
TidyProvider combine(std::vector< TidyProvider > Providers)
unsigned getDefaultAsyncThreadsCount()
Returns a number of a default async threads to use for TUScheduler.
std::string Path
A typedef to represent a file path.
Definition: Path.h:26
void vlog(const char *Fmt, Ts &&... Vals)
Definition: Logger.h:72
std::string platformString()
Definition: Feature.cpp:20
llvm::Error error(std::error_code EC, const char *Fmt, Ts &&... Vals)
Definition: Logger.h:79
llvm::Expected< PathMappings > parsePathMappings(llvm::StringRef RawPathMappings)
Parse the command line RawPathMappings (e.g.
std::string featureString()
Definition: Feature.cpp:33
std::unique_ptr< Transport > createPathMappingTransport(std::unique_ptr< Transport > Transp, PathMappings Mappings)
Creates a wrapping transport over Transp that applies the Mappings to all inbound and outbound LSP me...
std::unique_ptr< Transport > newXPCTransport()
void log(const char *Fmt, Ts &&... Vals)
Definition: Logger.h:67
TidyProvider provideClangTidyFiles(ThreadsafeFS &TFS)
Provider that searches for .clang-tidy configuration files in the directory tree.
void allowCrashPragmasForTest()
Respect #pragma clang __debug crash etc, which are usually disabled.
Definition: Compiler.cpp:44
std::unique_ptr< SymbolIndex > createProjectAwareIndex(IndexFactory Gen, bool Sync)
Returns an index that answers queries using external indices.
int clangdMain(int argc, char *argv[])
Definition: ClangdMain.cpp:746
void requestShutdown()
Sets a flag to indicate that clangd was sent a shutdown signal, and the transport loop should exit at...
Definition: Shutdown.cpp:28
TidyProvider disableUnusableChecks(llvm::ArrayRef< std::string > ExtraBadChecks)
Provider that will disable checks known to not work with clangd.
std::unique_ptr< Transport > newJSONTransport(std::FILE *In, llvm::raw_ostream &Out, llvm::raw_ostream *InMirror, bool Pretty, JSONStreamStyle Style)
TidyProvider provideDefaultChecks()
Provider that will enable a nice set of default checks if none are specified.
llvm::unique_function< void(tidy::ClangTidyOptions &, llvm::StringRef) const > TidyProvider
A factory to modify a tidy::ClangTidyOptions.
Definition: TidyProvider.h:23
TidyProvider provideClangdConfig()
void abortAfterTimeout(std::chrono::seconds Timeout)
Causes this process to crash if still running after Timeout.
Definition: Shutdown.cpp:18
bool check(llvm::StringRef File, const ThreadsafeFS &TFS, const ClangdLSPServer::Options &Opts)
Definition: Check.cpp:462
void elog(const char *Fmt, Ts &&... Vals)
Definition: Logger.h:61
std::string versionString()
Definition: Feature.cpp:18
std::unique_ptr< SymbolIndex > loadIndex(llvm::StringRef SymbolFilename, SymbolOrigin Origin, bool UseDex, bool SupportContainedRefs)
TidyProvider provideEnvironment()
Provider that just sets the defaults.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Some operations such as code completion produce a set of candidates.
Definition: Generators.h:58
@ AlwaysParse
Block until we can run the parser (e.g.
Definition: CodeComplete.h:121
@ ParseIfReady
Run the parser if inputs (preamble) are ready.
Definition: CodeComplete.h:124
@ NeverParse
Always use text-based completion.
Definition: CodeComplete.h:126
bool IncludeIneligibleResults
Include results that are not legal completions in the current context.
Definition: CodeComplete.h:54
CodeCompletionRankingModel RankingModel
Definition: CodeComplete.h:145
enum clang::clangd::CodeCompleteOptions::IncludeInsertion InsertIncludes
bool ImportInsertions
Whether include insertions for Objective-C code should use #import instead of #include.
Definition: CodeComplete.h:81
size_t Limit
Limit the number of results returned (0 means no limit).
Definition: CodeComplete.h:69
std::optional< bool > BundleOverloads
Combine overloads into a single completion item where possible.
Definition: CodeComplete.h:65
struct clang::clangd::CodeCompleteOptions::IncludeInsertionIndicator IncludeIndicator
enum clang::clangd::CodeCompleteOptions::CodeCompletionParse RunParser
bool ShowOrigins
Expose origins of completion items in the label (for debugging).
Definition: CodeComplete.h:91
bool ForceLoadPreamble
Force sema to load decls from preamble even if an index is provided.
Definition: CodeComplete.h:59
Describes an external index configuration.
Definition: Config.h:73
Settings that express user/project preferences and control clangd behavior.
Definition: Config.h:44
@ Delimiters
empty pair of delimiters "()" or "<>".
@ FullPlaceholders
full name of both type and variable.
@ Value
A number whose value is meaningful, and may vary over time.
Definition: Trace.h:42