1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
|
//===--- 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 <atomic>
#include <future>
#include <memory>
#include <mutex>
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<TextEdit, 1> FixIts;
};
// Stores Preamble and associated data.
struct PreambleData {
PreambleData(PrecompiledPreamble Preamble,
std::vector<serialization::DeclID> TopLevelDeclIDs,
std::vector<DiagWithFixIts> Diags);
PrecompiledPreamble Preamble;
std::vector<serialization::DeclID> TopLevelDeclIDs;
std::vector<DiagWithFixIts> 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<ParsedAST>
Build(const Context &Ctx, std::unique_ptr<clang::CompilerInvocation> CI,
std::shared_ptr<const PreambleData> Preamble,
std::unique_ptr<llvm::MemoryBuffer> Buffer,
std::shared_ptr<PCHContainerOperations> PCHs,
IntrusiveRefCntPtr<vfs::FileSystem> VFS);
ParsedAST(ParsedAST &&Other);
ParsedAST &operator=(ParsedAST &&Other);
~ParsedAST();
ASTContext &getASTContext();
const ASTContext &getASTContext() const;
Preprocessor &getPreprocessor();
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<const Decl *> getTopLevelDecls();
const std::vector<DiagWithFixIts> &getDiagnostics() const;
private:
ParsedAST(std::shared_ptr<const PreambleData> Preamble,
std::unique_ptr<CompilerInstance> Clang,
std::unique_ptr<FrontendAction> Action,
std::vector<const Decl *> TopLevelDecls,
std::vector<DiagWithFixIts> 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<const PreambleData> 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<CompilerInstance> Clang;
std::unique_ptr<FrontendAction> Action;
// Data, stored after parsing.
std::vector<DiagWithFixIts> Diags;
std::vector<const Decl *> TopLevelDecls;
bool PreambleDeclsDeserialized;
};
// Provides thread-safe access to ParsedAST.
class ParsedASTWrapper {
public:
ParsedASTWrapper(ParsedASTWrapper &&Wrapper);
ParsedASTWrapper(llvm::Optional<ParsedAST> AST);
/// Runs \p F on wrapped ParsedAST under lock. Ensures it is not accessed
/// concurrently.
template <class Func> void runUnderLock(Func F) const {
std::lock_guard<std::mutex> 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<ParsedAST> AST;
};
using ASTParsedCallback =
std::function<void(const Context &Ctx, PathRef Path, ParsedAST *)>;
/// 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<CppFile> {
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<CppFile>
Create(PathRef FileName, tooling::CompileCommand Command,
bool StorePreamblesInMemory,
std::shared_ptr<PCHContainerOperations> PCHs,
ASTParsedCallback ASTCallback);
private:
CppFile(PathRef FileName, tooling::CompileCommand Command,
bool StorePreamblesInMemory,
std::shared_ptr<PCHContainerOperations> 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<void()> 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<std::vector<DiagWithFixIts>>
rebuild(const Context &Ctx, StringRef NewContents,
IntrusiveRefCntPtr<vfs::FileSystem> 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<llvm::Optional<std::vector<DiagWithFixIts>>(const Context &)>
deferRebuild(StringRef NewContents, IntrusiveRefCntPtr<vfs::FileSystem> 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<std::shared_ptr<const PreambleData>> getPreamble() const;
/// Return some preamble for a file. It might be stale, but won't wait for
/// rebuild to finish.
std::shared_ptr<const PreambleData> 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<std::shared_ptr<ParsedASTWrapper>> 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<std::shared_ptr<ParsedASTWrapper>> ASTPromise;
std::shared_future<std::shared_ptr<ParsedASTWrapper>> ASTFuture;
/// Promise and future for the latests Preamble. Fulfilled during rebuild.
std::promise<std::shared_ptr<const PreambleData>> PreamblePromise;
std::shared_future<std::shared_ptr<const PreambleData>> PreambleFuture;
/// Latest preamble that was built. May be stale, but always available without
/// waiting for rebuild to finish.
std::shared_ptr<const PreambleData> LatestAvailablePreamble;
/// Utility class, required by clang.
std::shared_ptr<PCHContainerOperations> 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
|