Index: clangd/CMakeLists.txt =================================================================== --- clangd/CMakeLists.txt +++ clangd/CMakeLists.txt @@ -10,6 +10,7 @@ DraftStore.cpp GlobalCompilationDatabase.cpp JSONRPCDispatcher.cpp + Logger.cpp Protocol.cpp ProtocolHandlers.cpp Index: clangd/ClangdLSPServer.cpp =================================================================== --- clangd/ClangdLSPServer.cpp +++ clangd/ClangdLSPServer.cpp @@ -223,9 +223,9 @@ ClangdLSPServer::ClangdLSPServer(JSONOutput &Out, unsigned AsyncThreadsCount, bool SnippetCompletions, llvm::Optional ResourceDir) - : Out(Out), DiagConsumer(*this), + : Out(Out), CDB(/*Logger=*/Out), DiagConsumer(*this), Server(CDB, DiagConsumer, FSProvider, AsyncThreadsCount, - SnippetCompletions, ResourceDir) {} + SnippetCompletions, /*Logger=*/Out, ResourceDir) {} void ClangdLSPServer::run(std::istream &In) { assert(!IsDone && "Run was called before"); Index: clangd/ClangdServer.h =================================================================== --- clangd/ClangdServer.h +++ clangd/ClangdServer.h @@ -36,6 +36,8 @@ namespace clangd { +class Logger; + /// Turn a [line, column] pair into an offset in Code. size_t positionToOffset(StringRef Code, Position P); @@ -201,10 +203,12 @@ /// \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. + /// + /// Various messages are logged using \p Logger. ClangdServer(GlobalCompilationDatabase &CDB, DiagnosticsConsumer &DiagConsumer, FileSystemProvider &FSProvider, unsigned AsyncThreadsCount, - bool SnippetCompletions, + bool SnippetCompletions, clangd::Logger &Logger, llvm::Optional ResourceDir = llvm::None); /// Add a \p File to the list of tracked C++ files or update the contents if @@ -267,6 +271,7 @@ std::future scheduleCancelRebuild(std::shared_ptr Resources); + clangd::Logger &Logger; GlobalCompilationDatabase &CDB; DiagnosticsConsumer &DiagConsumer; FileSystemProvider &FSProvider; Index: clangd/ClangdServer.cpp =================================================================== --- clangd/ClangdServer.cpp +++ clangd/ClangdServer.cpp @@ -145,8 +145,10 @@ DiagnosticsConsumer &DiagConsumer, FileSystemProvider &FSProvider, unsigned AsyncThreadsCount, bool SnippetCompletions, + clangd::Logger &Logger, llvm::Optional ResourceDir) - : CDB(CDB), DiagConsumer(DiagConsumer), FSProvider(FSProvider), + : Logger(Logger), CDB(CDB), DiagConsumer(DiagConsumer), + FSProvider(FSProvider), ResourceDir(ResourceDir ? ResourceDir->str() : getStandardResourceDir()), PCHs(std::make_shared()), WorkScheduler(AsyncThreadsCount), SnippetCompletions(SnippetCompletions) { @@ -157,7 +159,7 @@ auto TaggedFS = FSProvider.getTaggedFileSystem(File); std::shared_ptr Resources = - Units.getOrCreateFile(File, ResourceDir, CDB, PCHs, TaggedFS.Value); + Units.getOrCreateFile(File, ResourceDir, CDB, PCHs, TaggedFS.Value, Logger); return scheduleReparseAndDiags(File, VersionedDraft{Version, Contents.str()}, std::move(Resources), std::move(TaggedFS)); } @@ -175,7 +177,7 @@ auto TaggedFS = FSProvider.getTaggedFileSystem(File); auto Recreated = Units.recreateFileIfCompileCommandChanged( - File, ResourceDir, CDB, PCHs, TaggedFS.Value); + File, ResourceDir, CDB, PCHs, TaggedFS.Value, Logger); // Note that std::future from this cleanup action is ignored. scheduleCancelRebuild(std::move(Recreated.RemovedFile)); @@ -210,7 +212,7 @@ std::vector Result = clangd::codeComplete( File, Resources->getCompileCommand(), Preamble ? &Preamble->Preamble : nullptr, *OverridenContents, Pos, - TaggedFS.Value, PCHs, SnippetCompletions); + TaggedFS.Value, PCHs, SnippetCompletions, Logger); return make_tagged(std::move(Result), TaggedFS.Tag); } @@ -278,10 +280,10 @@ assert(Resources && "Calling findDefinitions on non-added file"); std::vector Result; - Resources->getAST().get()->runUnderLock([Pos, &Result](ParsedAST *AST) { + Resources->getAST().get()->runUnderLock([Pos, &Result, this](ParsedAST *AST) { if (!AST) return; - Result = clangd::findDefinitions(*AST, Pos); + Result = clangd::findDefinitions(*AST, Pos, Logger); }); return make_tagged(std::move(Result), TaggedFS.Tag); } Index: clangd/ClangdUnit.h =================================================================== --- clangd/ClangdUnit.h +++ clangd/ClangdUnit.h @@ -40,6 +40,8 @@ namespace clangd { +class Logger; + /// A diagnostic with its FixIts. struct DiagWithFixIts { clangd::Diagnostic Diag; @@ -57,7 +59,7 @@ ArrayRef PreambleDeclIDs, std::unique_ptr Buffer, std::shared_ptr PCHs, - IntrusiveRefCntPtr VFS); + IntrusiveRefCntPtr VFS, clangd::Logger &Logger); ParsedAST(ParsedAST &&Other); ParsedAST &operator=(ParsedAST &&Other); @@ -141,11 +143,11 @@ // deferRebuild will hold references to it. static std::shared_ptr Create(PathRef FileName, tooling::CompileCommand Command, - std::shared_ptr PCHs); + std::shared_ptr PCHs, clangd::Logger &Logger); private: CppFile(PathRef FileName, tooling::CompileCommand Command, - std::shared_ptr PCHs); + std::shared_ptr PCHs, clangd::Logger &Logger); public: CppFile(CppFile const &) = delete; @@ -246,6 +248,8 @@ std::shared_ptr LatestAvailablePreamble; /// Utility class, required by clang. std::shared_ptr PCHs; + /// Used for logging various messages. + clangd::Logger &Logger; }; /// Get code completions at a specified \p Pos in \p FileName. @@ -254,10 +258,11 @@ PrecompiledPreamble const *Preamble, StringRef Contents, Position Pos, IntrusiveRefCntPtr VFS, std::shared_ptr PCHs, - bool SnippetCompletions); + bool SnippetCompletions, clangd::Logger &Logger); /// Get definition of symbol at a specified \p Pos. -std::vector findDefinitions(ParsedAST &AST, Position Pos); +std::vector findDefinitions(ParsedAST &AST, Position Pos, + clangd::Logger &Logger); /// For testing/debugging purposes. Note that this method deserializes all /// unserialized Decls, so use with care. Index: clangd/ClangdUnit.cpp =================================================================== --- clangd/ClangdUnit.cpp +++ clangd/ClangdUnit.cpp @@ -26,6 +26,7 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/Format.h" +#include "Logger.h" #include #include @@ -525,7 +526,7 @@ PrecompiledPreamble const *Preamble, StringRef Contents, Position Pos, IntrusiveRefCntPtr VFS, std::shared_ptr PCHs, - bool SnippetCompletions) { + bool SnippetCompletions, clangd::Logger &Logger) { std::vector ArgStrs; for (const auto &S : Command.CommandLine) ArgStrs.push_back(S.c_str()); @@ -583,12 +584,13 @@ SyntaxOnlyAction Action; if (!Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0])) { - // FIXME(ibiryukov): log errors + Logger.log("BeginSourceFile() failed when running codeComplete for " + + FileName); return Items; } - if (!Action.Execute()) { - // FIXME(ibiryukov): log errors - } + if (!Action.Execute()) + Logger.log("Execute() failed when running codeComplete for " + FileName); + Action.EndSourceFile(); return Items; @@ -604,7 +606,8 @@ ArrayRef PreambleDeclIDs, std::unique_ptr Buffer, std::shared_ptr PCHs, - IntrusiveRefCntPtr VFS) { + IntrusiveRefCntPtr VFS, + clangd::Logger &Logger) { std::vector ASTDiags; StoreDiagsConsumer UnitDiagsConsumer(/*ref*/ ASTDiags); @@ -618,13 +621,14 @@ Clang.get()); auto Action = llvm::make_unique(); - if (!Action->BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0])) { - // FIXME(ibiryukov): log error + const FrontendInputFile &MainInput = Clang->getFrontendOpts().Inputs[0]; + if (!Action->BeginSourceFile(*Clang, MainInput)) { + Logger.log("BeginSourceFile() failed when building AST for " + + MainInput.getFile()); return llvm::None; } - if (!Action->Execute()) { - // FIXME(ibiryukov): log error - } + if (!Action->Execute()) + Logger.log("Execute() failed when building AST for " + MainInput.getFile()); // UnitDiagsConsumer is local, we can not store it in CompilerInstance that // has a longer lifetime. @@ -789,7 +793,8 @@ } } // namespace -std::vector clangd::findDefinitions(ParsedAST &AST, Position Pos) { +std::vector clangd::findDefinitions(ParsedAST &AST, Position Pos, + clangd::Logger &Logger) { const SourceManager &SourceMgr = AST.getASTContext().getSourceManager(); const FileEntry *FE = SourceMgr.getFileEntryForID(SourceMgr.getMainFileID()); if (!FE) @@ -889,15 +894,16 @@ std::shared_ptr CppFile::Create(PathRef FileName, tooling::CompileCommand Command, - std::shared_ptr PCHs) { + std::shared_ptr PCHs, clangd::Logger &Logger) { return std::shared_ptr( - new CppFile(FileName, std::move(Command), std::move(PCHs))); + new CppFile(FileName, std::move(Command), std::move(PCHs), Logger)); } CppFile::CppFile(PathRef FileName, tooling::CompileCommand Command, - std::shared_ptr PCHs) + std::shared_ptr PCHs, + clangd::Logger &Logger) : FileName(FileName), Command(std::move(Command)), RebuildCounter(0), - RebuildInProgress(false), PCHs(std::move(PCHs)) { + RebuildInProgress(false), PCHs(std::move(PCHs)), Logger(Logger) { std::lock_guard Lock(Mutex); LatestAvailablePreamble = nullptr; @@ -1078,7 +1084,7 @@ // Compute updated AST. llvm::Optional NewAST = ParsedAST::Build(std::move(CI), PreambleForAST, SerializedPreambleDecls, - std::move(ContentsBuffer), PCHs, VFS); + std::move(ContentsBuffer), PCHs, VFS, That->Logger); if (NewAST) { Diagnostics.insert(Diagnostics.end(), NewAST->getDiagnostics().begin(), Index: clangd/ClangdUnitStore.h =================================================================== --- clangd/ClangdUnitStore.h +++ clangd/ClangdUnitStore.h @@ -20,14 +20,15 @@ namespace clang { namespace clangd { +class Logger; + /// Thread-safe mapping from FileNames to CppFile. class CppFileCollection { public: - std::shared_ptr - getOrCreateFile(PathRef File, PathRef ResourceDir, - GlobalCompilationDatabase &CDB, - std::shared_ptr PCHs, - IntrusiveRefCntPtr VFS) { + std::shared_ptr getOrCreateFile( + PathRef File, PathRef ResourceDir, GlobalCompilationDatabase &CDB, + std::shared_ptr PCHs, + IntrusiveRefCntPtr VFS, clangd::Logger &Logger) { std::lock_guard Lock(Mutex); auto It = OpenedFiles.find(File); @@ -36,7 +37,7 @@ It = OpenedFiles .try_emplace(File, CppFile::Create(File, std::move(Command), - std::move(PCHs))) + std::move(PCHs), Logger)) .first; } return It->second; @@ -59,7 +60,7 @@ RecreateResult recreateFileIfCompileCommandChanged( PathRef File, PathRef ResourceDir, GlobalCompilationDatabase &CDB, std::shared_ptr PCHs, - IntrusiveRefCntPtr VFS); + IntrusiveRefCntPtr VFS, clangd::Logger &Logger); std::shared_ptr getFile(PathRef File) { std::lock_guard Lock(Mutex); Index: clangd/ClangdUnitStore.cpp =================================================================== --- clangd/ClangdUnitStore.cpp +++ clangd/ClangdUnitStore.cpp @@ -30,7 +30,7 @@ CppFileCollection::recreateFileIfCompileCommandChanged( PathRef File, PathRef ResourceDir, GlobalCompilationDatabase &CDB, std::shared_ptr PCHs, - IntrusiveRefCntPtr VFS) { + IntrusiveRefCntPtr VFS, clangd::Logger &Logger) { auto NewCommand = getCompileCommand(CDB, File, ResourceDir); std::lock_guard Lock(Mutex); @@ -41,12 +41,13 @@ if (It == OpenedFiles.end()) { It = OpenedFiles .try_emplace(File, CppFile::Create(File, std::move(NewCommand), - std::move(PCHs))) + std::move(PCHs), Logger)) .first; } else if (!compileCommandsAreEqual(It->second->getCompileCommand(), NewCommand)) { Result.RemovedFile = std::move(It->second); - It->second = CppFile::Create(File, std::move(NewCommand), std::move(PCHs)); + It->second = + CppFile::Create(File, std::move(NewCommand), std::move(PCHs), Logger); } Result.FileInCollection = It->second; return Result; Index: clangd/GlobalCompilationDatabase.h =================================================================== --- clangd/GlobalCompilationDatabase.h +++ clangd/GlobalCompilationDatabase.h @@ -25,6 +25,8 @@ namespace clangd { +class Logger; + /// Returns a default compile command to use for \p File. tooling::CompileCommand getDefaultCompileCommand(PathRef File); @@ -45,6 +47,8 @@ class DirectoryBasedGlobalCompilationDatabase : public GlobalCompilationDatabase { public: + DirectoryBasedGlobalCompilationDatabase(clangd::Logger &Logger); + std::vector getCompileCommands(PathRef File) override; @@ -61,6 +65,8 @@ /// Stores extra flags per file. llvm::StringMap> ExtraFlagsForFile; + /// Used for logging. + clangd::Logger &Logger; }; } // namespace clangd } // namespace clang Index: clangd/GlobalCompilationDatabase.cpp =================================================================== --- clangd/GlobalCompilationDatabase.cpp +++ clangd/GlobalCompilationDatabase.cpp @@ -11,6 +11,7 @@ #include "clang/Tooling/CompilationDatabase.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" +#include "Logger.h" namespace clang { namespace clangd { @@ -36,6 +37,10 @@ /*Output=*/""); } +DirectoryBasedGlobalCompilationDatabase:: + DirectoryBasedGlobalCompilationDatabase(clangd::Logger &Logger) + : Logger(Logger) {} + std::vector DirectoryBasedGlobalCompilationDatabase::getCompileCommands(PathRef File) { std::vector Commands; @@ -77,26 +82,19 @@ auto CachedIt = CompilationDatabases.find(Path); if (CachedIt != CompilationDatabases.end()) return CachedIt->second.get(); + std::string Error; auto CDB = tooling::CompilationDatabase::loadFromDirectory(Path, Error); - if (!CDB) { - if (!Error.empty()) { - // FIXME(ibiryukov): logging - // Output.log("Error when trying to load compilation database from " + - // Twine(Path) + ": " + Twine(Error) + "\n"); - } + if (!CDB) continue; - } // FIXME(ibiryukov): Invalidate cached compilation databases on changes - auto result = CDB.get(); + auto Result = CDB.get(); CompilationDatabases.insert(std::make_pair(Path, std::move(CDB))); - return result; + return Result; } - // FIXME(ibiryukov): logging - // Output.log("Failed to find compilation database for " + Twine(File) + - // "\n"); + Logger.log("Failed to find compilation database for " + Twine(File) + "\n"); return nullptr; } Index: clangd/JSONRPCDispatcher.h =================================================================== --- clangd/JSONRPCDispatcher.h +++ clangd/JSONRPCDispatcher.h @@ -10,6 +10,7 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_JSONRPCDISPATCHER_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_JSONRPCDISPATCHER_H +#include "Logger.h" #include "clang/Basic/LLVM.h" #include "llvm/ADT/StringMap.h" #include "llvm/Support/YAMLParser.h" @@ -21,7 +22,7 @@ /// Encapsulates output and logs streams and provides thread-safe access to /// them. -class JSONOutput { +class JSONOutput : public Logger { public: JSONOutput(llvm::raw_ostream &Outs, llvm::raw_ostream &Logs) : Outs(Outs), Logs(Logs) {} @@ -30,7 +31,7 @@ void writeMessage(const Twine &Message); /// Write to the logging stream. - void log(const Twine &Message); + void log(const Twine &Message) override; private: llvm::raw_ostream &Outs; Index: clangd/Logger.h =================================================================== --- /dev/null +++ clangd/Logger.h @@ -0,0 +1,41 @@ +//===--- Logger.h - Logger interface for clangd ------------------*- 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_LOGGER_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_LOGGER_H + +#include "llvm/ADT/Twine.h" + +namespace clang { +namespace clangd { + +/// Interface to allow custom logging in clangd. +class Logger { +public: + virtual ~Logger() = default; + + /// Implementations of this method must be thread-safe. + virtual void log(const llvm::Twine &Message) = 0; +}; + +/// Logger implementation that ignores all messages. +class EmptyLogger : public Logger { +public: + static EmptyLogger &getInstance(); + + void log(const llvm::Twine &Message) override; + +private: + EmptyLogger() = default; +}; + +} // namespace clangd +} // namespace clang + +#endif Index: clangd/Logger.cpp =================================================================== --- /dev/null +++ clangd/Logger.cpp @@ -0,0 +1,19 @@ +//===--- Logger.cpp - Logger interface for clangd -------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Logger.h" + +using namespace clang::clangd; + +EmptyLogger &EmptyLogger::getInstance() { + static EmptyLogger Logger; + return Logger; +} + +void EmptyLogger::log(const llvm::Twine &Message) {} Index: unittests/clangd/ClangdTests.cpp =================================================================== --- unittests/clangd/ClangdTests.cpp +++ unittests/clangd/ClangdTests.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "ClangdServer.h" +#include "Logger.h" #include "clang/Basic/VirtualFileSystem.h" #include "clang/Config/config.h" #include "llvm/ADT/SmallVector.h" @@ -302,7 +303,8 @@ ErrorCheckingDiagConsumer DiagConsumer; MockCompilationDatabase CDB(/*AddFreestandingFlag=*/true); ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(), - /*SnippetCompletions=*/false); + /*SnippetCompletions=*/false, + EmptyLogger::getInstance()); for (const auto &FileWithContents : ExtraFiles) FS.Files[getVirtualTestFilePath(FileWithContents.first)] = FileWithContents.second; @@ -365,7 +367,7 @@ ErrorCheckingDiagConsumer DiagConsumer; MockCompilationDatabase CDB(/*AddFreestandingFlag=*/true); ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(), - /*SnippetCompletions=*/false); + /*SnippetCompletions=*/false, EmptyLogger::getInstance()); const auto SourceContents = R"cpp( #include "foo.h" @@ -410,7 +412,7 @@ MockCompilationDatabase CDB(/*AddFreestandingFlag=*/true); ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(), - /*SnippetCompletions=*/false); + /*SnippetCompletions=*/false, EmptyLogger::getInstance()); const auto SourceContents = R"cpp( #include "foo.h" @@ -457,7 +459,8 @@ MockCompilationDatabase CDB(/*AddFreestandingFlag=*/true); // Run ClangdServer synchronously. ClangdServer Server(CDB, DiagConsumer, FS, - /*AsyncThreadsCount=*/0, /*SnippetCompletions=*/false); + /*AsyncThreadsCount=*/0, /*SnippetCompletions=*/false, + EmptyLogger::getInstance()); auto FooCpp = getVirtualTestFilePath("foo.cpp"); const auto SourceContents = "int a;"; @@ -490,7 +493,8 @@ "-stdlib=libstdc++"}); // Run ClangdServer synchronously. ClangdServer Server(CDB, DiagConsumer, FS, - /*AsyncThreadsCount=*/0, /*SnippetCompletions=*/false); + /*AsyncThreadsCount=*/0, /*SnippetCompletions=*/false, + EmptyLogger::getInstance()); // Just a random gcc version string SmallString<8> Version("4.9.3"); @@ -538,7 +542,8 @@ ErrorCheckingDiagConsumer DiagConsumer; MockCompilationDatabase CDB(/*AddFreestandingFlag=*/true); ClangdServer Server(CDB, DiagConsumer, FS, - /*AsyncThreadsCount=*/0, /*SnippetCompletions=*/false); + /*AsyncThreadsCount=*/0, /*SnippetCompletions=*/false, + EmptyLogger::getInstance()); // No need to sync reparses, because reparses are performed on the calling // thread to true. @@ -597,7 +602,7 @@ MockCompilationDatabase CDB(/*AddFreestandingFlag=*/true); ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(), - /*SnippetCompletions=*/false); + /*SnippetCompletions=*/false, EmptyLogger::getInstance()); auto FooCpp = getVirtualTestFilePath("foo.cpp"); const auto SourceContents = R"cpp( @@ -745,7 +750,8 @@ { MockCompilationDatabase CDB(/*AddFreestandingFlag=*/true); ClangdServer Server(CDB, DiagConsumer, FS, getDefaultAsyncThreadsCount(), - /*SnippetCompletions=*/false); + /*SnippetCompletions=*/false, + EmptyLogger::getInstance()); // Prepare some random distributions for the test. std::random_device RandGen;