summaryrefslogtreecommitdiffstats
path: root/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang-tools-extra/clangd/GlobalCompilationDatabase.cpp')
-rw-r--r--clang-tools-extra/clangd/GlobalCompilationDatabase.cpp187
1 files changed, 145 insertions, 42 deletions
diff --git a/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp b/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp
index 9a09482ce39..51f0e7d015e 100644
--- a/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp
+++ b/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp
@@ -8,12 +8,18 @@
#include "GlobalCompilationDatabase.h"
#include "Logger.h"
+#include "Path.h"
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Tooling/ArgumentsAdjusters.h"
#include "clang/Tooling/CompilationDatabase.h"
+#include "llvm/ADT/None.h"
#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
+#include <string>
+#include <tuple>
+#include <vector>
namespace clang {
namespace clangd {
@@ -43,6 +49,16 @@ std::string getStandardResourceDir() {
return CompilerInvocation::GetResourcesPath("clangd", (void *)&Dummy);
}
+// Runs the given action on all parent directories of filename, starting from
+// deepest directory and going up to root. Stops whenever action succeeds.
+void actOnAllParentDirectories(PathRef FileName,
+ llvm::function_ref<bool(PathRef)> Action) {
+ for (auto Path = llvm::sys::path::parent_path(FileName);
+ !Path.empty() && !Action(Path);
+ Path = llvm::sys::path::parent_path(Path))
+ ;
+}
+
} // namespace
static std::string getFallbackClangPath() {
@@ -81,60 +97,138 @@ DirectoryBasedGlobalCompilationDatabase::
~DirectoryBasedGlobalCompilationDatabase() = default;
llvm::Optional<tooling::CompileCommand>
-DirectoryBasedGlobalCompilationDatabase::getCompileCommand(
- PathRef File, ProjectInfo *Project) const {
- if (auto CDB = getCDBForFile(File, Project)) {
- auto Candidates = CDB->getCompileCommands(File);
- if (!Candidates.empty()) {
- return std::move(Candidates.front());
- }
- } else {
+DirectoryBasedGlobalCompilationDatabase::getCompileCommand(PathRef File) const {
+ CDBLookupRequest Req;
+ Req.FileName = File;
+ Req.ShouldBroadcast = true;
+
+ auto Res = lookupCDB(Req);
+ if (!Res) {
log("Failed to find compilation database for {0}", File);
+ return llvm::None;
}
+
+ auto Candidates = Res->CDB->getCompileCommands(File);
+ if (!Candidates.empty())
+ return std::move(Candidates.front());
+
return None;
}
-std::pair<tooling::CompilationDatabase *, /*Cached*/ bool>
+std::pair<tooling::CompilationDatabase *, /*SentBroadcast*/ bool>
DirectoryBasedGlobalCompilationDatabase::getCDBInDirLocked(PathRef Dir) const {
// FIXME(ibiryukov): Invalidate cached compilation databases on changes
auto CachedIt = CompilationDatabases.find(Dir);
if (CachedIt != CompilationDatabases.end())
- return {CachedIt->second.get(), true};
+ return {CachedIt->second.CDB.get(), CachedIt->second.SentBroadcast};
std::string Error = "";
- auto CDB = tooling::CompilationDatabase::loadFromDirectory(Dir, Error);
- auto Result = CDB.get();
- CompilationDatabases.insert(std::make_pair(Dir, std::move(CDB)));
+
+ CachedCDB Entry;
+ Entry.CDB = tooling::CompilationDatabase::loadFromDirectory(Dir, Error);
+ auto Result = Entry.CDB.get();
+ CompilationDatabases[Dir] = std::move(Entry);
+
return {Result, false};
}
-tooling::CompilationDatabase *
-DirectoryBasedGlobalCompilationDatabase::getCDBForFile(
- PathRef File, ProjectInfo *Project) const {
- namespace path = llvm::sys::path;
- assert((path::is_absolute(File, path::Style::posix) ||
- path::is_absolute(File, path::Style::windows)) &&
+llvm::Optional<DirectoryBasedGlobalCompilationDatabase::CDBLookupResult>
+DirectoryBasedGlobalCompilationDatabase::lookupCDB(
+ CDBLookupRequest Request) const {
+ assert(llvm::sys::path::is_absolute(Request.FileName) &&
"path must be absolute");
- tooling::CompilationDatabase *CDB = nullptr;
- bool Cached = false;
- std::lock_guard<std::mutex> Lock(Mutex);
+ CDBLookupResult Result;
+ bool SentBroadcast = false;
+
+ {
+ std::lock_guard<std::mutex> Lock(Mutex);
+ if (CompileCommandsDir) {
+ std::tie(Result.CDB, SentBroadcast) =
+ getCDBInDirLocked(*CompileCommandsDir);
+ Result.PI.SourceRoot = *CompileCommandsDir;
+ } else {
+ actOnAllParentDirectories(
+ Request.FileName, [this, &SentBroadcast, &Result](PathRef Path) {
+ std::tie(Result.CDB, SentBroadcast) = getCDBInDirLocked(Path);
+ Result.PI.SourceRoot = Path;
+ return Result.CDB != nullptr;
+ });
+ }
+
+ if (!Result.CDB)
+ return llvm::None;
+
+ // Mark CDB as broadcasted to make sure discovery is performed once.
+ if (Request.ShouldBroadcast && !SentBroadcast)
+ CompilationDatabases[Result.PI.SourceRoot].SentBroadcast = true;
+ }
+
+ // FIXME: Maybe make the following part async, since this can block retrieval
+ // of compile commands.
+ if (Request.ShouldBroadcast && !SentBroadcast)
+ broadcastCDB(Result);
+ return Result;
+}
+
+void DirectoryBasedGlobalCompilationDatabase::broadcastCDB(
+ CDBLookupResult Result) const {
+ assert(Result.CDB && "Trying to broadcast an invalid CDB!");
+
+ std::vector<std::string> AllFiles = Result.CDB->getAllFiles();
+ // We assume CDB in CompileCommandsDir owns all of its entries, since we don't
+ // perform any search in parent paths whenever it is set.
if (CompileCommandsDir) {
- std::tie(CDB, Cached) = getCDBInDirLocked(*CompileCommandsDir);
- if (Project && CDB)
- Project->SourceRoot = *CompileCommandsDir;
- } else {
- for (auto Path = path::parent_path(File); !CDB && !Path.empty();
- Path = path::parent_path(Path)) {
- std::tie(CDB, Cached) = getCDBInDirLocked(Path);
- if (Project && CDB)
- Project->SourceRoot = Path;
+ assert(*CompileCommandsDir == Result.PI.SourceRoot &&
+ "Trying to broadcast a CDB outside of CompileCommandsDir!");
+ OnCommandChanged.broadcast(std::move(AllFiles));
+ return;
+ }
+
+ llvm::StringMap<bool> DirectoryHasCDB;
+ {
+ std::lock_guard<std::mutex> Lock(Mutex);
+ // Uniquify all parent directories of all files.
+ for (llvm::StringRef File : AllFiles) {
+ actOnAllParentDirectories(File, [&](PathRef Path) {
+ auto It = DirectoryHasCDB.try_emplace(Path);
+ // Already seen this path, and all of its parents.
+ if (!It.second)
+ return true;
+
+ auto Res = getCDBInDirLocked(Path);
+ It.first->second = Res.first != nullptr;
+ return Path == Result.PI.SourceRoot;
+ });
}
}
- // FIXME: getAllFiles() may return relative paths, we need absolute paths.
- // Hopefully the fix is to change JSONCompilationDatabase and the interface.
- if (CDB && !Cached)
- OnCommandChanged.broadcast(CDB->getAllFiles());
- return CDB;
+
+ std::vector<std::string> GovernedFiles;
+ for (llvm::StringRef File : AllFiles) {
+ // A file is governed by this CDB if lookup for the file would find it.
+ // Independent of whether it has an entry for that file or not.
+ actOnAllParentDirectories(File, [&](PathRef Path) {
+ if (DirectoryHasCDB.lookup(Path)) {
+ if (Path == Result.PI.SourceRoot)
+ GovernedFiles.push_back(File);
+ // Stop as soon as we hit a CDB.
+ return true;
+ }
+ return false;
+ });
+ }
+
+ OnCommandChanged.broadcast(std::move(GovernedFiles));
+}
+
+llvm::Optional<ProjectInfo>
+DirectoryBasedGlobalCompilationDatabase::getProjectInfo(PathRef File) const {
+ CDBLookupRequest Req;
+ Req.FileName = File;
+ Req.ShouldBroadcast = false;
+ auto Res = lookupCDB(Req);
+ if (!Res)
+ return llvm::None;
+ return Res->PI;
}
OverlayCDB::OverlayCDB(const GlobalCompilationDatabase *Base,
@@ -150,19 +244,16 @@ OverlayCDB::OverlayCDB(const GlobalCompilationDatabase *Base,
}
llvm::Optional<tooling::CompileCommand>
-OverlayCDB::getCompileCommand(PathRef File, ProjectInfo *Project) const {
+OverlayCDB::getCompileCommand(PathRef File) const {
llvm::Optional<tooling::CompileCommand> Cmd;
{
std::lock_guard<std::mutex> Lock(Mutex);
auto It = Commands.find(File);
- if (It != Commands.end()) {
- if (Project)
- Project->SourceRoot = "";
+ if (It != Commands.end())
Cmd = It->second;
- }
}
if (!Cmd && Base)
- Cmd = Base->getCompileCommand(File, Project);
+ Cmd = Base->getCompileCommand(File);
if (!Cmd)
return llvm::None;
adjustArguments(*Cmd, ResourceDir);
@@ -191,5 +282,17 @@ void OverlayCDB::setCompileCommand(
OnCommandChanged.broadcast({File});
}
+llvm::Optional<ProjectInfo> OverlayCDB::getProjectInfo(PathRef File) const {
+ {
+ std::lock_guard<std::mutex> Lock(Mutex);
+ auto It = Commands.find(File);
+ if (It != Commands.end())
+ return ProjectInfo{};
+ }
+ if (Base)
+ return Base->getProjectInfo(File);
+
+ return llvm::None;
+}
} // namespace clangd
} // namespace clang
OpenPOWER on IntegriCloud