Imported Upstream version 6.10.0.49

Former-commit-id: 1d6753294b2993e1fbf92de9366bb9544db4189b
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2020-01-16 16:38:04 +00:00
parent d94e79959b
commit 468663ddbb
48518 changed files with 2789335 additions and 61176 deletions

View File

@@ -0,0 +1,50 @@
set(LLVM_LINK_COMPONENTS
Support
)
add_clang_library(clangDaemon
ClangdLSPServer.cpp
ClangdServer.cpp
ClangdUnit.cpp
ClangdUnitStore.cpp
CodeComplete.cpp
CodeCompletionStrings.cpp
Context.cpp
Compiler.cpp
DraftStore.cpp
FuzzyMatch.cpp
GlobalCompilationDatabase.cpp
JSONExpr.cpp
JSONRPCDispatcher.cpp
Logger.cpp
Protocol.cpp
ProtocolHandlers.cpp
SourceCode.cpp
Trace.cpp
XRefs.cpp
index/FileIndex.cpp
index/Index.cpp
index/MemIndex.cpp
index/SymbolCollector.cpp
index/SymbolYAML.cpp
LINK_LIBS
clangAST
clangBasic
clangFormat
clangFrontend
clangIndex
clangLex
clangSema
clangSerialization
clangTooling
clangToolingCore
clangToolingRefactor
${LLVM_PTHREAD_LIB}
)
if( LLVM_LIB_FUZZING_ENGINE OR LLVM_USE_SANITIZE_COVERAGE )
add_subdirectory(fuzzer)
endif()
add_subdirectory(tool)
add_subdirectory(global-symbol-builder)

View File

@@ -0,0 +1,360 @@
//===--- ClangdLSPServer.cpp - LSP server ------------------------*- C++-*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===---------------------------------------------------------------------===//
#include "ClangdLSPServer.h"
#include "JSONRPCDispatcher.h"
#include "SourceCode.h"
#include "llvm/Support/FormatVariadic.h"
using namespace clang::clangd;
using namespace clang;
namespace {
TextEdit replacementToEdit(StringRef Code, const tooling::Replacement &R) {
Range ReplacementRange = {
offsetToPosition(Code, R.getOffset()),
offsetToPosition(Code, R.getOffset() + R.getLength())};
return {ReplacementRange, R.getReplacementText()};
}
std::vector<TextEdit>
replacementsToEdits(StringRef Code,
const std::vector<tooling::Replacement> &Replacements) {
// Turn the replacements into the format specified by the Language Server
// Protocol. Fuse them into one big JSON array.
std::vector<TextEdit> Edits;
for (const auto &R : Replacements)
Edits.push_back(replacementToEdit(Code, R));
return Edits;
}
std::vector<TextEdit> replacementsToEdits(StringRef Code,
const tooling::Replacements &Repls) {
std::vector<TextEdit> Edits;
for (const auto &R : Repls)
Edits.push_back(replacementToEdit(Code, R));
return Edits;
}
} // namespace
void ClangdLSPServer::onInitialize(Ctx C, InitializeParams &Params) {
reply(C, json::obj{
{{"capabilities",
json::obj{
{"textDocumentSync", 1},
{"documentFormattingProvider", true},
{"documentRangeFormattingProvider", true},
{"documentOnTypeFormattingProvider",
json::obj{
{"firstTriggerCharacter", "}"},
{"moreTriggerCharacter", {}},
}},
{"codeActionProvider", true},
{"completionProvider",
json::obj{
{"resolveProvider", false},
{"triggerCharacters", {".", ">", ":"}},
}},
{"signatureHelpProvider",
json::obj{
{"triggerCharacters", {"(", ","}},
}},
{"definitionProvider", true},
{"documentHighlightProvider", true},
{"renameProvider", true},
{"executeCommandProvider",
json::obj{
{"commands", {ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND}},
}},
}}}});
if (Params.rootUri && !Params.rootUri->file.empty())
Server.setRootPath(Params.rootUri->file);
else if (Params.rootPath && !Params.rootPath->empty())
Server.setRootPath(*Params.rootPath);
}
void ClangdLSPServer::onShutdown(Ctx C, ShutdownParams &Params) {
// Do essentially nothing, just say we're ready to exit.
ShutdownRequestReceived = true;
reply(C, nullptr);
}
void ClangdLSPServer::onExit(Ctx C, ExitParams &Params) { IsDone = true; }
void ClangdLSPServer::onDocumentDidOpen(Ctx C,
DidOpenTextDocumentParams &Params) {
if (Params.metadata && !Params.metadata->extraFlags.empty())
CDB.setExtraFlagsForFile(Params.textDocument.uri.file,
std::move(Params.metadata->extraFlags));
Server.addDocument(std::move(C), Params.textDocument.uri.file,
Params.textDocument.text);
}
void ClangdLSPServer::onDocumentDidChange(Ctx C,
DidChangeTextDocumentParams &Params) {
if (Params.contentChanges.size() != 1)
return replyError(C, ErrorCode::InvalidParams,
"can only apply one change at a time");
// We only support full syncing right now.
Server.addDocument(std::move(C), Params.textDocument.uri.file,
Params.contentChanges[0].text);
}
void ClangdLSPServer::onFileEvent(Ctx C, DidChangeWatchedFilesParams &Params) {
Server.onFileEvent(Params);
}
void ClangdLSPServer::onCommand(Ctx C, ExecuteCommandParams &Params) {
if (Params.command == ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND &&
Params.workspaceEdit) {
// The flow for "apply-fix" :
// 1. We publish a diagnostic, including fixits
// 2. The user clicks on the diagnostic, the editor asks us for code actions
// 3. We send code actions, with the fixit embedded as context
// 4. The user selects the fixit, the editor asks us to apply it
// 5. We unwrap the changes and send them back to the editor
// 6. The editor applies the changes (applyEdit), and sends us a reply (but
// we ignore it)
ApplyWorkspaceEditParams ApplyEdit;
ApplyEdit.edit = *Params.workspaceEdit;
reply(C, "Fix applied.");
// We don't need the response so id == 1 is OK.
// Ideally, we would wait for the response and if there is no error, we
// would reply success/failure to the original RPC.
call(C, "workspace/applyEdit", ApplyEdit);
} else {
// We should not get here because ExecuteCommandParams would not have
// parsed in the first place and this handler should not be called. But if
// more commands are added, this will be here has a safe guard.
replyError(
C, ErrorCode::InvalidParams,
llvm::formatv("Unsupported command \"{0}\".", Params.command).str());
}
}
void ClangdLSPServer::onRename(Ctx C, RenameParams &Params) {
auto File = Params.textDocument.uri.file;
auto Replacements = Server.rename(C, File, Params.position, Params.newName);
if (!Replacements) {
replyError(C, ErrorCode::InternalError,
llvm::toString(Replacements.takeError()));
return;
}
std::string Code = Server.getDocument(File);
std::vector<TextEdit> Edits = replacementsToEdits(Code, *Replacements);
WorkspaceEdit WE;
WE.changes = {{Params.textDocument.uri.uri, Edits}};
reply(C, WE);
}
void ClangdLSPServer::onDocumentDidClose(Ctx C,
DidCloseTextDocumentParams &Params) {
Server.removeDocument(std::move(C), Params.textDocument.uri.file);
}
void ClangdLSPServer::onDocumentOnTypeFormatting(
Ctx C, DocumentOnTypeFormattingParams &Params) {
auto File = Params.textDocument.uri.file;
std::string Code = Server.getDocument(File);
auto ReplacementsOrError = Server.formatOnType(Code, File, Params.position);
if (ReplacementsOrError)
reply(C, json::ary(replacementsToEdits(Code, ReplacementsOrError.get())));
else
replyError(C, ErrorCode::UnknownErrorCode,
llvm::toString(ReplacementsOrError.takeError()));
}
void ClangdLSPServer::onDocumentRangeFormatting(
Ctx C, DocumentRangeFormattingParams &Params) {
auto File = Params.textDocument.uri.file;
std::string Code = Server.getDocument(File);
auto ReplacementsOrError = Server.formatRange(Code, File, Params.range);
if (ReplacementsOrError)
reply(C, json::ary(replacementsToEdits(Code, ReplacementsOrError.get())));
else
replyError(C, ErrorCode::UnknownErrorCode,
llvm::toString(ReplacementsOrError.takeError()));
}
void ClangdLSPServer::onDocumentFormatting(Ctx C,
DocumentFormattingParams &Params) {
auto File = Params.textDocument.uri.file;
std::string Code = Server.getDocument(File);
auto ReplacementsOrError = Server.formatFile(Code, File);
if (ReplacementsOrError)
reply(C, json::ary(replacementsToEdits(Code, ReplacementsOrError.get())));
else
replyError(C, ErrorCode::UnknownErrorCode,
llvm::toString(ReplacementsOrError.takeError()));
}
void ClangdLSPServer::onCodeAction(Ctx C, CodeActionParams &Params) {
// We provide a code action for each diagnostic at the requested location
// which has FixIts available.
std::string Code = Server.getDocument(Params.textDocument.uri.file);
json::ary Commands;
for (Diagnostic &D : Params.context.diagnostics) {
auto Edits = getFixIts(Params.textDocument.uri.file, D);
if (!Edits.empty()) {
WorkspaceEdit WE;
WE.changes = {{Params.textDocument.uri.uri, std::move(Edits)}};
Commands.push_back(json::obj{
{"title", llvm::formatv("Apply FixIt {0}", D.message)},
{"command", ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND},
{"arguments", {WE}},
});
}
}
reply(C, std::move(Commands));
}
void ClangdLSPServer::onCompletion(Ctx C, TextDocumentPositionParams &Params) {
auto Reply = Server
.codeComplete(std::move(C), Params.textDocument.uri.file,
Position{Params.position.line,
Params.position.character},
CCOpts)
.get(); // FIXME(ibiryukov): This could be made async if we
// had an API that would allow to attach callbacks to
// futures returned by ClangdServer.
// We have std::move'd from C, now restore it from response of codeComplete.
C = std::move(Reply.first);
auto List = std::move(Reply.second.Value);
reply(C, List);
}
void ClangdLSPServer::onSignatureHelp(Ctx C,
TextDocumentPositionParams &Params) {
auto SignatureHelp = Server.signatureHelp(
C, Params.textDocument.uri.file,
Position{Params.position.line, Params.position.character});
if (!SignatureHelp)
return replyError(C, ErrorCode::InvalidParams,
llvm::toString(SignatureHelp.takeError()));
reply(C, SignatureHelp->Value);
}
void ClangdLSPServer::onGoToDefinition(Ctx C,
TextDocumentPositionParams &Params) {
auto Items = Server.findDefinitions(
C, Params.textDocument.uri.file,
Position{Params.position.line, Params.position.character});
if (!Items)
return replyError(C, ErrorCode::InvalidParams,
llvm::toString(Items.takeError()));
reply(C, json::ary(Items->Value));
}
void ClangdLSPServer::onSwitchSourceHeader(Ctx C,
TextDocumentIdentifier &Params) {
llvm::Optional<Path> Result = Server.switchSourceHeader(Params.uri.file);
std::string ResultUri;
reply(C, Result ? URI::fromFile(*Result).uri : "");
}
void ClangdLSPServer::onDocumentHighlight(Ctx C,
TextDocumentPositionParams &Params) {
auto Highlights = Server.findDocumentHighlights(
C, Params.textDocument.uri.file,
Position{Params.position.line, Params.position.character});
if (!Highlights) {
replyError(C, ErrorCode::InternalError,
llvm::toString(Highlights.takeError()));
return;
}
reply(C, json::ary(Highlights->Value));
}
ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, unsigned AsyncThreadsCount,
bool StorePreamblesInMemory,
const clangd::CodeCompleteOptions &CCOpts,
llvm::Optional<StringRef> ResourceDir,
llvm::Optional<Path> CompileCommandsDir,
bool BuildDynamicSymbolIndex)
: Out(Out), CDB(std::move(CompileCommandsDir)), CCOpts(CCOpts),
Server(CDB, /*DiagConsumer=*/*this, FSProvider, AsyncThreadsCount,
StorePreamblesInMemory, BuildDynamicSymbolIndex, ResourceDir) {}
bool ClangdLSPServer::run(std::istream &In) {
assert(!IsDone && "Run was called before");
// Set up JSONRPCDispatcher.
JSONRPCDispatcher Dispatcher([](Context Ctx, const json::Expr &Params) {
replyError(Ctx, ErrorCode::MethodNotFound, "method not found");
});
registerCallbackHandlers(Dispatcher, Out, /*Callbacks=*/*this);
// Run the Language Server loop.
runLanguageServerLoop(In, Out, Dispatcher, IsDone);
// Make sure IsDone is set to true after this method exits to ensure assertion
// at the start of the method fires if it's ever executed again.
IsDone = true;
return ShutdownRequestReceived;
}
std::vector<TextEdit> ClangdLSPServer::getFixIts(StringRef File,
const clangd::Diagnostic &D) {
std::lock_guard<std::mutex> Lock(FixItsMutex);
auto DiagToFixItsIter = FixItsMap.find(File);
if (DiagToFixItsIter == FixItsMap.end())
return {};
const auto &DiagToFixItsMap = DiagToFixItsIter->second;
auto FixItsIter = DiagToFixItsMap.find(D);
if (FixItsIter == DiagToFixItsMap.end())
return {};
return FixItsIter->second;
}
void ClangdLSPServer::onDiagnosticsReady(
PathRef File, Tagged<std::vector<DiagWithFixIts>> Diagnostics) {
json::ary DiagnosticsJSON;
DiagnosticToReplacementMap LocalFixIts; // Temporary storage
for (auto &DiagWithFixes : Diagnostics.Value) {
auto Diag = DiagWithFixes.Diag;
DiagnosticsJSON.push_back(json::obj{
{"range", Diag.range},
{"severity", Diag.severity},
{"message", Diag.message},
});
// We convert to Replacements to become independent of the SourceManager.
auto &FixItsForDiagnostic = LocalFixIts[Diag];
std::copy(DiagWithFixes.FixIts.begin(), DiagWithFixes.FixIts.end(),
std::back_inserter(FixItsForDiagnostic));
}
// Cache FixIts
{
// FIXME(ibiryukov): should be deleted when documents are removed
std::lock_guard<std::mutex> Lock(FixItsMutex);
FixItsMap[File] = LocalFixIts;
}
// Publish diagnostics.
Out.writeMessage(json::obj{
{"jsonrpc", "2.0"},
{"method", "textDocument/publishDiagnostics"},
{"params",
json::obj{
{"uri", URI::fromFile(File)},
{"diagnostics", std::move(DiagnosticsJSON)},
}},
});
}

View File

@@ -0,0 +1,112 @@
//===--- ClangdLSPServer.h - LSP server --------------------------*- C++-*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===---------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDLSPSERVER_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDLSPSERVER_H
#include "ClangdServer.h"
#include "GlobalCompilationDatabase.h"
#include "Path.h"
#include "Protocol.h"
#include "ProtocolHandlers.h"
#include "clang/Tooling/Core/Replacement.h"
#include "llvm/ADT/Optional.h"
namespace clang {
namespace clangd {
class JSONOutput;
/// This class provides implementation of an LSP server, glueing the JSON
/// dispatch and ClangdServer together.
class ClangdLSPServer : private DiagnosticsConsumer, private ProtocolCallbacks {
public:
/// If \p CompileCommandsDir has a value, compile_commands.json will be
/// loaded only from \p CompileCommandsDir. Otherwise, clangd will look
/// for compile_commands.json in all parent directories of each file.
ClangdLSPServer(JSONOutput &Out, unsigned AsyncThreadsCount,
bool StorePreamblesInMemory,
const clangd::CodeCompleteOptions &CCOpts,
llvm::Optional<StringRef> ResourceDir,
llvm::Optional<Path> CompileCommandsDir,
bool BuildDynamicSymbolIndex);
/// Run LSP server loop, receiving input for it from \p In. \p In must be
/// opened in binary mode. Output will be written using Out variable passed to
/// class constructor. This method must not be executed more than once for
/// each instance of ClangdLSPServer.
///
/// \return Wether we received a 'shutdown' request before an 'exit' request
bool run(std::istream &In);
private:
// Implement DiagnosticsConsumer.
virtual void
onDiagnosticsReady(PathRef File,
Tagged<std::vector<DiagWithFixIts>> Diagnostics) override;
// Implement ProtocolCallbacks.
void onInitialize(Ctx C, InitializeParams &Params) override;
void onShutdown(Ctx C, ShutdownParams &Params) override;
void onExit(Ctx C, ExitParams &Params) override;
void onDocumentDidOpen(Ctx C, DidOpenTextDocumentParams &Params) override;
void onDocumentDidChange(Ctx C, DidChangeTextDocumentParams &Params) override;
void onDocumentDidClose(Ctx C, DidCloseTextDocumentParams &Params) override;
void
onDocumentOnTypeFormatting(Ctx C,
DocumentOnTypeFormattingParams &Params) override;
void
onDocumentRangeFormatting(Ctx C,
DocumentRangeFormattingParams &Params) override;
void onDocumentFormatting(Ctx C, DocumentFormattingParams &Params) override;
void onCodeAction(Ctx C, CodeActionParams &Params) override;
void onCompletion(Ctx C, TextDocumentPositionParams &Params) override;
void onSignatureHelp(Ctx C, TextDocumentPositionParams &Params) override;
void onGoToDefinition(Ctx C, TextDocumentPositionParams &Params) override;
void onSwitchSourceHeader(Ctx C, TextDocumentIdentifier &Params) override;
void onDocumentHighlight(Ctx C, TextDocumentPositionParams &Params) override;
void onFileEvent(Ctx C, DidChangeWatchedFilesParams &Params) override;
void onCommand(Ctx C, ExecuteCommandParams &Params) override;
void onRename(Ctx C, RenameParams &Parames) override;
std::vector<TextEdit> getFixIts(StringRef File, const clangd::Diagnostic &D);
JSONOutput &Out;
/// Used to indicate that the 'shutdown' request was received from the
/// Language Server client.
bool ShutdownRequestReceived = false;
/// Used to indicate that the 'exit' notification was received from the
/// Language Server client.
/// It's used to break out of the LSP parsing loop.
bool IsDone = false;
std::mutex FixItsMutex;
typedef std::map<clangd::Diagnostic, std::vector<TextEdit>,
LSPDiagnosticCompare>
DiagnosticToReplacementMap;
/// Caches FixIts per file and diagnostics
llvm::StringMap<DiagnosticToReplacementMap> FixItsMap;
// Various ClangdServer parameters go here. It's important they're created
// before ClangdServer.
DirectoryBasedGlobalCompilationDatabase CDB;
RealFileSystemProvider FSProvider;
/// Options used for code completion
clangd::CodeCompleteOptions CCOpts;
// Server must be the last member of the class to allow its destructor to exit
// the worker thread that may otherwise run an async callback on partially
// destructed instance of ClangdLSPServer.
ClangdServer Server;
};
} // namespace clangd
} // namespace clang
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,362 @@
//===--- ClangdServer.h - Main clangd server code ----------------*- C++-*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDSERVER_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDSERVER_H
#include "ClangdUnit.h"
#include "ClangdUnitStore.h"
#include "CodeComplete.h"
#include "DraftStore.h"
#include "Function.h"
#include "GlobalCompilationDatabase.h"
#include "Protocol.h"
#include "index/FileIndex.h"
#include "clang/Tooling/CompilationDatabase.h"
#include "clang/Tooling/Core/Replacement.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/StringRef.h"
#include <condition_variable>
#include <functional>
#include <mutex>
#include <string>
#include <thread>
#include <type_traits>
#include <utility>
namespace clang {
class PCHContainerOperations;
namespace clangd {
/// A tag supplied by the FileSytemProvider.
typedef std::string VFSTag;
/// A value of an arbitrary type and VFSTag that was supplied by the
/// FileSystemProvider when this value was computed.
template <class T> class Tagged {
public:
// MSVC requires future<> arguments to be default-constructible.
Tagged() = default;
template <class U>
Tagged(U &&Value, VFSTag Tag)
: Value(std::forward<U>(Value)), Tag(std::move(Tag)) {}
template <class U>
Tagged(const Tagged<U> &Other) : Value(Other.Value), Tag(Other.Tag) {}
template <class U>
Tagged(Tagged<U> &&Other)
: Value(std::move(Other.Value)), Tag(std::move(Other.Tag)) {}
T Value = T();
VFSTag Tag = VFSTag();
};
template <class T>
Tagged<typename std::decay<T>::type> make_tagged(T &&Value, VFSTag Tag) {
return Tagged<typename std::decay<T>::type>(std::forward<T>(Value), Tag);
}
class DiagnosticsConsumer {
public:
virtual ~DiagnosticsConsumer() = default;
/// Called by ClangdServer when \p Diagnostics for \p File are ready.
virtual void
onDiagnosticsReady(PathRef File,
Tagged<std::vector<DiagWithFixIts>> Diagnostics) = 0;
};
class FileSystemProvider {
public:
virtual ~FileSystemProvider() = default;
/// Called by ClangdServer to obtain a vfs::FileSystem to be used for parsing.
/// Name of the file that will be parsed is passed in \p File.
///
/// \return A filesystem that will be used for all file accesses in clangd.
/// A Tag returned by this method will be propagated to all results of clangd
/// that will use this filesystem.
virtual Tagged<IntrusiveRefCntPtr<vfs::FileSystem>>
getTaggedFileSystem(PathRef File) = 0;
};
class RealFileSystemProvider : public FileSystemProvider {
public:
/// \return getRealFileSystem() tagged with default tag, i.e. VFSTag()
Tagged<IntrusiveRefCntPtr<vfs::FileSystem>>
getTaggedFileSystem(PathRef File) override;
};
class ClangdServer;
/// Returns a number of a default async threads to use for ClangdScheduler.
/// Returned value is always >= 1 (i.e. will not cause requests to be processed
/// synchronously).
unsigned getDefaultAsyncThreadsCount();
/// Handles running WorkerRequests of ClangdServer on a number of worker
/// threads.
class ClangdScheduler {
public:
/// If \p AsyncThreadsCount is 0, requests added using addToFront and addToEnd
/// will be processed synchronously on the calling thread.
// Otherwise, \p AsyncThreadsCount threads will be created to schedule the
// requests.
ClangdScheduler(unsigned AsyncThreadsCount);
~ClangdScheduler();
/// Add a new request to run function \p F with args \p As to the start of the
/// queue. The request will be run on a separate thread.
template <class Func, class... Args>
void addToFront(Func &&F, Args &&... As) {
if (RunSynchronously) {
std::forward<Func>(F)(std::forward<Args>(As)...);
return;
}
{
std::lock_guard<std::mutex> Lock(Mutex);
RequestQueue.push_front(
BindWithForward(std::forward<Func>(F), std::forward<Args>(As)...));
}
RequestCV.notify_one();
}
/// Add a new request to run function \p F with args \p As to the end of the
/// queue. The request will be run on a separate thread.
template <class Func, class... Args> void addToEnd(Func &&F, Args &&... As) {
if (RunSynchronously) {
std::forward<Func>(F)(std::forward<Args>(As)...);
return;
}
{
std::lock_guard<std::mutex> Lock(Mutex);
RequestQueue.push_back(
BindWithForward(std::forward<Func>(F), std::forward<Args>(As)...));
}
RequestCV.notify_one();
}
private:
bool RunSynchronously;
std::mutex Mutex;
/// We run some tasks on separate threads(parsing, CppFile cleanup).
/// These threads looks into RequestQueue to find requests to handle and
/// terminate when Done is set to true.
std::vector<std::thread> Workers;
/// Setting Done to true will make the worker threads terminate.
bool Done = false;
/// A queue of requests. Elements of this vector are async computations (i.e.
/// results of calling std::async(std::launch::deferred, ...)).
std::deque<UniqueFunction<void()>> RequestQueue;
/// Condition variable to wake up worker threads.
std::condition_variable RequestCV;
};
/// Provides API to manage ASTs for a collection of C++ files and request
/// various language features.
/// Currently supports async diagnostics, code completion, formatting and goto
/// definition.
class ClangdServer {
public:
/// Creates a new ClangdServer instance.
/// To process parsing requests asynchronously, ClangdServer will spawn \p
/// AsyncThreadsCount worker threads. However, if \p AsyncThreadsCount is 0,
/// all requests will be processed on the calling thread.
///
/// ClangdServer uses \p FSProvider to get an instance of vfs::FileSystem for
/// each parsing request. Results of code completion and diagnostics also
/// include a tag, that \p FSProvider returns along with the vfs::FileSystem.
///
/// The value of \p ResourceDir will be used to search for internal headers
/// (overriding defaults and -resource-dir compiler flag). If \p ResourceDir
/// is None, ClangdServer will call CompilerInvocation::GetResourcePath() to
/// obtain the standard resource directory.
///
/// ClangdServer uses \p CDB to obtain compilation arguments for parsing. Note
/// that ClangdServer only obtains compilation arguments once for each newly
/// added file (i.e., when processing a first call to addDocument) and reuses
/// those arguments for subsequent reparses. However, ClangdServer will check
/// if compilation arguments changed on calls to forceReparse().
///
/// After each parsing request finishes, ClangdServer reports diagnostics to
/// \p DiagConsumer. Note that a callback to \p DiagConsumer happens on a
/// worker thread. Therefore, instances of \p DiagConsumer must properly
/// synchronize access to shared state.
///
/// \p StorePreamblesInMemory defines whether the Preambles generated by
/// clangd are stored in-memory or on disk.
///
/// If \p BuildDynamicSymbolIndex is true, ClangdServer builds a dynamic
/// in-memory index for symbols in all opened files and uses the index to
/// augment code completion results.
ClangdServer(GlobalCompilationDatabase &CDB,
DiagnosticsConsumer &DiagConsumer,
FileSystemProvider &FSProvider, unsigned AsyncThreadsCount,
bool StorePreamblesInMemory,
bool BuildDynamicSymbolIndex = false,
llvm::Optional<StringRef> ResourceDir = llvm::None);
/// Set the root path of the workspace.
void setRootPath(PathRef RootPath);
/// Add a \p File to the list of tracked C++ files or update the contents if
/// \p File is already tracked. Also schedules parsing of the AST for it on a
/// separate thread. When the parsing is complete, DiagConsumer passed in
/// constructor will receive onDiagnosticsReady callback.
/// \return A future that will become ready when the rebuild (including
/// diagnostics) is finished.
std::future<Context> addDocument(Context Ctx, PathRef File,
StringRef Contents);
/// Remove \p File from list of tracked files, schedule a request to free
/// resources associated with it.
/// \return A future that will become ready when the file is removed and all
/// associated resources are freed.
std::future<Context> removeDocument(Context Ctx, PathRef File);
/// Force \p File to be reparsed using the latest contents.
/// Will also check if CompileCommand, provided by GlobalCompilationDatabase
/// for \p File has changed. If it has, will remove currently stored Preamble
/// and AST and rebuild them from scratch.
std::future<Context> forceReparse(Context Ctx, PathRef File);
/// DEPRECATED. Please use a callback-based version, this API is deprecated
/// and will soon be removed.
///
/// Run code completion for \p File at \p Pos.
///
/// Request is processed asynchronously. You can use the returned future to
/// wait for the results of the async request.
///
/// If \p OverridenContents is not None, they will used only for code
/// completion, i.e. no diagnostics update will be scheduled and a draft for
/// \p File will not be updated. If \p OverridenContents is None, contents of
/// the current draft for \p File will be used. If \p UsedFS is non-null, it
/// will be overwritten by vfs::FileSystem used for completion.
///
/// This method should only be called for currently tracked files. However, it
/// is safe to call removeDocument for \p File after this method returns, even
/// while returned future is not yet ready.
std::future<std::pair<Context, Tagged<CompletionList>>>
codeComplete(Context Ctx, PathRef File, Position Pos,
const clangd::CodeCompleteOptions &Opts,
llvm::Optional<StringRef> OverridenContents = llvm::None,
IntrusiveRefCntPtr<vfs::FileSystem> *UsedFS = nullptr);
/// A version of `codeComplete` that runs \p Callback on the processing thread
/// when codeComplete results become available.
void
codeComplete(Context Ctx, PathRef File, Position Pos,
const clangd::CodeCompleteOptions &Opts,
UniqueFunction<void(Context, Tagged<CompletionList>)> Callback,
llvm::Optional<StringRef> OverridenContents = llvm::None,
IntrusiveRefCntPtr<vfs::FileSystem> *UsedFS = nullptr);
/// Provide signature help for \p File at \p Pos. If \p OverridenContents is
/// not None, they will used only for signature help, i.e. no diagnostics
/// update will be scheduled and a draft for \p File will not be updated. If
/// \p OverridenContents is None, contents of the current draft for \p File
/// will be used. If \p UsedFS is non-null, it will be overwritten by
/// vfs::FileSystem used for signature help. This method should only be called
/// for currently tracked files.
llvm::Expected<Tagged<SignatureHelp>>
signatureHelp(const Context &Ctx, PathRef File, Position Pos,
llvm::Optional<StringRef> OverridenContents = llvm::None,
IntrusiveRefCntPtr<vfs::FileSystem> *UsedFS = nullptr);
/// Get definition of symbol at a specified \p Line and \p Column in \p File.
llvm::Expected<Tagged<std::vector<Location>>>
findDefinitions(const Context &Ctx, PathRef File, Position Pos);
/// Helper function that returns a path to the corresponding source file when
/// given a header file and vice versa.
llvm::Optional<Path> switchSourceHeader(PathRef Path);
/// Get document highlights for a given position.
llvm::Expected<Tagged<std::vector<DocumentHighlight>>>
findDocumentHighlights(const Context &Ctx, PathRef File, Position Pos);
/// Run formatting for \p Rng inside \p File with content \p Code.
llvm::Expected<tooling::Replacements> formatRange(StringRef Code,
PathRef File, Range Rng);
/// Run formatting for the whole \p File with content \p Code.
llvm::Expected<tooling::Replacements> formatFile(StringRef Code,
PathRef File);
/// Run formatting after a character was typed at \p Pos in \p File with
/// content \p Code.
llvm::Expected<tooling::Replacements>
formatOnType(StringRef Code, PathRef File, Position Pos);
/// Rename all occurrences of the symbol at the \p Pos in \p File to
/// \p NewName.
Expected<std::vector<tooling::Replacement>> rename(const Context &Ctx,
PathRef File, Position Pos,
llvm::StringRef NewName);
/// Gets current document contents for \p File. \p File must point to a
/// currently tracked file.
/// FIXME(ibiryukov): This function is here to allow offset-to-Position
/// conversions in outside code, maybe there's a way to get rid of it.
std::string getDocument(PathRef File);
/// Only for testing purposes.
/// Waits until all requests to worker thread are finished and dumps AST for
/// \p File. \p File must be in the list of added documents.
std::string dumpAST(PathRef File);
/// Called when an event occurs for a watched file in the workspace.
void onFileEvent(const DidChangeWatchedFilesParams &Params);
private:
/// FIXME: This stats several files to find a .clang-format file. I/O can be
/// slow. Think of a way to cache this.
llvm::Expected<tooling::Replacements>
formatCode(llvm::StringRef Code, PathRef File,
ArrayRef<tooling::Range> Ranges);
std::future<Context>
scheduleReparseAndDiags(Context Ctx, PathRef File, VersionedDraft Contents,
std::shared_ptr<CppFile> Resources,
Tagged<IntrusiveRefCntPtr<vfs::FileSystem>> TaggedFS);
std::future<Context>
scheduleCancelRebuild(Context Ctx, std::shared_ptr<CppFile> Resources);
GlobalCompilationDatabase &CDB;
DiagnosticsConsumer &DiagConsumer;
FileSystemProvider &FSProvider;
DraftStore DraftMgr;
/// If set, this manages index for symbols in opened files.
std::unique_ptr<FileIndex> FileIdx;
CppFileCollection Units;
std::string ResourceDir;
// If set, this represents the workspace path.
llvm::Optional<std::string> RootPath;
std::shared_ptr<PCHContainerOperations> PCHs;
bool StorePreamblesInMemory;
/// Used to serialize diagnostic callbacks.
/// FIXME(ibiryukov): get rid of an extra map and put all version counters
/// into CppFile.
std::mutex DiagnosticsMutex;
/// Maps from a filename to the latest version of reported diagnostics.
llvm::StringMap<DocVersion> ReportedDiagnosticVersions;
// WorkScheduler has to be the last member, because its destructor has to be
// called before all other members to stop the worker thread that references
// ClangdServer
ClangdScheduler WorkScheduler;
};
} // namespace clangd
} // namespace clang
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,275 @@
//===--- ClangdUnit.h -------------------------------------------*- C++-*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===---------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDUNIT_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDUNIT_H
#include "Context.h"
#include "Function.h"
#include "Path.h"
#include "Protocol.h"
#include "clang/Frontend/FrontendAction.h"
#include "clang/Frontend/PrecompiledPreamble.h"
#include "clang/Serialization/ASTBitCodes.h"
#include "clang/Tooling/CompilationDatabase.h"
#include "clang/Tooling/Core/Replacement.h"
#include <atomic>
#include <future>
#include <memory>
#include <mutex>
namespace llvm {
class raw_ostream;
}
namespace clang {
class PCHContainerOperations;
namespace vfs {
class FileSystem;
}
namespace tooling {
struct CompileCommand;
}
namespace clangd {
/// A diagnostic with its FixIts.
struct DiagWithFixIts {
clangd::Diagnostic Diag;
llvm::SmallVector<TextEdit, 1> FixIts;
};
// Stores Preamble and associated data.
struct PreambleData {
PreambleData(PrecompiledPreamble Preamble,
std::vector<serialization::DeclID> TopLevelDeclIDs,
std::vector<DiagWithFixIts> Diags);
PrecompiledPreamble Preamble;
std::vector<serialization::DeclID> TopLevelDeclIDs;
std::vector<DiagWithFixIts> Diags;
};
/// Stores and provides access to parsed AST.
class ParsedAST {
public:
/// Attempts to run Clang and store parsed AST. If \p Preamble is non-null
/// it is reused during parsing.
static llvm::Optional<ParsedAST>
Build(const Context &Ctx, std::unique_ptr<clang::CompilerInvocation> CI,
std::shared_ptr<const PreambleData> Preamble,
std::unique_ptr<llvm::MemoryBuffer> Buffer,
std::shared_ptr<PCHContainerOperations> PCHs,
IntrusiveRefCntPtr<vfs::FileSystem> VFS);
ParsedAST(ParsedAST &&Other);
ParsedAST &operator=(ParsedAST &&Other);
~ParsedAST();
ASTContext &getASTContext();
const ASTContext &getASTContext() const;
Preprocessor &getPreprocessor();
const Preprocessor &getPreprocessor() const;
/// This function returns all top-level decls, including those that come
/// from Preamble. Decls, coming from Preamble, have to be deserialized, so
/// this call might be expensive.
ArrayRef<const Decl *> getTopLevelDecls();
const std::vector<DiagWithFixIts> &getDiagnostics() const;
private:
ParsedAST(std::shared_ptr<const PreambleData> Preamble,
std::unique_ptr<CompilerInstance> Clang,
std::unique_ptr<FrontendAction> Action,
std::vector<const Decl *> TopLevelDecls,
std::vector<DiagWithFixIts> Diags);
private:
void ensurePreambleDeclsDeserialized();
// In-memory preambles must outlive the AST, it is important that this member
// goes before Clang and Action.
std::shared_ptr<const PreambleData> Preamble;
// We store an "incomplete" FrontendAction (i.e. no EndSourceFile was called
// on it) and CompilerInstance used to run it. That way we don't have to do
// complex memory management of all Clang structures on our own. (They are
// stored in CompilerInstance and cleaned up by
// FrontendAction.EndSourceFile).
std::unique_ptr<CompilerInstance> Clang;
std::unique_ptr<FrontendAction> Action;
// Data, stored after parsing.
std::vector<DiagWithFixIts> Diags;
std::vector<const Decl *> TopLevelDecls;
bool PreambleDeclsDeserialized;
};
// Provides thread-safe access to ParsedAST.
class ParsedASTWrapper {
public:
ParsedASTWrapper(ParsedASTWrapper &&Wrapper);
ParsedASTWrapper(llvm::Optional<ParsedAST> AST);
/// Runs \p F on wrapped ParsedAST under lock. Ensures it is not accessed
/// concurrently.
template <class Func> void runUnderLock(Func F) const {
std::lock_guard<std::mutex> Lock(Mutex);
F(AST ? AST.getPointer() : nullptr);
}
private:
// This wrapper is used as an argument to std::shared_future (and it returns a
// const ref in get()), but we need to have non-const ref in order to
// implement some features.
mutable std::mutex Mutex;
mutable llvm::Optional<ParsedAST> AST;
};
using ASTParsedCallback =
std::function<void(const Context &Ctx, PathRef Path, ParsedAST *)>;
/// Manages resources, required by clangd. Allows to rebuild file with new
/// contents, and provides AST and Preamble for it.
class CppFile : public std::enable_shared_from_this<CppFile> {
public:
// We only allow to create CppFile as shared_ptr, because a future returned by
// deferRebuild will hold references to it.
static std::shared_ptr<CppFile>
Create(PathRef FileName, tooling::CompileCommand Command,
bool StorePreamblesInMemory,
std::shared_ptr<PCHContainerOperations> PCHs,
ASTParsedCallback ASTCallback);
private:
CppFile(PathRef FileName, tooling::CompileCommand Command,
bool StorePreamblesInMemory,
std::shared_ptr<PCHContainerOperations> PCHs,
ASTParsedCallback ASTCallback);
public:
CppFile(CppFile const &) = delete;
CppFile(CppFile &&) = delete;
/// Cancels a scheduled rebuild, if any, and sets AST and Preamble to nulls.
/// If a rebuild is in progress, will wait for it to finish.
void cancelRebuild();
/// Similar to deferRebuild, but sets both Preamble and AST to nulls instead
/// of doing an actual parsing. Returned function is a deferred computation
/// that will wait for any ongoing rebuilds to finish and actually set the AST
/// and Preamble to nulls. It can be run on a different thread. This function
/// is useful to cancel ongoing rebuilds, if any, before removing CppFile.
UniqueFunction<void()> deferCancelRebuild();
/// Rebuild AST and Preamble synchronously on the calling thread.
/// Returns a list of diagnostics or a llvm::None, if another rebuild was
/// requested in parallel (effectively cancelling this rebuild) before
/// diagnostics were produced.
llvm::Optional<std::vector<DiagWithFixIts>>
rebuild(const Context &Ctx, StringRef NewContents,
IntrusiveRefCntPtr<vfs::FileSystem> VFS);
/// Schedule a rebuild and return a deferred computation that will finish the
/// rebuild, that can be called on a different thread.
/// After calling this method, resources, available via futures returned by
/// getPreamble() and getAST(), will be waiting for rebuild to finish. A
/// continuation fininshing rebuild, returned by this function, must be
/// computed(i.e., operator() must be called on it) in order to make those
/// resources ready. If deferRebuild is called again before the rebuild is
/// finished (either because returned future had not been called or because it
/// had not returned yet), the previous rebuild request is cancelled and the
/// resource futures (returned by getPreamble() or getAST()) that were not
/// ready will be waiting for the last rebuild to finish instead.
/// The future to finish rebuild returns a list of diagnostics built during
/// reparse, or None, if another deferRebuild was called before this
/// rebuild was finished.
UniqueFunction<llvm::Optional<std::vector<DiagWithFixIts>>(const Context &)>
deferRebuild(StringRef NewContents, IntrusiveRefCntPtr<vfs::FileSystem> VFS);
/// Returns a future to get the most fresh PreambleData for a file. The
/// future will wait until the Preamble is rebuilt.
std::shared_future<std::shared_ptr<const PreambleData>> getPreamble() const;
/// Return some preamble for a file. It might be stale, but won't wait for
/// rebuild to finish.
std::shared_ptr<const PreambleData> getPossiblyStalePreamble() const;
/// Returns a future to get the most fresh AST for a file. Returned AST is
/// wrapped to prevent concurrent accesses.
/// We use std::shared_ptr here because MVSC fails to compile non-copyable
/// classes as template arguments of promise/future. It is guaranteed to
/// always be non-null.
std::shared_future<std::shared_ptr<ParsedASTWrapper>> getAST() const;
/// Get CompileCommand used to build this CppFile.
tooling::CompileCommand const &getCompileCommand() const;
private:
/// A helper guard that manages the state of CppFile during rebuild.
class RebuildGuard {
public:
RebuildGuard(CppFile &File, unsigned RequestRebuildCounter);
~RebuildGuard();
bool wasCancelledBeforeConstruction() const;
private:
CppFile &File;
unsigned RequestRebuildCounter;
bool WasCancelledBeforeConstruction;
};
Path FileName;
tooling::CompileCommand Command;
bool StorePreamblesInMemory;
/// Mutex protects all fields, declared below it, FileName and Command are not
/// mutated.
mutable std::mutex Mutex;
/// A counter to cancel old rebuilds.
unsigned RebuildCounter;
/// Used to wait when rebuild is finished before starting another one.
bool RebuildInProgress;
/// Condition variable to indicate changes to RebuildInProgress.
std::condition_variable RebuildCond;
/// Promise and future for the latests AST. Fulfilled during rebuild.
/// We use std::shared_ptr here because MVSC fails to compile non-copyable
/// classes as template arguments of promise/future.
std::promise<std::shared_ptr<ParsedASTWrapper>> ASTPromise;
std::shared_future<std::shared_ptr<ParsedASTWrapper>> ASTFuture;
/// Promise and future for the latests Preamble. Fulfilled during rebuild.
std::promise<std::shared_ptr<const PreambleData>> PreamblePromise;
std::shared_future<std::shared_ptr<const PreambleData>> PreambleFuture;
/// Latest preamble that was built. May be stale, but always available without
/// waiting for rebuild to finish.
std::shared_ptr<const PreambleData> LatestAvailablePreamble;
/// Utility class, required by clang.
std::shared_ptr<PCHContainerOperations> PCHs;
/// This is called after the file is parsed. This can be nullptr if there is
/// no callback.
ASTParsedCallback ASTCallback;
};
/// Get the beginning SourceLocation at a specified \p Pos.
SourceLocation getBeginningOfIdentifier(ParsedAST &Unit, const Position &Pos,
const FileEntry *FE);
/// For testing/debugging purposes. Note that this method deserializes all
/// unserialized Decls, so use with care.
void dumpAST(ParsedAST &AST, llvm::raw_ostream &OS);
} // namespace clangd
} // namespace clang
#endif

View File

@@ -0,0 +1,77 @@
//===--- ClangdUnitStore.cpp - A ClangdUnits container -----------*-C++-*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "ClangdUnitStore.h"
#include "llvm/Support/Path.h"
#include <algorithm>
using namespace clang::clangd;
using namespace clang;
std::shared_ptr<CppFile> CppFileCollection::removeIfPresent(PathRef File) {
std::lock_guard<std::mutex> Lock(Mutex);
auto It = OpenedFiles.find(File);
if (It == OpenedFiles.end())
return nullptr;
std::shared_ptr<CppFile> Result = It->second;
OpenedFiles.erase(It);
return Result;
}
CppFileCollection::RecreateResult
CppFileCollection::recreateFileIfCompileCommandChanged(
PathRef File, PathRef ResourceDir, GlobalCompilationDatabase &CDB,
bool StorePreamblesInMemory, std::shared_ptr<PCHContainerOperations> PCHs) {
auto NewCommand = getCompileCommand(CDB, File, ResourceDir);
std::lock_guard<std::mutex> Lock(Mutex);
RecreateResult Result;
auto It = OpenedFiles.find(File);
if (It == OpenedFiles.end()) {
It = OpenedFiles
.try_emplace(File, CppFile::Create(File, std::move(NewCommand),
StorePreamblesInMemory,
std::move(PCHs), ASTCallback))
.first;
} else if (!compileCommandsAreEqual(It->second->getCompileCommand(),
NewCommand)) {
Result.RemovedFile = std::move(It->second);
It->second =
CppFile::Create(File, std::move(NewCommand), StorePreamblesInMemory,
std::move(PCHs), ASTCallback);
}
Result.FileInCollection = It->second;
return Result;
}
tooling::CompileCommand
CppFileCollection::getCompileCommand(GlobalCompilationDatabase &CDB,
PathRef File, PathRef ResourceDir) {
llvm::Optional<tooling::CompileCommand> C = CDB.getCompileCommand(File);
if (!C) // FIXME: Suppress diagnostics? Let the user know?
C = CDB.getFallbackCommand(File);
// Inject the resource dir.
// FIXME: Don't overwrite it if it's already there.
C->CommandLine.push_back("-resource-dir=" + ResourceDir.str());
return std::move(*C);
}
bool CppFileCollection::compileCommandsAreEqual(
tooling::CompileCommand const &LHS, tooling::CompileCommand const &RHS) {
// tooling::CompileCommand.Output is ignored, it's not relevant for clangd.
return LHS.Directory == RHS.Directory &&
LHS.CommandLine.size() == RHS.CommandLine.size() &&
std::equal(LHS.CommandLine.begin(), LHS.CommandLine.end(),
RHS.CommandLine.begin());
}

View File

@@ -0,0 +1,98 @@
//===--- ClangdUnitStore.h - A container of CppFiles -------------*-C++-*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===---------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDUNITSTORE_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDUNITSTORE_H
#include "ClangdUnit.h"
#include "GlobalCompilationDatabase.h"
#include "Logger.h"
#include "Path.h"
#include "clang/Tooling/CompilationDatabase.h"
#include <mutex>
namespace clang {
namespace clangd {
class Logger;
/// Thread-safe mapping from FileNames to CppFile.
class CppFileCollection {
public:
/// \p ASTCallback is called when a file is parsed synchronously. This should
/// not be expensive since it blocks diagnostics.
explicit CppFileCollection(ASTParsedCallback ASTCallback)
: ASTCallback(std::move(ASTCallback)) {}
std::shared_ptr<CppFile>
getOrCreateFile(PathRef File, PathRef ResourceDir,
GlobalCompilationDatabase &CDB, bool StorePreamblesInMemory,
std::shared_ptr<PCHContainerOperations> PCHs) {
std::lock_guard<std::mutex> Lock(Mutex);
auto It = OpenedFiles.find(File);
if (It == OpenedFiles.end()) {
auto Command = getCompileCommand(CDB, File, ResourceDir);
It = OpenedFiles
.try_emplace(File, CppFile::Create(File, std::move(Command),
StorePreamblesInMemory,
std::move(PCHs), ASTCallback))
.first;
}
return It->second;
}
struct RecreateResult {
/// A CppFile, stored in this CppFileCollection for the corresponding
/// filepath after calling recreateFileIfCompileCommandChanged.
std::shared_ptr<CppFile> FileInCollection;
/// If a new CppFile had to be created to account for changed
/// CompileCommand, a previous CppFile instance will be returned in this
/// field.
std::shared_ptr<CppFile> RemovedFile;
};
/// Similar to getOrCreateFile, but will replace a current CppFile for \p File
/// with a new one if CompileCommand, provided by \p CDB has changed.
/// If a currently stored CppFile had to be replaced, the previous instance
/// will be returned in RecreateResult.RemovedFile.
RecreateResult recreateFileIfCompileCommandChanged(
PathRef File, PathRef ResourceDir, GlobalCompilationDatabase &CDB,
bool StorePreamblesInMemory,
std::shared_ptr<PCHContainerOperations> PCHs);
std::shared_ptr<CppFile> getFile(PathRef File) {
std::lock_guard<std::mutex> Lock(Mutex);
auto It = OpenedFiles.find(File);
if (It == OpenedFiles.end())
return nullptr;
return It->second;
}
/// Removes a CppFile, stored for \p File, if it's inside collection and
/// returns it.
std::shared_ptr<CppFile> removeIfPresent(PathRef File);
private:
tooling::CompileCommand getCompileCommand(GlobalCompilationDatabase &CDB,
PathRef File, PathRef ResourceDir);
bool compileCommandsAreEqual(tooling::CompileCommand const &LHS,
tooling::CompileCommand const &RHS);
std::mutex Mutex;
llvm::StringMap<std::shared_ptr<CppFile>> OpenedFiles;
ASTParsedCallback ASTCallback;
};
} // namespace clangd
} // namespace clang
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,92 @@
//===--- CodeComplete.h -----------------------------------------*- C++-*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===---------------------------------------------------------------------===//
//
// Code completion provides suggestions for what the user might type next.
// After "std::string S; S." we might suggest members of std::string.
// Signature help describes the parameters of a function as you type them.
//
//===---------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CODECOMPLETE_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CODECOMPLETE_H
#include "Context.h"
#include "Logger.h"
#include "Path.h"
#include "Protocol.h"
#include "index/Index.h"
#include "clang/Frontend/PrecompiledPreamble.h"
#include "clang/Sema/CodeCompleteOptions.h"
#include "clang/Tooling/CompilationDatabase.h"
namespace clang {
class PCHContainerOperations;
namespace clangd {
struct CodeCompleteOptions {
/// Returns options that can be passed to clang's completion engine.
clang::CodeCompleteOptions getClangCompleteOpts() const;
/// When true, completion items will contain expandable code snippets in
/// completion (e.g. `return ${1:expression}` or `foo(${1:int a}, ${2:int
/// b})).
bool EnableSnippets = false;
/// Add code patterns to completion results.
/// If EnableSnippets is false, this options is ignored and code patterns will
/// always be omitted.
bool IncludeCodePatterns = true;
/// Add macros to code completion results.
bool IncludeMacros = true;
/// Add globals to code completion results.
bool IncludeGlobals = true;
/// Add brief comments to completion items, if available.
/// FIXME(ibiryukov): it looks like turning this option on significantly slows
/// down completion, investigate if it can be made faster.
bool IncludeBriefComments = true;
/// Include results that are not legal completions in the current context.
/// For example, private members are usually inaccessible.
bool IncludeIneligibleResults = false;
/// Limit the number of results returned (0 means no limit).
/// If more results are available, we set CompletionList.isIncomplete.
size_t Limit = 0;
// Populated internally by clangd, do not set.
/// If `Index` is set, it is used to augment the code completion
/// results.
/// FIXME(ioeric): we might want a better way to pass the index around inside
/// clangd.
const SymbolIndex *Index = nullptr;
};
/// Get code completions at a specified \p Pos in \p FileName.
CompletionList codeComplete(const Context &Ctx, PathRef FileName,
const tooling::CompileCommand &Command,
PrecompiledPreamble const *Preamble,
StringRef Contents, Position Pos,
IntrusiveRefCntPtr<vfs::FileSystem> VFS,
std::shared_ptr<PCHContainerOperations> PCHs,
CodeCompleteOptions Opts);
/// Get signature help at a specified \p Pos in \p FileName.
SignatureHelp signatureHelp(const Context &Ctx, PathRef FileName,
const tooling::CompileCommand &Command,
PrecompiledPreamble const *Preamble,
StringRef Contents, Position Pos,
IntrusiveRefCntPtr<vfs::FileSystem> VFS,
std::shared_ptr<PCHContainerOperations> PCHs);
} // namespace clangd
} // namespace clang
#endif

View File

@@ -0,0 +1,188 @@
//===--- CodeCompletionStrings.cpp -------------------------------*- C++-*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===---------------------------------------------------------------------===//
#include "CodeCompletionStrings.h"
#include <utility>
namespace clang {
namespace clangd {
namespace {
bool isInformativeQualifierChunk(CodeCompletionString::Chunk const &Chunk) {
return Chunk.Kind == CodeCompletionString::CK_Informative &&
StringRef(Chunk.Text).endswith("::");
}
void processPlainTextChunks(const CodeCompletionString &CCS,
std::string *LabelOut, std::string *InsertTextOut) {
std::string &Label = *LabelOut;
std::string &InsertText = *InsertTextOut;
for (const auto &Chunk : CCS) {
// Informative qualifier chunks only clutter completion results, skip
// them.
if (isInformativeQualifierChunk(Chunk))
continue;
switch (Chunk.Kind) {
case CodeCompletionString::CK_ResultType:
case CodeCompletionString::CK_Optional:
break;
case CodeCompletionString::CK_TypedText:
InsertText += Chunk.Text;
Label += Chunk.Text;
break;
default:
Label += Chunk.Text;
break;
}
}
}
void appendEscapeSnippet(const llvm::StringRef Text, std::string *Out) {
for (const auto Character : Text) {
if (Character == '$' || Character == '}' || Character == '\\')
Out->push_back('\\');
Out->push_back(Character);
}
}
void processSnippetChunks(const CodeCompletionString &CCS,
std::string *LabelOut, std::string *InsertTextOut) {
std::string &Label = *LabelOut;
std::string &InsertText = *InsertTextOut;
unsigned ArgCount = 0;
for (const auto &Chunk : CCS) {
// Informative qualifier chunks only clutter completion results, skip
// them.
if (isInformativeQualifierChunk(Chunk))
continue;
switch (Chunk.Kind) {
case CodeCompletionString::CK_TypedText:
case CodeCompletionString::CK_Text:
Label += Chunk.Text;
InsertText += Chunk.Text;
break;
case CodeCompletionString::CK_Optional:
// FIXME: Maybe add an option to allow presenting the optional chunks?
break;
case CodeCompletionString::CK_Placeholder:
++ArgCount;
InsertText += "${" + std::to_string(ArgCount) + ':';
appendEscapeSnippet(Chunk.Text, &InsertText);
InsertText += '}';
Label += Chunk.Text;
break;
case CodeCompletionString::CK_Informative:
// For example, the word "const" for a const method, or the name of
// the base class for methods that are part of the base class.
Label += Chunk.Text;
// Don't put the informative chunks in the insertText.
break;
case CodeCompletionString::CK_ResultType:
// This is retrieved as detail.
break;
case CodeCompletionString::CK_CurrentParameter:
// This should never be present while collecting completion items,
// only while collecting overload candidates.
llvm_unreachable("Unexpected CK_CurrentParameter while collecting "
"CompletionItems");
break;
case CodeCompletionString::CK_LeftParen:
case CodeCompletionString::CK_RightParen:
case CodeCompletionString::CK_LeftBracket:
case CodeCompletionString::CK_RightBracket:
case CodeCompletionString::CK_LeftBrace:
case CodeCompletionString::CK_RightBrace:
case CodeCompletionString::CK_LeftAngle:
case CodeCompletionString::CK_RightAngle:
case CodeCompletionString::CK_Comma:
case CodeCompletionString::CK_Colon:
case CodeCompletionString::CK_SemiColon:
case CodeCompletionString::CK_Equal:
case CodeCompletionString::CK_HorizontalSpace:
InsertText += Chunk.Text;
Label += Chunk.Text;
break;
case CodeCompletionString::CK_VerticalSpace:
InsertText += Chunk.Text;
// Don't even add a space to the label.
break;
}
}
}
} // namespace
void getLabelAndInsertText(const CodeCompletionString &CCS, std::string *Label,
std::string *InsertText, bool EnableSnippets) {
return EnableSnippets ? processSnippetChunks(CCS, Label, InsertText)
: processPlainTextChunks(CCS, Label, InsertText);
}
std::string getDocumentation(const CodeCompletionString &CCS) {
// Things like __attribute__((nonnull(1,3))) and [[noreturn]]. Present this
// information in the documentation field.
std::string Result;
const unsigned AnnotationCount = CCS.getAnnotationCount();
if (AnnotationCount > 0) {
Result += "Annotation";
if (AnnotationCount == 1) {
Result += ": ";
} else /* AnnotationCount > 1 */ {
Result += "s: ";
}
for (unsigned I = 0; I < AnnotationCount; ++I) {
Result += CCS.getAnnotation(I);
Result.push_back(I == AnnotationCount - 1 ? '\n' : ' ');
}
}
// Add brief documentation (if there is any).
if (CCS.getBriefComment() != nullptr) {
if (!Result.empty()) {
// This means we previously added annotations. Add an extra newline
// character to make the annotations stand out.
Result.push_back('\n');
}
Result += CCS.getBriefComment();
}
return Result;
}
std::string getDetail(const CodeCompletionString &CCS) {
for (const auto &Chunk : CCS) {
// Informative qualifier chunks only clutter completion results, skip
// them.
switch (Chunk.Kind) {
case CodeCompletionString::CK_ResultType:
return Chunk.Text;
default:
break;
}
}
return "";
}
std::string getFilterText(const CodeCompletionString &CCS) {
for (const auto &Chunk : CCS) {
switch (Chunk.Kind) {
case CodeCompletionString::CK_TypedText:
// There's always exactly one CK_TypedText chunk.
return Chunk.Text;
default:
break;
}
}
return "";
}
} // namespace clangd
} // namespace clang

View File

@@ -0,0 +1,46 @@
//===--- CodeCompletionStrings.h ---------------------------------*- C++-*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===---------------------------------------------------------------------===//
//
// Functions for retrieving code completion information from
// `CodeCompletionString`.
//
//===---------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CODECOMPLETIONSTRINGS_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CODECOMPLETIONSTRINGS_H
#include "clang/Sema/CodeCompleteConsumer.h"
namespace clang {
namespace clangd {
/// Gets label and insert text for a completion item. For example, for function
/// `Foo`, this returns <"Foo(int x, int y)", "Foo"> without snippts enabled.
///
/// If \p EnableSnippets is true, this will try to use snippet for the insert
/// text. Otherwise, the insert text will always be plain text.
void getLabelAndInsertText(const CodeCompletionString &CCS, std::string *Label,
std::string *InsertText, bool EnableSnippets);
/// Gets the documentation for a completion item. For example, comment for the
/// a class declaration.
std::string getDocumentation(const CodeCompletionString &CCS);
/// Gets detail to be used as the detail field in an LSP completion item. This
/// is usually the return type of a function.
std::string getDetail(const CodeCompletionString &CCS);
/// Gets the piece of text that the user is expected to type to match the
/// code-completion string, typically a keyword or the name of a declarator or
/// macro.
std::string getFilterText(const CodeCompletionString &CCS);
} // namespace clangd
} // namespace clang
#endif

View File

@@ -0,0 +1,66 @@
//===--- Compiler.cpp -------------------------------------------*- C++-*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===---------------------------------------------------------------------===//
#include "Compiler.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Lex/PreprocessorOptions.h"
namespace clang {
namespace clangd {
/// Creates a CompilerInstance from \p CI, with main buffer overriden to \p
/// Buffer and arguments to read the PCH from \p Preamble, if \p Preamble is not
/// null. Note that vfs::FileSystem inside returned instance may differ from \p
/// VFS if additional file remapping were set in command-line arguments.
/// On some errors, returns null. When non-null value is returned, it's expected
/// to be consumed by the FrontendAction as it will have a pointer to the \p
/// Buffer that will only be deleted if BeginSourceFile is called.
std::unique_ptr<CompilerInstance>
prepareCompilerInstance(std::unique_ptr<clang::CompilerInvocation> CI,
const PrecompiledPreamble *Preamble,
std::unique_ptr<llvm::MemoryBuffer> Buffer,
std::shared_ptr<PCHContainerOperations> PCHs,
IntrusiveRefCntPtr<vfs::FileSystem> VFS,
DiagnosticConsumer &DiagsClient) {
assert(VFS && "VFS is null");
assert(!CI->getPreprocessorOpts().RetainRemappedFileBuffers &&
"Setting RetainRemappedFileBuffers to true will cause a memory leak "
"of ContentsBuffer");
// NOTE: we use Buffer.get() when adding remapped files, so we have to make
// sure it will be released if no error is emitted.
if (Preamble) {
Preamble->AddImplicitPreamble(*CI, VFS, Buffer.get());
} else {
CI->getPreprocessorOpts().addRemappedFile(
CI->getFrontendOpts().Inputs[0].getFile(), Buffer.get());
}
auto Clang = llvm::make_unique<CompilerInstance>(PCHs);
Clang->setInvocation(std::move(CI));
Clang->createDiagnostics(&DiagsClient, false);
if (auto VFSWithRemapping = createVFSFromCompilerInvocation(
Clang->getInvocation(), Clang->getDiagnostics(), VFS))
VFS = VFSWithRemapping;
Clang->setVirtualFileSystem(VFS);
Clang->setTarget(TargetInfo::CreateTargetInfo(
Clang->getDiagnostics(), Clang->getInvocation().TargetOpts));
if (!Clang->hasTarget())
return nullptr;
// RemappedFileBuffers will handle the lifetime of the Buffer pointer,
// release it.
Buffer.release();
return Clang;
}
} // namespace clangd
} // namespace clang

View File

@@ -0,0 +1,47 @@
//===--- Compiler.h ---------------------------------------------*- C++-*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===---------------------------------------------------------------------===//
//
// Shared utilities for invoking the clang compiler.
// ClangdUnit takes care of much of this, but some features like CodeComplete
// run their own compile actions that share logic.
//
//===---------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_COMPILER_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_COMPILER_H
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/PrecompiledPreamble.h"
namespace clang {
namespace clangd {
class IgnoreDiagnostics : public DiagnosticConsumer {
public:
void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
const clang::Diagnostic &Info) override {}
};
/// Creates a CompilerInstance with the main file contens overridden.
/// The preamble will be reused unless it is null.
/// Note that the vfs::FileSystem inside returned instance may differ if
/// additional file remappings occur in command-line arguments.
/// On some errors, returns null. When non-null value is returned, it's expected
/// to be consumed by the FrontendAction as it will have a pointer to the
/// MainFile buffer that will only be deleted if BeginSourceFile is called.
std::unique_ptr<CompilerInstance> prepareCompilerInstance(
std::unique_ptr<clang::CompilerInvocation>, const PrecompiledPreamble *,
std::unique_ptr<llvm::MemoryBuffer> MainFile,
std::shared_ptr<PCHContainerOperations>,
IntrusiveRefCntPtr<vfs::FileSystem>, DiagnosticConsumer &);
} // namespace clangd
} // namespace clang
#endif

View File

@@ -0,0 +1,24 @@
//===--- Context.cpp -----------------------------------------*- C++-*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===---------------------------------------------------------------------===//
#include "Context.h"
#include <cassert>
namespace clang {
namespace clangd {
Context Context::empty() { return Context(/*Data=*/nullptr); }
Context::Context(std::shared_ptr<const Data> DataPtr)
: DataPtr(std::move(DataPtr)) {}
Context Context::clone() const { return Context(DataPtr); }
} // namespace clangd
} // namespace clang

View File

@@ -0,0 +1,191 @@
//===--- Context.h - Mechanism for passing implicit data --------*- C++-*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Context for storing and retrieving implicit data. Useful for passing implicit
// parameters on a per-request basis.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CONTEXT_H_
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CONTEXT_H_
#include "llvm/ADT/STLExtras.h"
#include <memory>
#include <type_traits>
namespace clang {
namespace clangd {
/// A key for a value of type \p Type, stored inside a context. Keys are
/// non-movable and non-copyable. See documentation of the Context class for
/// more details and usage examples.
template <class Type> class Key {
public:
static_assert(!std::is_reference<Type>::value,
"Reference arguments to Key<> are not allowed");
Key() = default;
Key(Key const &) = delete;
Key &operator=(Key const &) = delete;
Key(Key &&) = delete;
Key &operator=(Key &&) = delete;
};
/// A context is an immutable container for per-request data that must be
/// propagated through layers that don't care about it. An example is a request
/// ID that we may want to use when logging.
///
/// Conceptually, a context is a heterogeneous map<Key<T>, T>. Each key has
/// an associated value type, which allows the map to be typesafe.
///
/// You can't add data to an existing context, instead you create a new
/// immutable context derived from it with extra data added. When you retrieve
/// data, the context will walk up the parent chain until the key is found.
///
/// Contexts should be:
/// - passed by reference when calling synchronous functions
/// - passed by value (move) when calling asynchronous functions. The result
/// callback of async operations will receive the context again.
/// - cloned only when 'forking' an asynchronous computation that we don't wait
/// for.
///
/// Copy operations for this class are deleted, use an explicit clone() method
/// when you need a copy of the context instead.
///
/// To derive a child context use derive() function, e.g.
/// Context ChildCtx = ParentCtx.derive(RequestIdKey, 123);
///
/// To create a new root context, derive() from empty Context.
/// e.g.:
/// Context Ctx = Context::empty().derive(RequestIdKey, 123);
///
/// Values in the context are indexed by typed keys (instances of Key<T> class).
/// Key<T> serves two purposes:
/// - it provides a lookup key for the context (each instance of a key is
/// unique),
/// - it keeps the type information about the value stored in the context map
/// in the template arguments.
/// This provides a type-safe interface to store and access values of multiple
/// types inside a single context.
/// For example,
/// Key<int> RequestID;
/// Key<int> Version;
///
/// Context Ctx = Context::empty().derive(RequestID, 10).derive(Version, 3);
/// assert(*Ctx.get(RequestID) == 10);
/// assert(*Ctx.get(Version) == 3);
///
/// Keys are typically used across multiple functions, so most of the time you
/// would want to make them static class members or global variables.
class Context {
public:
/// Returns an empty context that contains no data. Useful for calling
/// functions that require a context when no explicit context is available.
static Context empty();
private:
struct Data;
Context(std::shared_ptr<const Data> DataPtr);
public:
/// Same as Context::empty(), please use Context::empty() instead.
/// Constructor is defined to workaround a bug in MSVC's version of STL.
/// (arguments of std::future<> must be default-construcitble in MSVC).
Context() = default;
/// Move-only.
Context(Context const &) = delete;
Context &operator=(const Context &) = delete;
Context(Context &&) = default;
Context &operator=(Context &&) = default;
/// Get data stored for a typed \p Key. If values are not found
/// \returns Pointer to the data associated with \p Key. If no data is
/// specified for \p Key, return null.
template <class Type> const Type *get(const Key<Type> &Key) const {
for (const Data *DataPtr = this->DataPtr.get(); DataPtr != nullptr;
DataPtr = DataPtr->Parent.get()) {
if (DataPtr->KeyPtr == &Key)
return static_cast<const Type *>(DataPtr->Value->getValuePtr());
}
return nullptr;
}
/// A helper to get a reference to a \p Key that must exist in the map.
/// Must not be called for keys that are not in the map.
template <class Type> const Type &getExisting(const Key<Type> &Key) const {
auto Val = get(Key);
assert(Val && "Key does not exist");
return *Val;
}
/// Derives a child context
/// It is safe to move or destroy a parent context after calling derive() from
/// it. The child context will continue to have access to the data stored in
/// the parent context.
template <class Type>
Context derive(const Key<Type> &Key,
typename std::decay<Type>::type Value) const & {
return Context(std::make_shared<Data>(Data{
/*Parent=*/DataPtr, &Key,
llvm::make_unique<TypedAnyStorage<typename std::decay<Type>::type>>(
std::move(Value))}));
}
template <class Type>
Context
derive(const Key<Type> &Key,
typename std::decay<Type>::type Value) && /* takes ownership */ {
return Context(std::make_shared<Data>(Data{
/*Parent=*/std::move(DataPtr), &Key,
llvm::make_unique<TypedAnyStorage<typename std::decay<Type>::type>>(
std::move(Value))}));
}
/// Clone this context object.
Context clone() const;
private:
class AnyStorage {
public:
virtual ~AnyStorage() = default;
virtual void *getValuePtr() = 0;
};
template <class T> class TypedAnyStorage : public Context::AnyStorage {
static_assert(std::is_same<typename std::decay<T>::type, T>::value,
"Argument to TypedAnyStorage must be decayed");
public:
TypedAnyStorage(T &&Value) : Value(std::move(Value)) {}
void *getValuePtr() override { return &Value; }
private:
T Value;
};
struct Data {
// We need to make sure Parent outlives the Value, so the order of members
// is important. We do that to allow classes stored in Context's child
// layers to store references to the data in the parent layers.
std::shared_ptr<const Data> Parent;
const void *KeyPtr;
std::unique_ptr<AnyStorage> Value;
};
std::shared_ptr<const Data> DataPtr;
}; // namespace clangd
} // namespace clangd
} // namespace clang
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_CONTEXT_H_

View File

@@ -0,0 +1,49 @@
//===--- DraftStore.cpp - File contents container ---------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "DraftStore.h"
using namespace clang;
using namespace clang::clangd;
VersionedDraft DraftStore::getDraft(PathRef File) const {
std::lock_guard<std::mutex> Lock(Mutex);
auto It = Drafts.find(File);
if (It == Drafts.end())
return {0, llvm::None};
return It->second;
}
DocVersion DraftStore::getVersion(PathRef File) const {
std::lock_guard<std::mutex> Lock(Mutex);
auto It = Drafts.find(File);
if (It == Drafts.end())
return 0;
return It->second.Version;
}
DocVersion DraftStore::updateDraft(PathRef File, StringRef Contents) {
std::lock_guard<std::mutex> Lock(Mutex);
auto &Entry = Drafts[File];
DocVersion NewVersion = ++Entry.Version;
Entry.Draft = Contents;
return NewVersion;
}
DocVersion DraftStore::removeDraft(PathRef File) {
std::lock_guard<std::mutex> Lock(Mutex);
auto &Entry = Drafts[File];
DocVersion NewVersion = ++Entry.Version;
Entry.Draft = llvm::None;
return NewVersion;
}

View File

@@ -0,0 +1,62 @@
//===--- DraftStore.h - File contents container -----------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_DRAFTSTORE_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_DRAFTSTORE_H
#include "Path.h"
#include "clang/Basic/LLVM.h"
#include "llvm/ADT/StringMap.h"
#include <cstdint>
#include <mutex>
#include <string>
#include <vector>
namespace clang {
namespace clangd {
/// Using unsigned int type here to avoid undefined behaviour on overflow.
typedef uint64_t DocVersion;
/// Document draft with a version of this draft.
struct VersionedDraft {
DocVersion Version;
/// If the value of the field is None, draft is now deleted
llvm::Optional<std::string> Draft;
};
/// A thread-safe container for files opened in a workspace, addressed by
/// filenames. The contents are owned by the DraftStore. Versions are mantained
/// for the all added documents, including removed ones. The document version is
/// incremented on each update and removal of the document.
class DraftStore {
public:
/// \return version and contents of the stored document.
/// For untracked files, a (0, None) pair is returned.
VersionedDraft getDraft(PathRef File) const;
/// \return version of the tracked document.
/// For untracked files, 0 is returned.
DocVersion getVersion(PathRef File) const;
/// Replace contents of the draft for \p File with \p Contents.
/// \return The new version of the draft for \p File.
DocVersion updateDraft(PathRef File, StringRef Contents);
/// Remove the contents of the draft
/// \return The new version of the draft for \p File.
DocVersion removeDraft(PathRef File);
private:
mutable std::mutex Mutex;
llvm::StringMap<VersionedDraft> Drafts;
};
} // namespace clangd
} // namespace clang
#endif

View File

@@ -0,0 +1,177 @@
//===--- Function.h - Utility callable wrappers -----------------*- C++-*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file provides an analogue to std::function that supports move semantics.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_FUNCTION_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_FUNCTION_H
#include "llvm/ADT/STLExtras.h"
#include <cassert>
#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>
namespace clang {
namespace clangd {
/// A move-only type-erasing function wrapper. Similar to `std::function`, but
/// allows to store move-only callables.
template <class> class UniqueFunction;
template <class Ret, class... Args> class UniqueFunction<Ret(Args...)> {
public:
UniqueFunction() = default;
UniqueFunction(std::nullptr_t) : UniqueFunction(){};
UniqueFunction(UniqueFunction const &) = delete;
UniqueFunction &operator=(UniqueFunction const &) = delete;
UniqueFunction(UniqueFunction &&) noexcept = default;
UniqueFunction &operator=(UniqueFunction &&) noexcept = default;
template <class Callable,
/// A sfinae-check that Callable can be called with Args... and
class = typename std::enable_if<std::is_convertible<
decltype(std::declval<Callable>()(std::declval<Args>()...)),
Ret>::value>::type>
UniqueFunction(Callable &&Func)
: CallablePtr(llvm::make_unique<
FunctionCallImpl<typename std::decay<Callable>::type>>(
std::forward<Callable>(Func))) {}
explicit operator bool() { return bool(CallablePtr); }
Ret operator()(Args... As) {
assert(CallablePtr);
return CallablePtr->Call(std::forward<Args>(As)...);
}
private:
class FunctionCallBase {
public:
virtual ~FunctionCallBase() = default;
virtual Ret Call(Args... As) = 0;
};
template <class Callable>
class FunctionCallImpl final : public FunctionCallBase {
static_assert(
std::is_same<Callable, typename std::decay<Callable>::type>::value,
"FunctionCallImpl must be instanstiated with std::decay'ed types");
public:
FunctionCallImpl(Callable Func) : Func(std::move(Func)) {}
Ret Call(Args... As) override { return Func(std::forward<Args>(As)...); }
private:
Callable Func;
};
std::unique_ptr<FunctionCallBase> CallablePtr;
};
/// Stores a callable object (Func) and arguments (Args) and allows to call the
/// callable with provided arguments later using `operator ()`. The arguments
/// are std::forward'ed into the callable in the body of `operator()`. Therefore
/// `operator()` can only be called once, as some of the arguments could be
/// std::move'ed into the callable on first call.
template <class Func, class... Args> struct ForwardBinder {
using Tuple = std::tuple<typename std::decay<Func>::type,
typename std::decay<Args>::type...>;
Tuple FuncWithArguments;
#ifndef NDEBUG
bool WasCalled = false;
#endif
public:
ForwardBinder(Tuple FuncWithArguments)
: FuncWithArguments(std::move(FuncWithArguments)) {}
private:
template <std::size_t... Indexes, class... RestArgs>
auto CallImpl(llvm::integer_sequence<std::size_t, Indexes...> Seq,
RestArgs &&... Rest)
-> decltype(std::get<0>(this->FuncWithArguments)(
std::forward<Args>(std::get<Indexes + 1>(this->FuncWithArguments))...,
std::forward<RestArgs>(Rest)...)) {
return std::get<0>(this->FuncWithArguments)(
std::forward<Args>(std::get<Indexes + 1>(this->FuncWithArguments))...,
std::forward<RestArgs>(Rest)...);
}
public:
template <class... RestArgs>
auto operator()(RestArgs &&... Rest)
-> decltype(this->CallImpl(llvm::index_sequence_for<Args...>(),
std::forward<RestArgs>(Rest)...)) {
#ifndef NDEBUG
assert(!WasCalled && "Can only call result of BindWithForward once.");
WasCalled = true;
#endif
return CallImpl(llvm::index_sequence_for<Args...>(),
std::forward<RestArgs>(Rest)...);
}
};
/// Creates an object that stores a callable (\p F) and first arguments to the
/// callable (\p As) and allows to call \p F with \Args at a later point.
/// Similar to std::bind, but also works with move-only \p F and \p As.
///
/// The returned object must be called no more than once, as \p As are
/// std::forwarded'ed (therefore can be moved) into \p F during the call.
template <class Func, class... Args>
ForwardBinder<Func, Args...> BindWithForward(Func F, Args &&... As) {
return ForwardBinder<Func, Args...>(
std::make_tuple(std::forward<Func>(F), std::forward<Args>(As)...));
}
namespace detail {
/// Runs provided callback in destructor. Use onScopeExit helper function to
/// create this object.
template <class Func> struct ScopeExitGuard {
static_assert(std::is_same<typename std::decay<Func>::type, Func>::value,
"Func must be decayed");
ScopeExitGuard(Func F) : F(std::move(F)) {}
~ScopeExitGuard() {
if (!F)
return;
(*F)();
}
// Move-only.
ScopeExitGuard(const ScopeExitGuard &) = delete;
ScopeExitGuard &operator=(const ScopeExitGuard &) = delete;
ScopeExitGuard(ScopeExitGuard &&Other) = default;
ScopeExitGuard &operator=(ScopeExitGuard &&Other) = default;
private:
llvm::Optional<Func> F;
};
} // namespace detail
/// Creates a RAII object that will run \p F in its destructor.
template <class Func>
auto onScopeExit(Func &&F)
-> detail::ScopeExitGuard<typename std::decay<Func>::type> {
return detail::ScopeExitGuard<typename std::decay<Func>::type>(
std::forward<Func>(F));
}
} // namespace clangd
} // namespace clang
#endif

Some files were not shown because too many files have changed in this diff Show More