//===--- 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 #include #include #include 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 FixIts; }; // Stores Preamble and associated data. struct PreambleData { PreambleData(PrecompiledPreamble Preamble, std::vector TopLevelDeclIDs, std::vector Diags); PrecompiledPreamble Preamble; std::vector TopLevelDeclIDs; std::vector 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 Build(const Context &Ctx, std::unique_ptr CI, std::shared_ptr Preamble, std::unique_ptr Buffer, std::shared_ptr PCHs, IntrusiveRefCntPtr VFS); ParsedAST(ParsedAST &&Other); ParsedAST &operator=(ParsedAST &&Other); ~ParsedAST(); ASTContext &getASTContext(); const ASTContext &getASTContext() const; Preprocessor &getPreprocessor(); std::shared_ptr getPreprocessorPtr(); 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 getTopLevelDecls(); const std::vector &getDiagnostics() const; private: ParsedAST(std::shared_ptr Preamble, std::unique_ptr Clang, std::unique_ptr Action, std::vector TopLevelDecls, std::vector 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 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 Clang; std::unique_ptr Action; // Data, stored after parsing. std::vector Diags; std::vector TopLevelDecls; bool PreambleDeclsDeserialized; }; // Provides thread-safe access to ParsedAST. class ParsedASTWrapper { public: ParsedASTWrapper(ParsedASTWrapper &&Wrapper); ParsedASTWrapper(llvm::Optional AST); /// Runs \p F on wrapped ParsedAST under lock. Ensures it is not accessed /// concurrently. template void runUnderLock(Func F) const { std::lock_guard 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 AST; }; using ASTParsedCallback = std::function; /// 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 { 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 Create(PathRef FileName, tooling::CompileCommand Command, bool StorePreamblesInMemory, std::shared_ptr PCHs, ASTParsedCallback ASTCallback); private: CppFile(PathRef FileName, tooling::CompileCommand Command, bool StorePreamblesInMemory, std::shared_ptr 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 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> rebuild(const Context &Ctx, StringRef NewContents, IntrusiveRefCntPtr 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>(const Context &)> deferRebuild(StringRef NewContents, IntrusiveRefCntPtr 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> getPreamble() const; /// Return some preamble for a file. It might be stale, but won't wait for /// rebuild to finish. std::shared_ptr 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> 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> ASTPromise; std::shared_future> ASTFuture; /// Promise and future for the latests Preamble. Fulfilled during rebuild. std::promise> PreamblePromise; std::shared_future> PreambleFuture; /// Latest preamble that was built. May be stale, but always available without /// waiting for rebuild to finish. std::shared_ptr LatestAvailablePreamble; /// Utility class, required by clang. std::shared_ptr 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