diff options
Diffstat (limited to 'clangd/tool/ClangdMain.cpp')
-rw-r--r-- | clangd/tool/ClangdMain.cpp | 387 |
1 files changed, 224 insertions, 163 deletions
diff --git a/clangd/tool/ClangdMain.cpp b/clangd/tool/ClangdMain.cpp index 3d94401f..9495869b 100644 --- a/clangd/tool/ClangdMain.cpp +++ b/clangd/tool/ClangdMain.cpp @@ -1,12 +1,12 @@ //===--- ClangdMain.cpp - clangd server loop ------------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// +#include "Features.inc" #include "ClangdLSPServer.h" #include "Path.h" #include "Trace.h" @@ -25,176 +25,199 @@ #include <string> #include <thread> -using namespace llvm; -using namespace clang; -using namespace clang::clangd; - +namespace clang { +namespace clangd { // FIXME: remove this option when Dex is cheap enough. -static cl::opt<bool> UseDex("use-dex-index", - cl::desc("Use experimental Dex dynamic index."), - cl::init(false), cl::Hidden); +static llvm::cl::opt<bool> + UseDex("use-dex-index", + llvm::cl::desc("Use experimental Dex dynamic index."), + llvm::cl::init(true), llvm::cl::Hidden); -static cl::opt<Path> CompileCommandsDir( +static llvm::cl::opt<Path> CompileCommandsDir( "compile-commands-dir", - cl::desc("Specify a path to look for compile_commands.json. If path " - "is invalid, clangd will look in the current directory and " - "parent paths of each source file.")); + llvm::cl::desc("Specify a path to look for compile_commands.json. If path " + "is invalid, clangd will look in the current directory and " + "parent paths of each source file.")); -static cl::opt<unsigned> - WorkerThreadsCount("j", cl::desc("Number of async workers used by clangd"), - cl::init(getDefaultAsyncThreadsCount())); +static llvm::cl::opt<unsigned> + WorkerThreadsCount("j", + llvm::cl::desc("Number of async workers used by clangd"), + llvm::cl::init(getDefaultAsyncThreadsCount())); // FIXME: also support "plain" style where signatures are always omitted. enum CompletionStyleFlag { Detailed, Bundled }; -static cl::opt<CompletionStyleFlag> CompletionStyle( - "completion-style", cl::desc("Granularity of code completion suggestions"), - cl::values( +static llvm::cl::opt<CompletionStyleFlag> CompletionStyle( + "completion-style", + llvm::cl::desc("Granularity of code completion suggestions"), + llvm::cl::values( clEnumValN(Detailed, "detailed", "One completion item for each semantically distinct " "completion, with full type information."), clEnumValN(Bundled, "bundled", "Similar completion items (e.g. function overloads) are " "combined. Type information shown where possible.")), - cl::init(Detailed)); + llvm::cl::init(Detailed)); // FIXME: Flags are the wrong mechanism for user preferences. // We should probably read a dotfile or similar. -static cl::opt<bool> IncludeIneligibleResults( +static llvm::cl::opt<bool> IncludeIneligibleResults( "include-ineligible-results", - cl::desc("Include ineligible completion results (e.g. private members)"), - cl::init(clangd::CodeCompleteOptions().IncludeIneligibleResults), - cl::Hidden); - -static cl::opt<JSONStreamStyle> InputStyle( - "input-style", cl::desc("Input JSON stream encoding"), - cl::values( + llvm::cl::desc( + "Include ineligible completion results (e.g. private members)"), + llvm::cl::init(CodeCompleteOptions().IncludeIneligibleResults), + llvm::cl::Hidden); + +static llvm::cl::opt<JSONStreamStyle> InputStyle( + "input-style", llvm::cl::desc("Input JSON stream encoding"), + llvm::cl::values( clEnumValN(JSONStreamStyle::Standard, "standard", "usual LSP protocol"), clEnumValN(JSONStreamStyle::Delimited, "delimited", "messages delimited by --- lines, with # comment support")), - cl::init(JSONStreamStyle::Standard)); + llvm::cl::init(JSONStreamStyle::Standard)); -static cl::opt<bool> PrettyPrint("pretty", cl::desc("Pretty-print JSON output"), - cl::init(false)); +static llvm::cl::opt<bool> + PrettyPrint("pretty", llvm::cl::desc("Pretty-print JSON output"), + llvm::cl::init(false)); -static cl::opt<Logger::Level> LogLevel( - "log", cl::desc("Verbosity of log messages written to stderr"), - cl::values(clEnumValN(Logger::Error, "error", "Error messages only"), - clEnumValN(Logger::Info, "info", "High level execution tracing"), - clEnumValN(Logger::Debug, "verbose", "Low level details")), - cl::init(Logger::Info)); +static llvm::cl::opt<Logger::Level> LogLevel( + "log", llvm::cl::desc("Verbosity of log messages written to stderr"), + llvm::cl::values(clEnumValN(Logger::Error, "error", "Error messages only"), + clEnumValN(Logger::Info, "info", + "High level execution tracing"), + clEnumValN(Logger::Debug, "verbose", "Low level details")), + llvm::cl::init(Logger::Info)); -static cl::opt<bool> +static llvm::cl::opt<bool> Test("lit-test", - cl::desc("Abbreviation for -input-style=delimited -pretty " - "-run-synchronously -enable-test-scheme. " - "Intended to simplify lit tests."), - cl::init(false), cl::Hidden); + llvm::cl::desc("Abbreviation for -input-style=delimited -pretty " + "-run-synchronously -enable-test-scheme. " + "Intended to simplify lit tests."), + llvm::cl::init(false), llvm::cl::Hidden); -static cl::opt<bool> EnableTestScheme( +static llvm::cl::opt<bool> EnableTestScheme( "enable-test-uri-scheme", - cl::desc("Enable 'test:' URI scheme. Only use in lit tests."), - cl::init(false), cl::Hidden); + llvm::cl::desc("Enable 'test:' URI scheme. Only use in lit tests."), + llvm::cl::init(false), llvm::cl::Hidden); enum PCHStorageFlag { Disk, Memory }; -static cl::opt<PCHStorageFlag> PCHStorage( +static llvm::cl::opt<PCHStorageFlag> PCHStorage( "pch-storage", - cl::desc("Storing PCHs in memory increases memory usages, but may " - "improve performance"), - cl::values(clEnumValN(PCHStorageFlag::Disk, "disk", "store PCHs on disk"), - clEnumValN(PCHStorageFlag::Memory, "memory", - "store PCHs in memory")), - cl::init(PCHStorageFlag::Disk)); - -static cl::opt<int> - LimitResults("limit-results", - cl::desc("Limit the number of results returned by clangd. " - "0 means no limit."), - cl::init(100)); - -static cl::opt<bool> - RunSynchronously("run-synchronously", - cl::desc("Parse on main thread. If set, -j is ignored"), - cl::init(false), cl::Hidden); - -static cl::opt<Path> ResourceDir("resource-dir", - cl::desc("Directory for system clang headers"), - cl::init(""), cl::Hidden); - -static cl::opt<Path> InputMirrorFile( + llvm::cl::desc("Storing PCHs in memory increases memory usages, but may " + "improve performance"), + llvm::cl::values( + clEnumValN(PCHStorageFlag::Disk, "disk", "store PCHs on disk"), + clEnumValN(PCHStorageFlag::Memory, "memory", "store PCHs in memory")), + llvm::cl::init(PCHStorageFlag::Disk)); + +static llvm::cl::opt<int> LimitResults( + "limit-results", + llvm::cl::desc("Limit the number of results returned by clangd. " + "0 means no limit."), + llvm::cl::init(100)); + +static llvm::cl::opt<bool> RunSynchronously( + "run-synchronously", + llvm::cl::desc("Parse on main thread. If set, -j is ignored"), + llvm::cl::init(false), llvm::cl::Hidden); + +static llvm::cl::opt<Path> + ResourceDir("resource-dir", + llvm::cl::desc("Directory for system clang headers"), + llvm::cl::init(""), llvm::cl::Hidden); + +static llvm::cl::opt<Path> InputMirrorFile( "input-mirror-file", - cl::desc( + llvm::cl::desc( "Mirror all LSP input to the specified file. Useful for debugging."), - cl::init(""), cl::Hidden); + llvm::cl::init(""), llvm::cl::Hidden); -static cl::opt<bool> EnableIndex( +static llvm::cl::opt<bool> EnableIndex( "index", - cl::desc( + llvm::cl::desc( "Enable index-based features. By default, clangd maintains an index " "built from symbols in opened files. Global index support needs to " "enabled separatedly."), - cl::init(true), cl::Hidden); + llvm::cl::init(true), llvm::cl::Hidden); -static cl::opt<bool> AllScopesCompletion( +static llvm::cl::opt<bool> AllScopesCompletion( "all-scopes-completion", - cl::desc( + llvm::cl::desc( "If set to true, code completion will include index symbols that are " "not defined in the scopes (e.g. " "namespaces) visible from the code completion point. Such completions " "can insert scope qualifiers."), - cl::init(true)); + llvm::cl::init(true)); -static cl::opt<bool> - ShowOrigins("debug-origin", cl::desc("Show origins of completion items"), - cl::init(clangd::CodeCompleteOptions().ShowOrigins), - cl::Hidden); +static llvm::cl::opt<bool> ShowOrigins( + "debug-origin", llvm::cl::desc("Show origins of completion items"), + llvm::cl::init(CodeCompleteOptions().ShowOrigins), llvm::cl::Hidden); -static cl::opt<bool> HeaderInsertionDecorators( +static llvm::cl::opt<bool> HeaderInsertionDecorators( "header-insertion-decorators", - cl::desc("Prepend a circular dot or space before the completion " - "label, depending on whether " - "an include line will be inserted or not."), - cl::init(true)); + llvm::cl::desc("Prepend a circular dot or space before the completion " + "label, depending on whether " + "an include line will be inserted or not."), + llvm::cl::init(true)); -static cl::opt<Path> IndexFile( +static llvm::cl::opt<Path> IndexFile( "index-file", - cl::desc( + llvm::cl::desc( "Index file to build the static index. The file must have been created " "by a compatible clangd-index.\n" "WARNING: This option is experimental only, and will be removed " "eventually. Don't rely on it."), - cl::init(""), cl::Hidden); + llvm::cl::init(""), llvm::cl::Hidden); -static cl::opt<bool> EnableBackgroundIndex( +static llvm::cl::opt<bool> EnableBackgroundIndex( "background-index", - cl::desc("Index project code in the background and persist index on disk. " - "Experimental"), - cl::init(false), cl::Hidden); + llvm::cl::desc( + "Index project code in the background and persist index on disk. " + "Experimental"), + llvm::cl::init(false), llvm::cl::Hidden); -static cl::opt<int> BackgroundIndexRebuildPeriod( +static llvm::cl::opt<int> BackgroundIndexRebuildPeriod( "background-index-rebuild-period", - cl::desc( + llvm::cl::desc( "If set to non-zero, the background index rebuilds the symbol index " "periodically every X milliseconds; otherwise, the " "symbol index will be updated for each indexed file."), - cl::init(5000), cl::Hidden); + llvm::cl::init(5000), llvm::cl::Hidden); enum CompileArgsFrom { LSPCompileArgs, FilesystemCompileArgs }; -static cl::opt<CompileArgsFrom> CompileArgsFrom( - "compile_args_from", cl::desc("The source of compile commands"), - cl::values(clEnumValN(LSPCompileArgs, "lsp", - "All compile commands come from LSP and " - "'compile_commands.json' files are ignored"), - clEnumValN(FilesystemCompileArgs, "filesystem", - "All compile commands come from the " - "'compile_commands.json' files")), - cl::init(FilesystemCompileArgs), cl::Hidden); - -static cl::opt<bool> EnableFunctionArgSnippets( +static llvm::cl::opt<CompileArgsFrom> CompileArgsFrom( + "compile_args_from", llvm::cl::desc("The source of compile commands"), + llvm::cl::values(clEnumValN(LSPCompileArgs, "lsp", + "All compile commands come from LSP and " + "'compile_commands.json' files are ignored"), + clEnumValN(FilesystemCompileArgs, "filesystem", + "All compile commands come from the " + "'compile_commands.json' files")), + llvm::cl::init(FilesystemCompileArgs), llvm::cl::Hidden); + +static llvm::cl::opt<bool> EnableFunctionArgSnippets( "function-arg-placeholders", - cl::desc("When disabled, completions contain only parentheses for " - "function calls. When enabled, completions also contain " - "placeholders for method parameters."), - cl::init(clangd::CodeCompleteOptions().EnableFunctionArgSnippets)); + llvm::cl::desc("When disabled, completions contain only parentheses for " + "function calls. When enabled, completions also contain " + "placeholders for method parameters."), + llvm::cl::init(CodeCompleteOptions().EnableFunctionArgSnippets)); + +static llvm::cl::opt<std::string> ClangTidyChecks( + "clang-tidy-checks", + llvm::cl::desc( + "List of clang-tidy checks to run (this will override " + ".clang-tidy files). Only meaningful when -clang-tidy flag is on."), + llvm::cl::init("")); + +static llvm::cl::opt<bool> EnableClangTidy( + "clang-tidy", + llvm::cl::desc("Enable clang-tidy diagnostics."), + llvm::cl::init(false)); + +static llvm::cl::opt<bool> SuggestMissingIncludes( + "suggest-missing-includes", + llvm::cl::desc("Attempts to fix diagnostic errors caused by missing " + "includes using index."), + llvm::cl::init(false)); namespace { @@ -204,33 +227,34 @@ namespace { /// C:\clangd-test\a.cpp on Windows and /clangd-test/a.cpp on Unix. class TestScheme : public URIScheme { public: - Expected<std::string> getAbsolutePath(StringRef /*Authority*/, StringRef Body, - StringRef /*HintPath*/) const override { + llvm::Expected<std::string> + getAbsolutePath(llvm::StringRef /*Authority*/, llvm::StringRef Body, + llvm::StringRef /*HintPath*/) const override { using namespace llvm::sys; // Still require "/" in body to mimic file scheme, as we want lengths of an // equivalent URI in both schemes to be the same. if (!Body.startswith("/")) - return make_error<StringError>( + return llvm::make_error<llvm::StringError>( "Expect URI body to be an absolute path starting with '/': " + Body, - inconvertibleErrorCode()); + llvm::inconvertibleErrorCode()); Body = Body.ltrim('/'); - SmallVector<char, 16> Path(Body.begin(), Body.end()); + llvm::SmallVector<char, 16> Path(Body.begin(), Body.end()); path::native(Path); - auto Err = fs::make_absolute(TestScheme::TestDir, Path); - if (Err) - llvm_unreachable("Failed to make absolute path in test scheme."); + fs::make_absolute(TestScheme::TestDir, Path); return std::string(Path.begin(), Path.end()); } - Expected<URI> uriFromAbsolutePath(StringRef AbsolutePath) const override { - StringRef Body = AbsolutePath; + llvm::Expected<URI> + uriFromAbsolutePath(llvm::StringRef AbsolutePath) const override { + llvm::StringRef Body = AbsolutePath; if (!Body.consume_front(TestScheme::TestDir)) { - return make_error<StringError>("Path " + AbsolutePath + - " doesn't start with root " + TestDir, - inconvertibleErrorCode()); + return llvm::make_error<llvm::StringError>( + "Path " + AbsolutePath + " doesn't start with root " + TestDir, + llvm::inconvertibleErrorCode()); } - return URI("test", /*Authority=*/"", sys::path::convert_to_slash(Body)); + return URI("test", /*Authority=*/"", + llvm::sys::path::convert_to_slash(Body)); } private: @@ -243,14 +267,24 @@ const char TestScheme::TestDir[] = "C:\\clangd-test"; const char TestScheme::TestDir[] = "/clangd-test"; #endif -} +} // namespace +} // namespace clangd +} // namespace clang + +enum class ErrorResultCode : int { + NoShutdownRequest = 1, + CantRunAsXPCService = 2 +}; int main(int argc, char *argv[]) { - sys::PrintStackTraceOnErrorSignal(argv[0]); - cl::SetVersionPrinter([](raw_ostream &OS) { + using namespace clang; + using namespace clang::clangd; + + llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); + llvm::cl::SetVersionPrinter([](llvm::raw_ostream &OS) { OS << clang::getClangToolFullVersion("clangd") << "\n"; }); - cl::ParseCommandLineOptions( + llvm::cl::ParseCommandLineOptions( argc, argv, "clangd is a language server that provides IDE-like features to editors. " "\n\nIt should be used via an editor plugin rather than invoked " @@ -270,26 +304,27 @@ int main(int argc, char *argv[]) { } if (!RunSynchronously && WorkerThreadsCount == 0) { - errs() << "A number of worker threads cannot be 0. Did you mean to " - "specify -run-synchronously?"; + llvm::errs() << "A number of worker threads cannot be 0. Did you mean to " + "specify -run-synchronously?"; return 1; } if (RunSynchronously) { if (WorkerThreadsCount.getNumOccurrences()) - errs() << "Ignoring -j because -run-synchronously is set.\n"; + llvm::errs() << "Ignoring -j because -run-synchronously is set.\n"; WorkerThreadsCount = 0; } // Validate command line arguments. - Optional<raw_fd_ostream> InputMirrorStream; + llvm::Optional<llvm::raw_fd_ostream> InputMirrorStream; if (!InputMirrorFile.empty()) { std::error_code EC; InputMirrorStream.emplace(InputMirrorFile, /*ref*/ EC, - sys::fs::FA_Read | sys::fs::FA_Write); + llvm::sys::fs::FA_Read | llvm::sys::fs::FA_Write); if (EC) { InputMirrorStream.reset(); - errs() << "Error while opening an input mirror file: " << EC.message(); + llvm::errs() << "Error while opening an input mirror file: " + << EC.message(); } else { InputMirrorStream->SetUnbuffered(); } @@ -298,51 +333,52 @@ int main(int argc, char *argv[]) { // Setup tracing facilities if CLANGD_TRACE is set. In practice enabling a // trace flag in your editor's config is annoying, launching with // `CLANGD_TRACE=trace.json vim` is easier. - Optional<raw_fd_ostream> TraceStream; + llvm::Optional<llvm::raw_fd_ostream> TraceStream; std::unique_ptr<trace::EventTracer> Tracer; if (auto *TraceFile = getenv("CLANGD_TRACE")) { std::error_code EC; TraceStream.emplace(TraceFile, /*ref*/ EC, - sys::fs::FA_Read | sys::fs::FA_Write); + llvm::sys::fs::FA_Read | llvm::sys::fs::FA_Write); if (EC) { TraceStream.reset(); - errs() << "Error while opening trace file " << TraceFile << ": " - << EC.message(); + llvm::errs() << "Error while opening trace file " << TraceFile << ": " + << EC.message(); } else { Tracer = trace::createJSONTracer(*TraceStream, PrettyPrint); } } - Optional<trace::Session> TracingSession; + llvm::Optional<trace::Session> TracingSession; if (Tracer) TracingSession.emplace(*Tracer); // Use buffered stream to stderr (we still flush each log message). Unbuffered // stream can cause significant (non-deterministic) latency for the logger. - errs().SetBuffered(); - StreamLogger Logger(errs(), LogLevel); - clangd::LoggingSession LoggingSession(Logger); + llvm::errs().SetBuffered(); + StreamLogger Logger(llvm::errs(), LogLevel); + LoggingSession LoggingSession(Logger); // If --compile-commands-dir arg was invoked, check value and override default // path. - Optional<Path> CompileCommandsDirPath; + llvm::Optional<Path> CompileCommandsDirPath; if (!CompileCommandsDir.empty()) { - if (sys::fs::exists(CompileCommandsDir)) { + if (llvm::sys::fs::exists(CompileCommandsDir)) { // We support passing both relative and absolute paths to the // --compile-commands-dir argument, but we assume the path is absolute in // the rest of clangd so we make sure the path is absolute before // continuing. - SmallString<128> Path(CompileCommandsDir); - if (std::error_code EC = sys::fs::make_absolute(Path)) { - errs() << "Error while converting the relative path specified by " - "--compile-commands-dir to an absolute path: " - << EC.message() << ". The argument will be ignored.\n"; + llvm::SmallString<128> Path(CompileCommandsDir); + if (std::error_code EC = llvm::sys::fs::make_absolute(Path)) { + llvm::errs() << "Error while converting the relative path specified by " + "--compile-commands-dir to an absolute path: " + << EC.message() << ". The argument will be ignored.\n"; } else { CompileCommandsDirPath = Path.str(); } } else { - errs() << "Path specified by --compile-commands-dir does not exist. The " - "argument will be ignored.\n"; + llvm::errs() + << "Path specified by --compile-commands-dir does not exist. The " + "argument will be ignored.\n"; } } @@ -390,17 +426,42 @@ int main(int argc, char *argv[]) { CCOpts.EnableFunctionArgSnippets = EnableFunctionArgSnippets; CCOpts.AllScopes = AllScopesCompletion; + RealFileSystemProvider FSProvider; // Initialize and run ClangdLSPServer. // Change stdin to binary to not lose \r\n on windows. - sys::ChangeStdinToBinary(); - auto Transport = newJSONTransport( - stdin, outs(), - InputMirrorStream ? InputMirrorStream.getPointer() : nullptr, PrettyPrint, - InputStyle); + llvm::sys::ChangeStdinToBinary(); + + std::unique_ptr<Transport> TransportLayer; + if (getenv("CLANGD_AS_XPC_SERVICE")) { +#if CLANGD_BUILD_XPC + TransportLayer = newXPCTransport(); +#else + llvm::errs() << "This clangd binary wasn't built with XPC support.\n"; + return (int)ErrorResultCode::CantRunAsXPCService; +#endif + } else { + TransportLayer = newJSONTransport( + stdin, llvm::outs(), + InputMirrorStream ? InputMirrorStream.getPointer() : nullptr, + PrettyPrint, InputStyle); + } + + // Create an empty clang-tidy option. + std::unique_ptr<tidy::ClangTidyOptionsProvider> ClangTidyOptProvider; + if (EnableClangTidy) { + auto OverrideClangTidyOptions = tidy::ClangTidyOptions::getDefaults(); + OverrideClangTidyOptions.Checks = ClangTidyChecks; + ClangTidyOptProvider = llvm::make_unique<tidy::FileOptionsProvider>( + tidy::ClangTidyGlobalOptions(), + /* Default */ tidy::ClangTidyOptions::getDefaults(), + /* Override */ OverrideClangTidyOptions, FSProvider.getFileSystem()); + } + Opts.ClangTidyOptProvider = ClangTidyOptProvider.get(); + Opts.SuggestMissingIncludes = SuggestMissingIncludes; ClangdLSPServer LSPServer( - *Transport, CCOpts, CompileCommandsDirPath, + *TransportLayer, FSProvider, CCOpts, CompileCommandsDirPath, /*UseDirBasedCDB=*/CompileArgsFrom == FilesystemCompileArgs, Opts); - constexpr int NoShutdownRequestErrorCode = 1; - set_thread_name("clangd.main"); - return LSPServer.run() ? 0 : NoShutdownRequestErrorCode; + llvm::set_thread_name("clangd.main"); + return LSPServer.run() ? 0 + : static_cast<int>(ErrorResultCode::NoShutdownRequest); } |