11 #include "Features.inc"
22 #include "clang/Basic/Version.h"
23 #include "clang/Format/Format.h"
24 #include "llvm/ADT/Optional.h"
25 #include "llvm/ADT/SmallString.h"
26 #include "llvm/ADT/StringRef.h"
27 #include "llvm/Support/CommandLine.h"
28 #include "llvm/Support/FileSystem.h"
29 #include "llvm/Support/Path.h"
30 #include "llvm/Support/Process.h"
31 #include "llvm/Support/Program.h"
32 #include "llvm/Support/Signals.h"
33 #include "llvm/Support/TargetSelect.h"
34 #include "llvm/Support/raw_ostream.h"
52 using llvm::cl::CommaSeparated;
54 using llvm::cl::Hidden;
58 using llvm::cl::OptionCategory;
59 using llvm::cl::values;
63 OptionCategory CompileCommands(
"clangd compilation flags options");
64 OptionCategory Features(
"clangd feature options");
65 OptionCategory Misc(
"clangd miscellaneous options");
66 OptionCategory Protocol(
"clangd protocol and logging options");
67 const OptionCategory *ClangdCategories[] = {&Features, &Protocol,
68 &CompileCommands, &Misc};
71 opt<CompileArgsFrom> CompileArgsFrom{
74 desc(
"The source of compile commands"),
75 values(clEnumValN(LSPCompileArgs,
"lsp",
76 "All compile commands come from LSP and "
77 "'compile_commands.json' files are ignored"),
78 clEnumValN(FilesystemCompileArgs,
"filesystem",
79 "All compile commands come from the "
80 "'compile_commands.json' files")),
81 init(FilesystemCompileArgs),
85 opt<Path> CompileCommandsDir{
86 "compile-commands-dir",
88 desc(
"Specify a path to look for compile_commands.json. If path "
89 "is invalid, clangd will look in the current directory and "
90 "parent paths of each source file"),
93 opt<Path> ResourceDir{
96 desc(
"Directory for system clang headers"),
101 list<std::string> QueryDriverGlobs{
103 cat(CompileCommands),
105 "Comma separated list of globs for white-listing gcc-compatible "
106 "drivers that are safe to execute. Drivers matching any of these globs "
107 "will be used to extract system includes. e.g. "
108 "/usr/bin/**/clang-*,/path/to/repo/**/g++-*"),
114 opt<bool> AllScopesCompletion{
115 "all-scopes-completion",
117 desc(
"If set to true, code completion will include index symbols that are "
118 "not defined in the scopes (e.g. "
119 "namespaces) visible from the code completion point. Such completions "
120 "can insert scope qualifiers"),
124 opt<bool> ShowOrigins{
127 desc(
"Show origins of completion items"),
128 init(CodeCompleteOptions().ShowOrigins),
132 opt<bool> EnableBackgroundIndex{
135 desc(
"Index project code in the background and persist index on disk."),
139 opt<bool> EnableClangTidy{
142 desc(
"Enable clang-tidy diagnostics"),
146 opt<std::string> ClangTidyChecks{
149 desc(
"List of clang-tidy checks to run (this will override "
150 ".clang-tidy files). Only meaningful when -clang-tidy flag is on"),
154 opt<CodeCompleteOptions::CodeCompletionParse> CodeCompletionParse{
157 desc(
"Whether the clang-parser is used for code-completion"),
159 "Block until the parser can be used"),
161 "Use text-based completion if the parser "
164 "Always used text-based completion")),
165 init(CodeCompleteOptions().RunParser),
171 opt<CompletionStyleFlag> CompletionStyle{
174 desc(
"Granularity of code completion suggestions"),
175 values(clEnumValN(Detailed,
"detailed",
176 "One completion item for each semantically distinct "
177 "completion, with full type information"),
178 clEnumValN(Bundled,
"bundled",
179 "Similar completion items (e.g. function overloads) are "
180 "combined. Type information shown where possible")),
183 opt<std::string> FallbackStyle{
186 desc(
"clang-format style to apply by default when "
187 "no .clang-format file is found"),
188 init(clang::format::DefaultFallbackStyle),
191 opt<bool> EnableFunctionArgSnippets{
192 "function-arg-placeholders",
194 desc(
"When disabled, completions contain only parentheses for "
195 "function calls. When enabled, completions also contain "
196 "placeholders for method parameters"),
197 init(CodeCompleteOptions().EnableFunctionArgSnippets),
201 opt<CodeCompleteOptions::IncludeInsertion> HeaderInsertion{
204 desc(
"Add #include directives when accepting code completions"),
205 init(CodeCompleteOptions().InsertIncludes),
208 "Include what you use. "
209 "Insert the owning header for top-level symbols, unless the "
210 "header is already directly included or the symbol is "
214 "Never insert #include directives as part of code completion")),
217 opt<bool> HeaderInsertionDecorators{
218 "header-insertion-decorators",
220 desc(
"Prepend a circular dot or space before the completion "
221 "label, depending on whether "
222 "an include line will be inserted or not"),
226 opt<bool> HiddenFeatures{
229 desc(
"Enable hidden features mostly useful to clangd developers"),
234 opt<bool> IncludeIneligibleResults{
235 "include-ineligible-results",
237 desc(
"Include ineligible completion results (e.g. private members)"),
238 init(CodeCompleteOptions().IncludeIneligibleResults),
242 opt<bool> EnableIndex{
245 desc(
"Enable index-based features. By default, clangd maintains an index "
246 "built from symbols in opened files. Global index support needs to "
247 "enabled separatedly"),
252 opt<int> LimitResults{
255 desc(
"Limit the number of results returned by clangd. "
256 "0 means no limit (default=100)"),
260 opt<bool> SuggestMissingIncludes{
261 "suggest-missing-includes",
263 desc(
"Attempts to fix diagnostic errors caused by missing "
264 "includes using index"),
268 list<std::string> TweakList{
271 desc(
"Specify a list of Tweaks to enable (only for clangd developers)."),
276 opt<bool> CrossFileRename{
279 desc(
"Enable cross-file rename feature."),
283 opt<bool> RecoveryAST{
286 desc(
"Preserve expressions in AST for broken code (C++ only)."),
287 init(ClangdServer::Options().BuildRecoveryAST),
290 opt<bool> RecoveryASTType{
293 desc(
"Preserve the type for recovery AST. Note that "
294 "this feature is experimental and may lead to crashes"),
299 opt<bool> FoldingRanges{
302 desc(
"Enable preview of FoldingRanges feature"),
307 opt<unsigned> WorkerThreadsCount{
310 desc(
"Number of async workers used by clangd. Background index also "
311 "uses this many workers."),
319 "Index file to build the static index. The file must have been created "
320 "by a compatible clangd-indexer\n"
321 "WARNING: This option is experimental only, and will be removed "
322 "eventually. Don't rely on it"),
330 desc(
"Abbreviation for -input-style=delimited -pretty -sync "
331 "-enable-test-scheme -enable-config=0 -log=verbose. "
332 "Intended to simplify lit tests"),
338 opt<PCHStorageFlag> PCHStorage{
341 desc(
"Storing PCHs in memory increases memory usages, but may "
342 "improve performance"),
344 clEnumValN(PCHStorageFlag::Disk,
"disk",
"store PCHs on disk"),
345 clEnumValN(PCHStorageFlag::Memory,
"memory",
"store PCHs in memory")),
346 init(PCHStorageFlag::Disk),
352 desc(
"Handle client requests on main thread. Background index still uses "
358 opt<JSONStreamStyle> InputStyle{
361 desc(
"Input JSON stream encoding"),
365 "messages delimited by --- lines, with # comment support")),
370 opt<bool> EnableTestScheme{
371 "enable-test-uri-scheme",
373 desc(
"Enable 'test:' URI scheme. Only use in lit tests"),
378 opt<std::string> PathMappingsArg{
382 "Translates between client paths (as seen by a remote editor) and "
383 "server paths (where clangd sees files on disk). "
384 "Comma separated list of '<client_path>=<server_path>' pairs, the "
385 "first entry matching a given path is used. "
386 "e.g. /home/project/incl=/opt/include,/home/project=/workarea/project"),
390 opt<Path> InputMirrorFile{
393 desc(
"Mirror all LSP input to the specified file. Useful for debugging"),
398 opt<Logger::Level> LogLevel{
401 desc(
"Verbosity of log messages written to stderr"),
402 values(clEnumValN(
Logger::Error,
"error",
"Error messages only"),
403 clEnumValN(
Logger::Info,
"info",
"High level execution tracing"),
408 opt<OffsetEncoding> ForceOffsetEncoding{
411 desc(
"Force the offsetEncoding used for character positions. "
412 "This bypasses negotiation via client capabilities"),
416 "Offsets are in UTF-16 code units"),
418 "Offsets are in unicode codepoints")),
422 opt<bool> PrettyPrint{
425 desc(
"Pretty-print JSON output"),
429 opt<bool> AsyncPreamble{
432 desc(
"Reuse even stale preambles, and rebuild them in the background. This "
433 "improves latency at the cost of accuracy."),
434 init(ClangdServer::Options().AsyncPreambleBuilds),
438 opt<bool> EnableConfig{
442 "Read user and project configuration from YAML files.\n"
443 "Project config is from a .clangd file in the project directory.\n"
444 "User config is from clangd/config.yaml in the following directories:\n"
445 "\tWindows: %USERPROFILE%\\AppData\\Local\n"
446 "\tMac OS: ~/Library/Preferences/\n"
447 "\tOthers: $XDG_CONFIG_HOME, usually ~/.config\n"
448 "Configuration is documented at https://clangd.llvm.org/config.html"),
456 class TestScheme :
public URIScheme {
458 llvm::Expected<std::string>
460 llvm::StringRef )
const override {
464 if (!Body.startswith(
"/"))
465 return llvm::make_error<llvm::StringError>(
466 "Expect URI body to be an absolute path starting with '/': " + Body,
467 llvm::inconvertibleErrorCode());
468 Body = Body.ltrim(
'/');
469 llvm::SmallVector<char, 16>
Path(Body.begin(), Body.end());
472 return std::string(
Path.begin(),
Path.end());
477 llvm::StringRef Body = AbsolutePath;
478 if (!Body.consume_front(TestScheme::TestDir)) {
479 return llvm::make_error<llvm::StringError>(
480 "Path " + AbsolutePath +
" doesn't start with root " + TestDir,
481 llvm::inconvertibleErrorCode());
484 return URI(
"test",
"",
485 llvm::sys::path::convert_to_slash(Body));
489 const static char TestDir[];
493 const char TestScheme::TestDir[] =
"C:\\clangd-test";
495 const char TestScheme::TestDir[] =
"/clangd-test";
507 int main(
int argc,
char *argv[]) {
508 using namespace clang;
511 llvm::InitializeAllTargetInfos();
512 llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
514 llvm::cl::SetVersionPrinter([](llvm::raw_ostream &
OS) {
515 OS << clang::getClangToolFullVersion(
"clangd") <<
"\n";
517 const char *FlagsEnvVar =
"CLANGD_FLAGS";
518 const char *Overview =
519 R
"(clangd is a language server that provides IDE-like features to editors.
521 It should be used via an editor plugin rather than invoked directly. For more information, see:
522 https://clangd.llvm.org/
523 https://microsoft.github.io/language-server-protocol/
525 clangd accepts flags on the commandline, and in the CLANGD_FLAGS environment variable.
527 llvm::cl::HideUnrelatedOptions(ClangdCategories);
528 llvm::cl::ParseCommandLineOptions(argc, argv, Overview,
529 nullptr, FlagsEnvVar);
536 if (!EnableConfig.getNumOccurrences())
537 EnableConfig =
false;
539 if (!EnableBackgroundIndex.getNumOccurrences())
540 EnableBackgroundIndex =
false;
542 else if (EnableBackgroundIndex)
545 if (Test || EnableTestScheme) {
546 static URISchemeRegistry::Add<TestScheme>
X(
547 "test",
"Test scheme for clangd lit tests.");
550 if (!Sync && WorkerThreadsCount == 0) {
551 llvm::errs() <<
"A number of worker threads cannot be 0. Did you mean to "
557 if (WorkerThreadsCount.getNumOccurrences())
558 llvm::errs() <<
"Ignoring -j because -sync is set.\n";
559 WorkerThreadsCount = 0;
561 if (FallbackStyle.getNumOccurrences())
562 clang::format::DefaultFallbackStyle = FallbackStyle.c_str();
565 llvm::Optional<llvm::raw_fd_ostream> InputMirrorStream;
566 if (!InputMirrorFile.empty()) {
568 InputMirrorStream.emplace(InputMirrorFile, EC,
569 llvm::sys::fs::FA_Read | llvm::sys::fs::FA_Write);
571 InputMirrorStream.reset();
572 llvm::errs() <<
"Error while opening an input mirror file: "
575 InputMirrorStream->SetUnbuffered();
582 llvm::Optional<llvm::raw_fd_ostream> TracerStream;
583 std::unique_ptr<trace::EventTracer>
Tracer;
584 const char *JSONTraceFile = getenv(
"CLANGD_TRACE");
585 const char *MetricsCSVFile = getenv(
"CLANGD_METRICS");
586 const char *TracerFile = JSONTraceFile ? JSONTraceFile : MetricsCSVFile;
589 TracerStream.emplace(TracerFile, EC,
590 llvm::sys::fs::FA_Read | llvm::sys::fs::FA_Write);
592 TracerStream.reset();
593 llvm::errs() <<
"Error while opening trace file " << TracerFile <<
": "
596 Tracer = (TracerFile == JSONTraceFile)
602 llvm::Optional<trace::Session> TracingSession;
604 TracingSession.emplace(*
Tracer);
609 if (llvm::outs().is_displayed() && llvm::errs().is_displayed())
610 llvm::errs() << Overview <<
"\n";
613 llvm::errs().SetBuffered();
615 llvm::errs().tie(
nullptr);
619 log(
"{0}", clang::getClangToolFullVersion(
"clangd"));
620 log(
"PID: {0}", llvm::sys::Process::getProcessId());
622 SmallString<128> CWD;
623 if (
auto Err = llvm::sys::fs::current_path(CWD))
624 log(
"Working directory unknown: {0}", Err.message());
626 log(
"Working directory: {0}", CWD);
628 for (
int I = 0; I < argc; ++I)
629 log(
"argv[{0}]: {1}", I, argv[I]);
630 if (
auto EnvFlags = llvm::sys::Process::GetEnv(FlagsEnvVar))
631 log(
"{0}: {1}", FlagsEnvVar, *EnvFlags);
635 llvm::Optional<Path> CompileCommandsDirPath;
636 if (!CompileCommandsDir.empty()) {
637 if (llvm::sys::fs::exists(CompileCommandsDir)) {
642 llvm::SmallString<128>
Path(CompileCommandsDir);
644 llvm::errs() <<
"Error while converting the relative path specified by "
645 "--compile-commands-dir to an absolute path: "
646 << EC.message() <<
". The argument will be ignored.\n";
648 CompileCommandsDirPath = std::string(
Path.str());
652 <<
"Path specified by --compile-commands-dir does not exist. The "
653 "argument will be ignored.\n";
658 switch (PCHStorage) {
659 case PCHStorageFlag::Memory:
660 Opts.StorePreamblesInMemory =
true;
662 case PCHStorageFlag::Disk:
663 Opts.StorePreamblesInMemory =
false;
666 if (!ResourceDir.empty())
667 Opts.ResourceDir = ResourceDir;
668 Opts.BuildDynamicSymbolIndex = EnableIndex;
669 Opts.BackgroundIndex = EnableBackgroundIndex;
670 std::unique_ptr<SymbolIndex> StaticIdx;
671 std::future<void> AsyncIndexLoad;
672 if (EnableIndex && !IndexFile.empty()) {
675 StaticIdx.
reset(Placeholder =
new SwapIndex(std::make_unique<MemIndex>()));
676 AsyncIndexLoad = runAsync<void>([Placeholder] {
677 if (
auto Idx =
loadIndex(IndexFile,
true))
678 Placeholder->
reset(std::move(Idx));
681 AsyncIndexLoad.wait();
683 Opts.StaticIndex = StaticIdx.get();
684 Opts.AsyncThreadsCount = WorkerThreadsCount;
685 Opts.BuildRecoveryAST = RecoveryAST;
686 Opts.PreserveRecoveryASTType = RecoveryASTType;
687 Opts.FoldingRanges = FoldingRanges;
692 if (CompletionStyle.getNumOccurrences())
696 if (!HeaderInsertionDecorators) {
706 std::vector<std::unique_ptr<config::Provider>> ProviderStack;
707 std::unique_ptr<config::Provider>
Config;
709 ProviderStack.push_back(
711 llvm::SmallString<256> UserConfig;
712 if (llvm::sys::path::user_config_directory(UserConfig)) {
713 llvm::sys::path::append(UserConfig,
"clangd",
"config.yaml");
714 vlog(
"User config file is {0}", UserConfig);
717 elog(
"Couldn't determine user config file, not loading");
719 std::vector<const config::Provider *> ProviderPointers;
720 for (
const auto& P : ProviderStack)
721 ProviderPointers.push_back(P.get());
723 Opts.ConfigProvider =
Config.get();
728 llvm::sys::ChangeStdinToBinary();
730 std::unique_ptr<Transport> TransportLayer;
731 if (getenv(
"CLANGD_AS_XPC_SERVICE")) {
733 log(
"Starting LSP over XPC service");
736 llvm::errs() <<
"This clangd binary wasn't built with XPC support.\n";
740 log(
"Starting LSP over stdin/stdout");
743 InputMirrorStream ? InputMirrorStream.getPointer() :
nullptr,
744 PrettyPrint, InputStyle);
746 if (!PathMappingsArg.empty()) {
749 elog(
"Invalid -path-mappings: {0}", Mappings.takeError());
753 std::move(*Mappings));
756 std::mutex ClangTidyOptMu;
757 std::unique_ptr<tidy::ClangTidyOptionsProvider>
758 ClangTidyOptProvider;
759 if (EnableClangTidy) {
761 EmptyDefaults.Checks.reset();
763 if (!ClangTidyChecks.empty())
764 OverrideClangTidyOptions.
Checks = ClangTidyChecks;
765 ClangTidyOptProvider = std::make_unique<tidy::FileOptionsProvider>(
769 Opts.GetClangTidyOptions = [&](llvm::vfs::FileSystem &,
770 llvm::StringRef File) {
774 std::lock_guard<std::mutex> Lock(ClangTidyOptMu);
776 Opts = ClangTidyOptProvider->getOptions(File);
787 Opts.Checks = llvm::join_items(
788 ",",
"readability-misleading-indentation",
789 "readability-deleted-default",
"bugprone-integer-division",
790 "bugprone-sizeof-expression",
"bugprone-suspicious-missing-comma",
791 "bugprone-unused-raii",
"bugprone-unused-return-value",
792 "misc-unused-using-decls",
"misc-unused-alias-decls",
793 "misc-definitions-in-headers");
798 Opts.SuggestMissingIncludes = SuggestMissingIncludes;
799 Opts.QueryDriverGlobs = std::move(QueryDriverGlobs);
801 Opts.TweakFilter = [&](
const Tweak &T) {
802 if (T.hidden() && !HiddenFeatures)
804 if (TweakList.getNumOccurrences())
805 return llvm::is_contained(TweakList, T.id());
808 llvm::Optional<OffsetEncoding> OffsetEncodingFromFlag;
810 OffsetEncodingFromFlag = ForceOffsetEncoding;
816 Opts.AsyncPreambleBuilds = AsyncPreamble;
820 CompileArgsFrom == FilesystemCompileArgs,
821 OffsetEncodingFromFlag, Opts);
822 llvm::set_thread_name(
"clangd.main");
823 int ExitCode = LSPServer.
run()
826 log(
"LSP finished, exiting with status {0}", ExitCode);