diff --git a/clang-tools-extra/clangd/GlobalCompilationDatabase.h b/clang-tools-extra/clangd/GlobalCompilationDatabase.h --- a/clang-tools-extra/clangd/GlobalCompilationDatabase.h +++ b/clang-tools-extra/clangd/GlobalCompilationDatabase.h @@ -86,6 +86,7 @@ std::string Path; // Not case-folded. std::unique_ptr CDB = nullptr; bool SentBroadcast = false; + bool Diagnosed = false; }; CachedCDB &getCDBInDirLocked(PathRef File) const; @@ -93,6 +94,8 @@ PathRef FileName; // Whether this lookup should trigger discovery of the CDB found. bool ShouldBroadcast = false; + // If no CDB is found, attempt diagnosis of the directories searched. + bool ShouldDiagnose = false; }; struct CDBLookupResult { tooling::CompilationDatabase *CDB = nullptr; @@ -100,6 +103,9 @@ }; llvm::Optional lookupCDB(CDBLookupRequest Request) const; + // Examines a directory where a CDB was not found, to suggest fixes. + bool diagnose(llvm::StringRef Path) const; + // Performs broadcast on governed files. void broadcastCDB(CDBLookupResult Res) const; diff --git a/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp b/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp --- a/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp +++ b/clang-tools-extra/clangd/GlobalCompilationDatabase.cpp @@ -16,6 +16,7 @@ #include "llvm/ADT/None.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SmallString.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/FileUtilities.h" @@ -71,6 +72,7 @@ CDBLookupRequest Req; Req.FileName = File; Req.ShouldBroadcast = true; + Req.ShouldDiagnose = true; auto Res = lookupCDB(Req); if (!Res) { @@ -129,10 +131,18 @@ CDBLookupRequest Request) const { assert(llvm::sys::path::is_absolute(Request.FileName) && "path must be absolute"); + std::string CanonicalName = removeDots(Request.FileName); bool ShouldBroadcast = false; CDBLookupResult Result; + llvm::SmallVector DiagnoseDirs; + auto DiagnoseBeforeReturn = llvm::make_scope_exit([&]{ + for (PathRef Dir : DiagnoseDirs) + if (diagnose(Dir)) + break; + }); + { std::lock_guard Lock(Mutex); CachedCDB *Entry = nullptr; @@ -142,16 +152,22 @@ // Traverse the canonical version to prevent false positives. i.e.: // src/build/../a.cc can detect a CDB in /src/build if not canonicalized. // FIXME(sammccall): this loop is hot, use a union-find-like structure. - actOnAllParentDirectories(removeDots(Request.FileName), - [&](PathRef Path) { - Entry = &getCDBInDirLocked(Path); - return Entry->CDB != nullptr; - }); + actOnAllParentDirectories(CanonicalName, [&](PathRef Path) { + Entry = &getCDBInDirLocked(Path); + if (Request.ShouldDiagnose && !Entry->CDB && !Entry->Diagnosed) { + Entry->Diagnosed = true; + DiagnoseDirs.push_back(Path); + } + return Entry->CDB != nullptr; + }); } if (!Entry || !Entry->CDB) return llvm::None; + // Don't diagnose if we found a CDB in the end. + DiagnoseDirs.clear(); + // Mark CDB as broadcasted to make sure discovery is performed once. if (Request.ShouldBroadcast && !Entry->SentBroadcast) { Entry->SentBroadcast = true; @@ -231,6 +247,29 @@ return Res->PI; } +bool DirectoryBasedGlobalCompilationDatabase::diagnose(PathRef Dir) const { + vlog("Diagnosing missing CDB in {0}", Dir); + + namespace fs = llvm::sys::fs; + namespace path = llvm::sys::path; + llvm::SmallString<256> Path = Dir; + auto Exists = [&](llvm::StringRef Filename){ + auto Size = Path.size(); + path::append(Path, Filename); + bool Ret = fs::exists(Path); + Path.resize(Size); + return Ret; + }; + if (Exists("CMakeLists.txt")) { + Path.resize(path::parent_path(Path).size()); + if (!Exists("CMakeLists.txt")) { + elog("Missing compile_commands.json in {0}", Dir); + return true; + } + } + return false; +} + OverlayCDB::OverlayCDB(const GlobalCompilationDatabase *Base, std::vector FallbackFlags, tooling::ArgumentsAdjuster Adjuster)