forked from OSchip/llvm-project
[clangd] Reland rL365634
This was reverted in rL365678, the failure was due to YAML parsing of compile_commands.json. Converting backslashes to forward slashes to fix the issue in unittest. llvm-svn: 365748
This commit is contained in:
parent
7916198a41
commit
ad54935c77
|
@ -8,12 +8,18 @@
|
||||||
|
|
||||||
#include "GlobalCompilationDatabase.h"
|
#include "GlobalCompilationDatabase.h"
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
|
#include "Path.h"
|
||||||
#include "clang/Frontend/CompilerInvocation.h"
|
#include "clang/Frontend/CompilerInvocation.h"
|
||||||
#include "clang/Tooling/ArgumentsAdjusters.h"
|
#include "clang/Tooling/ArgumentsAdjusters.h"
|
||||||
#include "clang/Tooling/CompilationDatabase.h"
|
#include "clang/Tooling/CompilationDatabase.h"
|
||||||
|
#include "llvm/ADT/None.h"
|
||||||
#include "llvm/ADT/Optional.h"
|
#include "llvm/ADT/Optional.h"
|
||||||
|
#include "llvm/ADT/STLExtras.h"
|
||||||
#include "llvm/Support/FileSystem.h"
|
#include "llvm/Support/FileSystem.h"
|
||||||
#include "llvm/Support/Path.h"
|
#include "llvm/Support/Path.h"
|
||||||
|
#include <string>
|
||||||
|
#include <tuple>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace clang {
|
namespace clang {
|
||||||
namespace clangd {
|
namespace clangd {
|
||||||
|
@ -43,6 +49,16 @@ std::string getStandardResourceDir() {
|
||||||
return CompilerInvocation::GetResourcesPath("clangd", (void *)&Dummy);
|
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
|
} // namespace
|
||||||
|
|
||||||
static std::string getFallbackClangPath() {
|
static std::string getFallbackClangPath() {
|
||||||
|
@ -81,60 +97,138 @@ DirectoryBasedGlobalCompilationDatabase::
|
||||||
~DirectoryBasedGlobalCompilationDatabase() = default;
|
~DirectoryBasedGlobalCompilationDatabase() = default;
|
||||||
|
|
||||||
llvm::Optional<tooling::CompileCommand>
|
llvm::Optional<tooling::CompileCommand>
|
||||||
DirectoryBasedGlobalCompilationDatabase::getCompileCommand(
|
DirectoryBasedGlobalCompilationDatabase::getCompileCommand(PathRef File) const {
|
||||||
PathRef File, ProjectInfo *Project) const {
|
CDBLookupRequest Req;
|
||||||
if (auto CDB = getCDBForFile(File, Project)) {
|
Req.FileName = File;
|
||||||
auto Candidates = CDB->getCompileCommands(File);
|
Req.ShouldBroadcast = true;
|
||||||
if (!Candidates.empty()) {
|
|
||||||
return std::move(Candidates.front());
|
auto Res = lookupCDB(Req);
|
||||||
}
|
if (!Res) {
|
||||||
} else {
|
|
||||||
log("Failed to find compilation database for {0}", File);
|
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;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<tooling::CompilationDatabase *, /*Cached*/ bool>
|
std::pair<tooling::CompilationDatabase *, /*SentBroadcast*/ bool>
|
||||||
DirectoryBasedGlobalCompilationDatabase::getCDBInDirLocked(PathRef Dir) const {
|
DirectoryBasedGlobalCompilationDatabase::getCDBInDirLocked(PathRef Dir) const {
|
||||||
// FIXME(ibiryukov): Invalidate cached compilation databases on changes
|
// FIXME(ibiryukov): Invalidate cached compilation databases on changes
|
||||||
auto CachedIt = CompilationDatabases.find(Dir);
|
auto CachedIt = CompilationDatabases.find(Dir);
|
||||||
if (CachedIt != CompilationDatabases.end())
|
if (CachedIt != CompilationDatabases.end())
|
||||||
return {CachedIt->second.get(), true};
|
return {CachedIt->second.CDB.get(), CachedIt->second.SentBroadcast};
|
||||||
std::string Error = "";
|
std::string Error = "";
|
||||||
auto CDB = tooling::CompilationDatabase::loadFromDirectory(Dir, Error);
|
|
||||||
auto Result = CDB.get();
|
CachedCDB Entry;
|
||||||
CompilationDatabases.insert(std::make_pair(Dir, std::move(CDB)));
|
Entry.CDB = tooling::CompilationDatabase::loadFromDirectory(Dir, Error);
|
||||||
|
auto Result = Entry.CDB.get();
|
||||||
|
CompilationDatabases[Dir] = std::move(Entry);
|
||||||
|
|
||||||
return {Result, false};
|
return {Result, false};
|
||||||
}
|
}
|
||||||
|
|
||||||
tooling::CompilationDatabase *
|
llvm::Optional<DirectoryBasedGlobalCompilationDatabase::CDBLookupResult>
|
||||||
DirectoryBasedGlobalCompilationDatabase::getCDBForFile(
|
DirectoryBasedGlobalCompilationDatabase::lookupCDB(
|
||||||
PathRef File, ProjectInfo *Project) const {
|
CDBLookupRequest Request) const {
|
||||||
namespace path = llvm::sys::path;
|
assert(llvm::sys::path::is_absolute(Request.FileName) &&
|
||||||
assert((path::is_absolute(File, path::Style::posix) ||
|
|
||||||
path::is_absolute(File, path::Style::windows)) &&
|
|
||||||
"path must be absolute");
|
"path must be absolute");
|
||||||
|
|
||||||
tooling::CompilationDatabase *CDB = nullptr;
|
CDBLookupResult Result;
|
||||||
bool Cached = false;
|
bool SentBroadcast = false;
|
||||||
std::lock_guard<std::mutex> Lock(Mutex);
|
|
||||||
|
{
|
||||||
|
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) {
|
if (CompileCommandsDir) {
|
||||||
std::tie(CDB, Cached) = getCDBInDirLocked(*CompileCommandsDir);
|
assert(*CompileCommandsDir == Result.PI.SourceRoot &&
|
||||||
if (Project && CDB)
|
"Trying to broadcast a CDB outside of CompileCommandsDir!");
|
||||||
Project->SourceRoot = *CompileCommandsDir;
|
OnCommandChanged.broadcast(std::move(AllFiles));
|
||||||
} else {
|
return;
|
||||||
for (auto Path = path::parent_path(File); !CDB && !Path.empty();
|
}
|
||||||
Path = path::parent_path(Path)) {
|
|
||||||
std::tie(CDB, Cached) = getCDBInDirLocked(Path);
|
llvm::StringMap<bool> DirectoryHasCDB;
|
||||||
if (Project && CDB)
|
{
|
||||||
Project->SourceRoot = Path;
|
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.
|
std::vector<std::string> GovernedFiles;
|
||||||
if (CDB && !Cached)
|
for (llvm::StringRef File : AllFiles) {
|
||||||
OnCommandChanged.broadcast(CDB->getAllFiles());
|
// A file is governed by this CDB if lookup for the file would find it.
|
||||||
return CDB;
|
// 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,
|
OverlayCDB::OverlayCDB(const GlobalCompilationDatabase *Base,
|
||||||
|
@ -150,19 +244,16 @@ OverlayCDB::OverlayCDB(const GlobalCompilationDatabase *Base,
|
||||||
}
|
}
|
||||||
|
|
||||||
llvm::Optional<tooling::CompileCommand>
|
llvm::Optional<tooling::CompileCommand>
|
||||||
OverlayCDB::getCompileCommand(PathRef File, ProjectInfo *Project) const {
|
OverlayCDB::getCompileCommand(PathRef File) const {
|
||||||
llvm::Optional<tooling::CompileCommand> Cmd;
|
llvm::Optional<tooling::CompileCommand> Cmd;
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> Lock(Mutex);
|
std::lock_guard<std::mutex> Lock(Mutex);
|
||||||
auto It = Commands.find(File);
|
auto It = Commands.find(File);
|
||||||
if (It != Commands.end()) {
|
if (It != Commands.end())
|
||||||
if (Project)
|
|
||||||
Project->SourceRoot = "";
|
|
||||||
Cmd = It->second;
|
Cmd = It->second;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!Cmd && Base)
|
if (!Cmd && Base)
|
||||||
Cmd = Base->getCompileCommand(File, Project);
|
Cmd = Base->getCompileCommand(File);
|
||||||
if (!Cmd)
|
if (!Cmd)
|
||||||
return llvm::None;
|
return llvm::None;
|
||||||
adjustArguments(*Cmd, ResourceDir);
|
adjustArguments(*Cmd, ResourceDir);
|
||||||
|
@ -191,5 +282,17 @@ void OverlayCDB::setCompileCommand(
|
||||||
OnCommandChanged.broadcast({File});
|
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 clangd
|
||||||
} // namespace clang
|
} // namespace clang
|
||||||
|
|
|
@ -36,9 +36,13 @@ public:
|
||||||
virtual ~GlobalCompilationDatabase() = default;
|
virtual ~GlobalCompilationDatabase() = default;
|
||||||
|
|
||||||
/// If there are any known-good commands for building this file, returns one.
|
/// If there are any known-good commands for building this file, returns one.
|
||||||
/// If the ProjectInfo pointer is set, it will also be populated.
|
|
||||||
virtual llvm::Optional<tooling::CompileCommand>
|
virtual llvm::Optional<tooling::CompileCommand>
|
||||||
getCompileCommand(PathRef File, ProjectInfo * = nullptr) const = 0;
|
getCompileCommand(PathRef File) const = 0;
|
||||||
|
|
||||||
|
/// Finds the closest project to \p File.
|
||||||
|
virtual llvm::Optional<ProjectInfo> getProjectInfo(PathRef File) const {
|
||||||
|
return llvm::None;
|
||||||
|
}
|
||||||
|
|
||||||
/// Makes a guess at how to build a file.
|
/// Makes a guess at how to build a file.
|
||||||
/// The default implementation just runs clang on the file.
|
/// The default implementation just runs clang on the file.
|
||||||
|
@ -67,20 +71,40 @@ public:
|
||||||
|
|
||||||
/// Scans File's parents looking for compilation databases.
|
/// Scans File's parents looking for compilation databases.
|
||||||
/// Any extra flags will be added.
|
/// Any extra flags will be added.
|
||||||
|
/// Might trigger OnCommandChanged, if CDB wasn't broadcasted yet.
|
||||||
llvm::Optional<tooling::CompileCommand>
|
llvm::Optional<tooling::CompileCommand>
|
||||||
getCompileCommand(PathRef File, ProjectInfo * = nullptr) const override;
|
getCompileCommand(PathRef File) const override;
|
||||||
|
|
||||||
|
/// Returns the path to first directory containing a compilation database in
|
||||||
|
/// \p File's parents.
|
||||||
|
llvm::Optional<ProjectInfo> getProjectInfo(PathRef File) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
tooling::CompilationDatabase *getCDBForFile(PathRef File,
|
std::pair<tooling::CompilationDatabase *, /*SentBroadcast*/ bool>
|
||||||
ProjectInfo *) const;
|
|
||||||
std::pair<tooling::CompilationDatabase *, /*Cached*/ bool>
|
|
||||||
getCDBInDirLocked(PathRef File) const;
|
getCDBInDirLocked(PathRef File) const;
|
||||||
|
|
||||||
|
struct CDBLookupRequest {
|
||||||
|
PathRef FileName;
|
||||||
|
// Whether this lookup should trigger discovery of the CDB found.
|
||||||
|
bool ShouldBroadcast = false;
|
||||||
|
};
|
||||||
|
struct CDBLookupResult {
|
||||||
|
tooling::CompilationDatabase *CDB = nullptr;
|
||||||
|
ProjectInfo PI;
|
||||||
|
};
|
||||||
|
llvm::Optional<CDBLookupResult> lookupCDB(CDBLookupRequest Request) const;
|
||||||
|
|
||||||
|
// Performs broadcast on governed files.
|
||||||
|
void broadcastCDB(CDBLookupResult Res) const;
|
||||||
|
|
||||||
mutable std::mutex Mutex;
|
mutable std::mutex Mutex;
|
||||||
/// Caches compilation databases loaded from directories(keys are
|
/// Caches compilation databases loaded from directories(keys are
|
||||||
/// directories).
|
/// directories).
|
||||||
mutable llvm::StringMap<std::unique_ptr<clang::tooling::CompilationDatabase>>
|
struct CachedCDB {
|
||||||
CompilationDatabases;
|
std::unique_ptr<clang::tooling::CompilationDatabase> CDB = nullptr;
|
||||||
|
bool SentBroadcast = false;
|
||||||
|
};
|
||||||
|
mutable llvm::StringMap<CachedCDB> CompilationDatabases;
|
||||||
|
|
||||||
/// Used for command argument pointing to folder where compile_commands.json
|
/// Used for command argument pointing to folder where compile_commands.json
|
||||||
/// is located.
|
/// is located.
|
||||||
|
@ -105,8 +129,9 @@ public:
|
||||||
llvm::Optional<std::string> ResourceDir = llvm::None);
|
llvm::Optional<std::string> ResourceDir = llvm::None);
|
||||||
|
|
||||||
llvm::Optional<tooling::CompileCommand>
|
llvm::Optional<tooling::CompileCommand>
|
||||||
getCompileCommand(PathRef File, ProjectInfo * = nullptr) const override;
|
getCompileCommand(PathRef File) const override;
|
||||||
tooling::CompileCommand getFallbackCommand(PathRef File) const override;
|
tooling::CompileCommand getFallbackCommand(PathRef File) const override;
|
||||||
|
llvm::Optional<ProjectInfo> getProjectInfo(PathRef File) const override;
|
||||||
|
|
||||||
/// Sets or clears the compilation command for a particular file.
|
/// Sets or clears the compilation command for a particular file.
|
||||||
void
|
void
|
||||||
|
|
|
@ -215,8 +215,8 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
llvm::Optional<tooling::CompileCommand>
|
llvm::Optional<tooling::CompileCommand>
|
||||||
getCompileCommand(PathRef File, ProjectInfo *PI = nullptr) const override {
|
getCompileCommand(PathRef File) const override {
|
||||||
auto Cmd = Base->getCompileCommand(File, PI);
|
auto Cmd = Base->getCompileCommand(File);
|
||||||
if (!Cmd || Cmd->CommandLine.empty())
|
if (!Cmd || Cmd->CommandLine.empty())
|
||||||
return Cmd;
|
return Cmd;
|
||||||
|
|
||||||
|
@ -240,6 +240,10 @@ public:
|
||||||
return addSystemIncludes(*Cmd, SystemIncludes);
|
return addSystemIncludes(*Cmd, SystemIncludes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
llvm::Optional<ProjectInfo> getProjectInfo(PathRef File) const override {
|
||||||
|
return Base->getProjectInfo(File);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
mutable std::mutex Mu;
|
mutable std::mutex Mu;
|
||||||
// Caches includes extracted from a driver.
|
// Caches includes extracted from a driver.
|
||||||
|
|
|
@ -619,11 +619,15 @@ BackgroundIndex::loadShards(std::vector<std::string> ChangedFiles) {
|
||||||
llvm::StringSet<> LoadedShards;
|
llvm::StringSet<> LoadedShards;
|
||||||
Rebuilder.startLoading();
|
Rebuilder.startLoading();
|
||||||
for (const auto &File : ChangedFiles) {
|
for (const auto &File : ChangedFiles) {
|
||||||
ProjectInfo PI;
|
auto Cmd = CDB.getCompileCommand(File);
|
||||||
auto Cmd = CDB.getCompileCommand(File, &PI);
|
|
||||||
if (!Cmd)
|
if (!Cmd)
|
||||||
continue;
|
continue;
|
||||||
BackgroundIndexStorage *IndexStorage = IndexStorageFactory(PI.SourceRoot);
|
|
||||||
|
std::string ProjectRoot;
|
||||||
|
if (auto PI = CDB.getProjectInfo(File))
|
||||||
|
ProjectRoot = std::move(PI->SourceRoot);
|
||||||
|
|
||||||
|
BackgroundIndexStorage *IndexStorage = IndexStorageFactory(ProjectRoot);
|
||||||
auto Dependencies = loadShard(*Cmd, IndexStorage, LoadedShards);
|
auto Dependencies = loadShard(*Cmd, IndexStorage, LoadedShards);
|
||||||
for (const auto &Dependency : Dependencies) {
|
for (const auto &Dependency : Dependencies) {
|
||||||
if (!Dependency.NeedsReIndexing || FilesToIndex.count(Dependency.Path))
|
if (!Dependency.NeedsReIndexing || FilesToIndex.count(Dependency.Path))
|
||||||
|
|
|
@ -1064,7 +1064,7 @@ TEST_F(ClangdVFSTest, FallbackWhenPreambleIsNotReady) {
|
||||||
ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
|
ClangdServer Server(CDB, FS, DiagConsumer, ClangdServer::optsForTest());
|
||||||
|
|
||||||
auto FooCpp = testPath("foo.cpp");
|
auto FooCpp = testPath("foo.cpp");
|
||||||
Annotations Code(R"cpp(
|
Annotations Code(R"cpp(
|
||||||
namespace ns { int xyz; }
|
namespace ns { int xyz; }
|
||||||
using namespace ns;
|
using namespace ns;
|
||||||
int main() {
|
int main() {
|
||||||
|
@ -1113,7 +1113,7 @@ TEST_F(ClangdVFSTest, FallbackWhenWaitingForCompileCommand) {
|
||||||
: CanReturnCommand(CanReturnCommand) {}
|
: CanReturnCommand(CanReturnCommand) {}
|
||||||
|
|
||||||
llvm::Optional<tooling::CompileCommand>
|
llvm::Optional<tooling::CompileCommand>
|
||||||
getCompileCommand(PathRef File, ProjectInfo * = nullptr) const override {
|
getCompileCommand(PathRef File) const override {
|
||||||
// FIXME: make this timeout and fail instead of waiting forever in case
|
// FIXME: make this timeout and fail instead of waiting forever in case
|
||||||
// something goes wrong.
|
// something goes wrong.
|
||||||
CanReturnCommand.wait();
|
CanReturnCommand.wait();
|
||||||
|
|
|
@ -8,10 +8,21 @@
|
||||||
|
|
||||||
#include "GlobalCompilationDatabase.h"
|
#include "GlobalCompilationDatabase.h"
|
||||||
|
|
||||||
|
#include "Path.h"
|
||||||
#include "TestFS.h"
|
#include "TestFS.h"
|
||||||
|
#include "llvm/ADT/Optional.h"
|
||||||
|
#include "llvm/ADT/SmallString.h"
|
||||||
#include "llvm/ADT/StringExtras.h"
|
#include "llvm/ADT/StringExtras.h"
|
||||||
|
#include "llvm/ADT/StringRef.h"
|
||||||
|
#include "llvm/Support/FileSystem.h"
|
||||||
|
#include "llvm/Support/FormatVariadic.h"
|
||||||
|
#include "llvm/Support/MemoryBuffer.h"
|
||||||
|
#include "llvm/Support/Path.h"
|
||||||
|
#include "llvm/Support/raw_ostream.h"
|
||||||
#include "gmock/gmock.h"
|
#include "gmock/gmock.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
#include <fstream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace clang {
|
namespace clang {
|
||||||
namespace clangd {
|
namespace clangd {
|
||||||
|
@ -20,8 +31,10 @@ using ::testing::AllOf;
|
||||||
using ::testing::Contains;
|
using ::testing::Contains;
|
||||||
using ::testing::ElementsAre;
|
using ::testing::ElementsAre;
|
||||||
using ::testing::EndsWith;
|
using ::testing::EndsWith;
|
||||||
|
using ::testing::IsEmpty;
|
||||||
using ::testing::Not;
|
using ::testing::Not;
|
||||||
using ::testing::StartsWith;
|
using ::testing::StartsWith;
|
||||||
|
using ::testing::UnorderedElementsAre;
|
||||||
|
|
||||||
TEST(GlobalCompilationDatabaseTest, FallbackCommand) {
|
TEST(GlobalCompilationDatabaseTest, FallbackCommand) {
|
||||||
DirectoryBasedGlobalCompilationDatabase DB(None);
|
DirectoryBasedGlobalCompilationDatabase DB(None);
|
||||||
|
@ -50,13 +63,9 @@ class OverlayCDBTest : public ::testing::Test {
|
||||||
class BaseCDB : public GlobalCompilationDatabase {
|
class BaseCDB : public GlobalCompilationDatabase {
|
||||||
public:
|
public:
|
||||||
llvm::Optional<tooling::CompileCommand>
|
llvm::Optional<tooling::CompileCommand>
|
||||||
getCompileCommand(llvm::StringRef File,
|
getCompileCommand(llvm::StringRef File) const override {
|
||||||
ProjectInfo *Project) const override {
|
if (File == testPath("foo.cc"))
|
||||||
if (File == testPath("foo.cc")) {
|
|
||||||
if (Project)
|
|
||||||
Project->SourceRoot = testRoot();
|
|
||||||
return cmd(File, "-DA=1");
|
return cmd(File, "-DA=1");
|
||||||
}
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,6 +73,10 @@ class OverlayCDBTest : public ::testing::Test {
|
||||||
getFallbackCommand(llvm::StringRef File) const override {
|
getFallbackCommand(llvm::StringRef File) const override {
|
||||||
return cmd(File, "-DA=2");
|
return cmd(File, "-DA=2");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
llvm::Optional<ProjectInfo> getProjectInfo(PathRef File) const override {
|
||||||
|
return ProjectInfo{testRoot()};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -153,6 +166,110 @@ TEST_F(OverlayCDBTest, Adjustments) {
|
||||||
Not(Contains("random-plugin"))));
|
Not(Contains("random-plugin"))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(GlobalCompilationDatabaseTest, DiscoveryWithNestedCDBs) {
|
||||||
|
const char *const CDBOuter =
|
||||||
|
R"cdb(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"file": "a.cc",
|
||||||
|
"command": "",
|
||||||
|
"directory": "{0}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "build/gen.cc",
|
||||||
|
"command": "",
|
||||||
|
"directory": "{0}",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "build/gen2.cc",
|
||||||
|
"command": "",
|
||||||
|
"directory": "{0}",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)cdb";
|
||||||
|
const char *const CDBInner =
|
||||||
|
R"cdb(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"file": "gen.cc",
|
||||||
|
"command": "",
|
||||||
|
"directory": "{0}/build",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)cdb";
|
||||||
|
class CleaningFS {
|
||||||
|
public:
|
||||||
|
llvm::SmallString<128> Root;
|
||||||
|
|
||||||
|
CleaningFS() {
|
||||||
|
EXPECT_FALSE(
|
||||||
|
llvm::sys::fs::createUniqueDirectory("clangd-cdb-test", Root))
|
||||||
|
<< "Failed to create unique directory";
|
||||||
|
}
|
||||||
|
|
||||||
|
~CleaningFS() {
|
||||||
|
EXPECT_FALSE(llvm::sys::fs::remove_directories(Root))
|
||||||
|
<< "Failed to cleanup " << Root;
|
||||||
|
}
|
||||||
|
|
||||||
|
void registerFile(PathRef RelativePath, llvm::StringRef Contents) {
|
||||||
|
llvm::SmallString<128> AbsPath(Root);
|
||||||
|
llvm::sys::path::append(AbsPath, RelativePath);
|
||||||
|
|
||||||
|
EXPECT_FALSE(llvm::sys::fs::create_directories(
|
||||||
|
llvm::sys::path::parent_path(AbsPath)))
|
||||||
|
<< "Failed to create directories for: " << AbsPath;
|
||||||
|
|
||||||
|
std::error_code EC;
|
||||||
|
llvm::raw_fd_ostream OS(AbsPath, EC);
|
||||||
|
EXPECT_FALSE(EC) << "Failed to open " << AbsPath << " for writing";
|
||||||
|
OS << llvm::formatv(Contents.data(),
|
||||||
|
llvm::sys::path::convert_to_slash(Root));
|
||||||
|
OS.close();
|
||||||
|
|
||||||
|
EXPECT_FALSE(OS.has_error());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
CleaningFS FS;
|
||||||
|
FS.registerFile("compile_commands.json", CDBOuter);
|
||||||
|
FS.registerFile("build/compile_commands.json", CDBInner);
|
||||||
|
|
||||||
|
// Note that gen2.cc goes missing with our following model, not sure this
|
||||||
|
// happens in practice though.
|
||||||
|
{
|
||||||
|
DirectoryBasedGlobalCompilationDatabase DB(llvm::None);
|
||||||
|
std::vector<std::string> DiscoveredFiles;
|
||||||
|
auto Sub =
|
||||||
|
DB.watch([&DiscoveredFiles](const std::vector<std::string> Changes) {
|
||||||
|
DiscoveredFiles = Changes;
|
||||||
|
});
|
||||||
|
DB.getCompileCommand((FS.Root + "/a.cc").str());
|
||||||
|
EXPECT_THAT(DiscoveredFiles, UnorderedElementsAre(EndsWith("a.cc")));
|
||||||
|
|
||||||
|
DB.getCompileCommand((FS.Root + "/build/gen.cc").str());
|
||||||
|
EXPECT_THAT(DiscoveredFiles, UnorderedElementsAre(EndsWith("gen.cc")));
|
||||||
|
}
|
||||||
|
|
||||||
|
// With a custom compile commands dir.
|
||||||
|
{
|
||||||
|
DirectoryBasedGlobalCompilationDatabase DB(FS.Root.str().str());
|
||||||
|
std::vector<std::string> DiscoveredFiles;
|
||||||
|
auto Sub =
|
||||||
|
DB.watch([&DiscoveredFiles](const std::vector<std::string> Changes) {
|
||||||
|
DiscoveredFiles = Changes;
|
||||||
|
});
|
||||||
|
DB.getCompileCommand((FS.Root + "/a.cc").str());
|
||||||
|
EXPECT_THAT(DiscoveredFiles,
|
||||||
|
UnorderedElementsAre(EndsWith("a.cc"), EndsWith("gen.cc"),
|
||||||
|
EndsWith("gen2.cc")));
|
||||||
|
|
||||||
|
DiscoveredFiles.clear();
|
||||||
|
DB.getCompileCommand((FS.Root + "/build/gen.cc").str());
|
||||||
|
EXPECT_THAT(DiscoveredFiles, IsEmpty());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace clangd
|
} // namespace clangd
|
||||||
} // namespace clang
|
} // namespace clang
|
||||||
|
|
|
@ -6,7 +6,11 @@
|
||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
#include "TestFS.h"
|
#include "TestFS.h"
|
||||||
|
#include "GlobalCompilationDatabase.h"
|
||||||
|
#include "Path.h"
|
||||||
#include "URI.h"
|
#include "URI.h"
|
||||||
|
#include "llvm/ADT/None.h"
|
||||||
|
#include "llvm/ADT/Optional.h"
|
||||||
#include "llvm/ADT/StringRef.h"
|
#include "llvm/ADT/StringRef.h"
|
||||||
#include "llvm/Support/Errc.h"
|
#include "llvm/Support/Errc.h"
|
||||||
#include "llvm/Support/Path.h"
|
#include "llvm/Support/Path.h"
|
||||||
|
@ -36,9 +40,13 @@ MockCompilationDatabase::MockCompilationDatabase(llvm::StringRef Directory,
|
||||||
// -ffreestanding avoids implicit stdc-predef.h.
|
// -ffreestanding avoids implicit stdc-predef.h.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
llvm::Optional<ProjectInfo>
|
||||||
|
MockCompilationDatabase::getProjectInfo(PathRef File) const {
|
||||||
|
return ProjectInfo{Directory};
|
||||||
|
};
|
||||||
|
|
||||||
llvm::Optional<tooling::CompileCommand>
|
llvm::Optional<tooling::CompileCommand>
|
||||||
MockCompilationDatabase::getCompileCommand(PathRef File,
|
MockCompilationDatabase::getCompileCommand(PathRef File) const {
|
||||||
ProjectInfo *Project) const {
|
|
||||||
if (ExtraClangFlags.empty())
|
if (ExtraClangFlags.empty())
|
||||||
return None;
|
return None;
|
||||||
|
|
||||||
|
@ -57,8 +65,6 @@ MockCompilationDatabase::getCompileCommand(PathRef File,
|
||||||
CommandLine.push_back(RelativeFilePath.str());
|
CommandLine.push_back(RelativeFilePath.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Project)
|
|
||||||
Project->SourceRoot = Directory;
|
|
||||||
return {tooling::CompileCommand(Directory != llvm::StringRef()
|
return {tooling::CompileCommand(Directory != llvm::StringRef()
|
||||||
? Directory
|
? Directory
|
||||||
: llvm::sys::path::parent_path(File),
|
: llvm::sys::path::parent_path(File),
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
#ifndef LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_TESTFS_H
|
#ifndef LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_TESTFS_H
|
||||||
#define LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_TESTFS_H
|
#define LLVM_CLANG_TOOLS_EXTRA_UNITTESTS_CLANGD_TESTFS_H
|
||||||
#include "ClangdServer.h"
|
#include "ClangdServer.h"
|
||||||
|
#include "GlobalCompilationDatabase.h"
|
||||||
|
#include "Path.h"
|
||||||
#include "llvm/ADT/IntrusiveRefCntPtr.h"
|
#include "llvm/ADT/IntrusiveRefCntPtr.h"
|
||||||
#include "llvm/Support/Path.h"
|
#include "llvm/Support/Path.h"
|
||||||
#include "llvm/Support/VirtualFileSystem.h"
|
#include "llvm/Support/VirtualFileSystem.h"
|
||||||
|
@ -48,7 +50,9 @@ public:
|
||||||
StringRef RelPathPrefix = StringRef());
|
StringRef RelPathPrefix = StringRef());
|
||||||
|
|
||||||
llvm::Optional<tooling::CompileCommand>
|
llvm::Optional<tooling::CompileCommand>
|
||||||
getCompileCommand(PathRef File, ProjectInfo * = nullptr) const override;
|
getCompileCommand(PathRef File) const override;
|
||||||
|
|
||||||
|
llvm::Optional<ProjectInfo> getProjectInfo(PathRef File) const override;
|
||||||
|
|
||||||
std::vector<std::string> ExtraClangFlags;
|
std::vector<std::string> ExtraClangFlags;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue