summaryrefslogtreecommitdiffstats
path: root/clangd/ClangdUnit.h
blob: 47902c544a87eb720d0f2560a17dcc78ad61fcf0 (plain)
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
//===--- 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;
};

/// 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);

private:
  CppFile(PathRef FileName, tooling::CompileCommand Command,
          bool StorePreamblesInMemory,
          std::shared_ptr<PCHContainerOperations> PCHs);

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;
};

/// 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