1//===--- Preamble.h - Reusing expensive parts of the AST ---------*- C++-*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// The vast majority of code in a typical translation unit is in the headers
10// included at the top of the file.
11//
12// The preamble optimization says that we can parse this code once, and reuse
13// the result multiple times. The preamble is invalidated by changes to the
14// code in the preamble region, to the compile command, or to files on disk.
15//
16// This is the most important optimization in clangd: it allows operations like
17// code-completion to have sub-second latency. It is supported by the
18// PrecompiledPreamble functionality in clang, which wraps the techniques used
19// by PCH files, modules etc into a convenient interface.
20//
21//===----------------------------------------------------------------------===//
22#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_PREAMBLE_H
23#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_PREAMBLE_H
24
25#include "CollectMacros.h"
26#include "Compiler.h"
27#include "Diagnostics.h"
28#include "FS.h"
29#include "Headers.h"
30#include "clang-include-cleaner/Record.h"
31#include "support/Path.h"
32#include "clang/Basic/SourceManager.h"
33#include "clang/Basic/TargetOptions.h"
34#include "clang/Frontend/CompilerInvocation.h"
35#include "clang/Frontend/PrecompiledPreamble.h"
36#include "clang/Lex/Lexer.h"
37#include "clang/Tooling/CompilationDatabase.h"
38#include "llvm/ADT/ArrayRef.h"
39#include "llvm/ADT/StringRef.h"
40
41#include <cstddef>
42#include <functional>
43#include <memory>
44#include <string>
45#include <utility>
46#include <vector>
47
48namespace clang {
49namespace clangd {
50
51/// The captured AST context.
52/// Keeps necessary structs for an ASTContext and Preprocessor alive.
53/// This enables consuming them after context that produced the AST is gone.
54/// (e.g. indexing a preamble ast on a separate thread). ASTContext stored
55/// inside is still not thread-safe.
56
57struct CapturedASTCtx {
58public:
59 CapturedASTCtx(CompilerInstance &Clang)
60 : Invocation(Clang.getInvocationPtr()),
61 Diagnostics(Clang.getDiagnosticsPtr()), Target(Clang.getTargetPtr()),
62 AuxTarget(Clang.getAuxTarget()), FileMgr(Clang.getFileManagerPtr()),
63 SourceMgr(Clang.getSourceManagerPtr()), PP(Clang.getPreprocessorPtr()),
64 Context(Clang.getASTContextPtr()) {}
65
66 CapturedASTCtx(const CapturedASTCtx &) = delete;
67 CapturedASTCtx &operator=(const CapturedASTCtx &) = delete;
68 CapturedASTCtx(CapturedASTCtx &&) = default;
69 CapturedASTCtx &operator=(CapturedASTCtx &&) = default;
70
71 ASTContext &getASTContext() { return *Context; }
72 Preprocessor &getPreprocessor() { return *PP; }
73 CompilerInvocation &getCompilerInvocation() { return *Invocation; }
74 FileManager &getFileManager() { return *FileMgr; }
75 void setStatCache(std::shared_ptr<PreambleFileStatusCache> StatCache) {
76 this->StatCache = StatCache;
77 }
78
79private:
80 std::shared_ptr<CompilerInvocation> Invocation;
81 IntrusiveRefCntPtr<DiagnosticsEngine> Diagnostics;
82 IntrusiveRefCntPtr<TargetInfo> Target;
83 IntrusiveRefCntPtr<TargetInfo> AuxTarget;
84 IntrusiveRefCntPtr<FileManager> FileMgr;
85 IntrusiveRefCntPtr<SourceManager> SourceMgr;
86 std::shared_ptr<Preprocessor> PP;
87 IntrusiveRefCntPtr<ASTContext> Context;
88 std::shared_ptr<PreambleFileStatusCache> StatCache;
89};
90
91/// The parsed preamble and associated data.
92///
93/// As we must avoid re-parsing the preamble, any information that can only
94/// be obtained during parsing must be eagerly captured and stored here.
95struct PreambleData {
96 PreambleData(PrecompiledPreamble Preamble) : Preamble(std::move(Preamble)) {}
97
98 // Version of the ParseInputs this preamble was built from.
99 std::string Version;
100 tooling::CompileCommand CompileCommand;
101 // Target options used when building the preamble. Changes in target can cause
102 // crashes when deserializing preamble, this enables consumers to use the
103 // same target (without reparsing CompileCommand).
104 std::shared_ptr<TargetOptions> TargetOpts = nullptr;
105 PrecompiledPreamble Preamble;
106 std::vector<Diag> Diags;
107 // Processes like code completions and go-to-definitions will need #include
108 // information, and their compile action skips preamble range.
109 IncludeStructure Includes;
110 // Captures #include-mapping information in #included headers.
111 std::shared_ptr<const include_cleaner::PragmaIncludes> Pragmas;
112 // Macros defined in the preamble section of the main file.
113 // Users care about headers vs main-file, not preamble vs non-preamble.
114 // These should be treated as main-file entities e.g. for code completion.
115 MainFileMacros Macros;
116 // Pragma marks defined in the preamble section of the main file.
117 std::vector<PragmaMark> Marks;
118 // Cache of FS operations performed when building the preamble.
119 // When reusing a preamble, this cache can be consumed to save IO.
120 std::shared_ptr<PreambleFileStatusCache> StatCache;
121 // Whether there was a (possibly-incomplete) include-guard on the main file.
122 // We need to propagate this information "by hand" to subsequent parses.
123 bool MainIsIncludeGuarded = false;
124};
125
126using PreambleParsedCallback =
127 std::function<void(CapturedASTCtx ASTCtx,
128 std::shared_ptr<const include_cleaner::PragmaIncludes>)>;
129
130/// Timings and statistics from the premble build. Unlike PreambleData, these
131/// do not need to be stored for later, but can be useful for logging, metrics,
132/// etc.
133struct PreambleBuildStats {
134 /// Total wall time it took to build preamble, in seconds.
135 double TotalBuildTime;
136 /// Time spent in filesystem operations during the build, in seconds.
137 double FileSystemTime;
138
139 /// Estimate of the memory used while building the preamble.
140 /// This memory has been released when buildPreamble returns.
141 /// For example, this includes the size of the in-memory AST (ASTContext).
142 size_t BuildSize;
143 /// The serialized size of the preamble.
144 /// This storage is needed while the preamble is used (but may be on disk).
145 size_t SerializedSize;
146};
147
148/// Build a preamble for the new inputs unless an old one can be reused.
149/// If \p PreambleCallback is set, it will be run on top of the AST while
150/// building the preamble.
151/// If Stats is not non-null, build statistics will be exported there.
152std::shared_ptr<const PreambleData>
153buildPreamble(PathRef FileName, CompilerInvocation CI,
154 const ParseInputs &Inputs, bool StoreInMemory,
155 PreambleParsedCallback PreambleCallback,
156 PreambleBuildStats *Stats = nullptr);
157
158/// Returns true if \p Preamble is reusable for \p Inputs. Note that it will
159/// return true when some missing headers are now available.
160/// FIXME: Should return more information about the delta between \p Preamble
161/// and \p Inputs, e.g. new headers.
162bool isPreambleCompatible(const PreambleData &Preamble,
163 const ParseInputs &Inputs, PathRef FileName,
164 const CompilerInvocation &CI);
165
166/// Stores information required to parse a TU using a (possibly stale) Baseline
167/// preamble. Later on this information can be injected into the main file by
168/// updating compiler invocation with \c apply. This injected section
169/// approximately reflects additions to the preamble in Modified contents, e.g.
170/// new include directives.
171class PreamblePatch {
172public:
173 enum class PatchType { MacroDirectives, All };
174 /// \p Preamble is used verbatim.
175 static PreamblePatch unmodified(const PreambleData &Preamble);
176 /// Builds a patch that contains new PP directives introduced to the preamble
177 /// section of \p Modified compared to \p Baseline.
178 /// FIXME: This only handles include directives, we should at least handle
179 /// define/undef.
180 static PreamblePatch createFullPatch(llvm::StringRef FileName,
181 const ParseInputs &Modified,
182 const PreambleData &Baseline);
183 static PreamblePatch createMacroPatch(llvm::StringRef FileName,
184 const ParseInputs &Modified,
185 const PreambleData &Baseline);
186 /// Returns the FileEntry for the preamble patch of MainFilePath in SM, if
187 /// any.
188 static OptionalFileEntryRef getPatchEntry(llvm::StringRef MainFilePath,
189 const SourceManager &SM);
190
191 /// Adjusts CI (which compiles the modified inputs) to be used with the
192 /// baseline preamble. This is done by inserting an artificial include to the
193 /// \p CI that contains new directives calculated in create.
194 void apply(CompilerInvocation &CI) const;
195
196 /// Returns #include directives from the \c Modified preamble that were
197 /// resolved using the \c Baseline preamble. This covers the new locations of
198 /// inclusions that were moved around, but not inclusions of new files. Those
199 /// will be recorded when parsing the main file: the includes in the injected
200 /// section will be resolved back to their spelled positions in the main file
201 /// using the presumed-location mechanism.
202 std::vector<Inclusion> preambleIncludes() const;
203
204 /// Returns preamble bounds for the Modified.
205 PreambleBounds modifiedBounds() const { return ModifiedBounds; }
206
207 /// Returns textual patch contents.
208 llvm::StringRef text() const { return PatchContents; }
209
210 /// Returns diag locations for Modified contents.
211 llvm::ArrayRef<Diag> patchedDiags() const { return PatchedDiags; }
212
213 static constexpr llvm::StringLiteral HeaderName = "__preamble_patch__.h";
214
215 llvm::ArrayRef<PragmaMark> marks() const;
216 const MainFileMacros &mainFileMacros() const;
217
218private:
219 static PreamblePatch create(llvm::StringRef FileName,
220 const ParseInputs &Modified,
221 const PreambleData &Baseline,
222 PatchType PatchType);
223
224 PreamblePatch() = default;
225 std::string PatchContents;
226 std::string PatchFileName;
227 // Includes that are present in both Baseline and Modified. Used for
228 // patching includes of baseline preamble.
229 std::vector<Inclusion> PreambleIncludes;
230 // Diags that were attached to a line preserved in Modified contents.
231 std::vector<Diag> PatchedDiags;
232 PreambleBounds ModifiedBounds = {0, false};
233 const PreambleData *Baseline = nullptr;
234 std::vector<PragmaMark> PatchedMarks;
235 MainFileMacros PatchedMacros;
236};
237
238} // namespace clangd
239} // namespace clang
240
241#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_PREAMBLE_H
242

source code of clang-tools-extra/clangd/Preamble.h