blob: 2768bd1ec6a08594ed6520d50c17588472e8346c [file] [log] [blame]
Sam McCallcf3a5852019-09-04 07:35:00 +00001//===--- Preamble.cpp - Reusing expensive parts of the AST ----------------===//
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#include "Preamble.h"
Kadir Cetinkayaecd3e672020-03-11 16:34:01 +010010#include "Compiler.h"
Kadir Cetinkaya2214b902020-04-02 10:53:23 +020011#include "Headers.h"
Sam McCallad97ccf2020-04-28 17:49:17 +020012#include "support/Logger.h"
13#include "support/Trace.h"
Kadir Cetinkaya2214b902020-04-02 10:53:23 +020014#include "clang/Basic/Diagnostic.h"
15#include "clang/Basic/LangOptions.h"
Sam McCallcf3a5852019-09-04 07:35:00 +000016#include "clang/Basic/SourceLocation.h"
Kadir Cetinkaya2214b902020-04-02 10:53:23 +020017#include "clang/Basic/TokenKinds.h"
18#include "clang/Frontend/CompilerInvocation.h"
19#include "clang/Frontend/FrontendActions.h"
20#include "clang/Lex/Lexer.h"
Sam McCallcf3a5852019-09-04 07:35:00 +000021#include "clang/Lex/PPCallbacks.h"
Kadir Cetinkaya2214b902020-04-02 10:53:23 +020022#include "clang/Lex/Preprocessor.h"
Sam McCallcf3a5852019-09-04 07:35:00 +000023#include "clang/Lex/PreprocessorOptions.h"
Kadir Cetinkaya2214b902020-04-02 10:53:23 +020024#include "clang/Tooling/CompilationDatabase.h"
25#include "llvm/ADT/ArrayRef.h"
26#include "llvm/ADT/IntrusiveRefCntPtr.h"
27#include "llvm/ADT/STLExtras.h"
28#include "llvm/ADT/SmallString.h"
29#include "llvm/ADT/StringRef.h"
30#include "llvm/ADT/StringSet.h"
31#include "llvm/Support/Error.h"
32#include "llvm/Support/ErrorHandling.h"
33#include "llvm/Support/FormatVariadic.h"
34#include "llvm/Support/MemoryBuffer.h"
35#include "llvm/Support/Path.h"
36#include "llvm/Support/VirtualFileSystem.h"
37#include "llvm/Support/raw_ostream.h"
38#include <iterator>
39#include <memory>
40#include <string>
41#include <system_error>
42#include <utility>
43#include <vector>
Sam McCallcf3a5852019-09-04 07:35:00 +000044
45namespace clang {
46namespace clangd {
47namespace {
48
49bool compileCommandsAreEqual(const tooling::CompileCommand &LHS,
50 const tooling::CompileCommand &RHS) {
51 // We don't check for Output, it should not matter to clangd.
52 return LHS.Directory == RHS.Directory && LHS.Filename == RHS.Filename &&
53 llvm::makeArrayRef(LHS.CommandLine).equals(RHS.CommandLine);
54}
55
Sam McCallcf3a5852019-09-04 07:35:00 +000056class CppFilePreambleCallbacks : public PreambleCallbacks {
57public:
58 CppFilePreambleCallbacks(PathRef File, PreambleParsedCallback ParsedCallback)
Haojian Wu7e3c74b2019-09-24 11:14:06 +000059 : File(File), ParsedCallback(ParsedCallback) {}
Sam McCallcf3a5852019-09-04 07:35:00 +000060
61 IncludeStructure takeIncludes() { return std::move(Includes); }
62
Haojian Wu7e3c74b2019-09-24 11:14:06 +000063 MainFileMacros takeMacros() { return std::move(Macros); }
Sam McCallcf3a5852019-09-04 07:35:00 +000064
65 CanonicalIncludes takeCanonicalIncludes() { return std::move(CanonIncludes); }
66
67 void AfterExecute(CompilerInstance &CI) override {
68 if (!ParsedCallback)
69 return;
70 trace::Span Tracer("Running PreambleCallback");
71 ParsedCallback(CI.getASTContext(), CI.getPreprocessorPtr(), CanonIncludes);
72 }
73
74 void BeforeExecute(CompilerInstance &CI) override {
Ilya Biryukov8b767092019-09-09 15:32:51 +000075 CanonIncludes.addSystemHeadersMapping(CI.getLangOpts());
Haojian Wu7e3c74b2019-09-24 11:14:06 +000076 LangOpts = &CI.getLangOpts();
Sam McCallcf3a5852019-09-04 07:35:00 +000077 SourceMgr = &CI.getSourceManager();
78 }
79
80 std::unique_ptr<PPCallbacks> createPPCallbacks() override {
Haojian Wu7e3c74b2019-09-24 11:14:06 +000081 assert(SourceMgr && LangOpts &&
82 "SourceMgr and LangOpts must be set at this point");
83
Sam McCallcf3a5852019-09-04 07:35:00 +000084 return std::make_unique<PPChainedCallbacks>(
85 collectIncludeStructureCallback(*SourceMgr, &Includes),
Kadir Cetinkaya37550392020-03-01 16:05:12 +010086 std::make_unique<CollectMainFileMacros>(*SourceMgr, Macros));
Sam McCallcf3a5852019-09-04 07:35:00 +000087 }
88
89 CommentHandler *getCommentHandler() override {
90 IWYUHandler = collectIWYUHeaderMaps(&CanonIncludes);
91 return IWYUHandler.get();
92 }
93
94private:
95 PathRef File;
96 PreambleParsedCallback ParsedCallback;
97 IncludeStructure Includes;
98 CanonicalIncludes CanonIncludes;
Haojian Wu7e3c74b2019-09-24 11:14:06 +000099 MainFileMacros Macros;
Sam McCallcf3a5852019-09-04 07:35:00 +0000100 std::unique_ptr<CommentHandler> IWYUHandler = nullptr;
Haojian Wu7e3c74b2019-09-24 11:14:06 +0000101 const clang::LangOptions *LangOpts = nullptr;
102 const SourceManager *SourceMgr = nullptr;
Sam McCallcf3a5852019-09-04 07:35:00 +0000103};
104
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200105// Runs preprocessor over preamble section.
106class PreambleOnlyAction : public PreprocessorFrontendAction {
107protected:
108 void ExecuteAction() override {
109 Preprocessor &PP = getCompilerInstance().getPreprocessor();
110 auto &SM = PP.getSourceManager();
111 PP.EnterMainSourceFile();
112 auto Bounds = ComputePreambleBounds(getCompilerInstance().getLangOpts(),
113 SM.getBuffer(SM.getMainFileID()), 0);
114 Token Tok;
115 do {
116 PP.Lex(Tok);
117 assert(SM.isInMainFile(Tok.getLocation()));
118 } while (Tok.isNot(tok::eof) &&
119 SM.getDecomposedLoc(Tok.getLocation()).second < Bounds.Size);
120 }
121};
122
123/// Gets the includes in the preamble section of the file by running
124/// preprocessor over \p Contents. Returned includes do not contain resolved
125/// paths. \p VFS and \p Cmd is used to build the compiler invocation, which
126/// might stat/read files.
127llvm::Expected<std::vector<Inclusion>>
128scanPreambleIncludes(llvm::StringRef Contents,
129 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
130 const tooling::CompileCommand &Cmd) {
131 // Build and run Preprocessor over the preamble.
132 ParseInputs PI;
133 PI.Contents = Contents.str();
134 PI.FS = std::move(VFS);
135 PI.CompileCommand = Cmd;
136 IgnoringDiagConsumer IgnoreDiags;
137 auto CI = buildCompilerInvocation(PI, IgnoreDiags);
138 if (!CI)
139 return llvm::createStringError(llvm::inconvertibleErrorCode(),
140 "failed to create compiler invocation");
141 CI->getDiagnosticOpts().IgnoreWarnings = true;
142 auto ContentsBuffer = llvm::MemoryBuffer::getMemBuffer(Contents);
143 auto Clang = prepareCompilerInstance(
144 std::move(CI), nullptr, std::move(ContentsBuffer),
145 // Provide an empty FS to prevent preprocessor from performing IO. This
146 // also implies missing resolved paths for includes.
147 new llvm::vfs::InMemoryFileSystem, IgnoreDiags);
148 if (Clang->getFrontendOpts().Inputs.empty())
149 return llvm::createStringError(llvm::inconvertibleErrorCode(),
150 "compiler instance had no inputs");
151 // We are only interested in main file includes.
152 Clang->getPreprocessorOpts().SingleFileParseMode = true;
153 PreambleOnlyAction Action;
154 if (!Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0]))
155 return llvm::createStringError(llvm::inconvertibleErrorCode(),
156 "failed BeginSourceFile");
157 Preprocessor &PP = Clang->getPreprocessor();
158 IncludeStructure Includes;
159 PP.addPPCallbacks(
160 collectIncludeStructureCallback(Clang->getSourceManager(), &Includes));
161 if (llvm::Error Err = Action.Execute())
162 return std::move(Err);
163 Action.EndSourceFile();
164 return Includes.MainFileIncludes;
165}
166
167const char *spellingForIncDirective(tok::PPKeywordKind IncludeDirective) {
168 switch (IncludeDirective) {
169 case tok::pp_include:
170 return "include";
171 case tok::pp_import:
172 return "import";
173 case tok::pp_include_next:
174 return "include_next";
175 default:
176 break;
177 }
178 llvm_unreachable("not an include directive");
179}
Sam McCallcf3a5852019-09-04 07:35:00 +0000180} // namespace
181
Kadir Cetinkayaecd3e672020-03-11 16:34:01 +0100182PreambleData::PreambleData(const ParseInputs &Inputs,
Sam McCall2cd33e62020-03-04 00:33:29 +0100183 PrecompiledPreamble Preamble,
Sam McCallcf3a5852019-09-04 07:35:00 +0000184 std::vector<Diag> Diags, IncludeStructure Includes,
Haojian Wu7e3c74b2019-09-24 11:14:06 +0000185 MainFileMacros Macros,
Sam McCallcf3a5852019-09-04 07:35:00 +0000186 std::unique_ptr<PreambleFileStatusCache> StatCache,
187 CanonicalIncludes CanonIncludes)
Kadir Cetinkayaecd3e672020-03-11 16:34:01 +0100188 : Version(Inputs.Version), CompileCommand(Inputs.CompileCommand),
189 Preamble(std::move(Preamble)), Diags(std::move(Diags)),
Haojian Wu7e3c74b2019-09-24 11:14:06 +0000190 Includes(std::move(Includes)), Macros(std::move(Macros)),
Sam McCallcf3a5852019-09-04 07:35:00 +0000191 StatCache(std::move(StatCache)), CanonIncludes(std::move(CanonIncludes)) {
192}
193
194std::shared_ptr<const PreambleData>
Kadir Cetinkaya276a95b2020-03-13 11:52:19 +0100195buildPreamble(PathRef FileName, CompilerInvocation CI,
Sam McCallcf3a5852019-09-04 07:35:00 +0000196 const ParseInputs &Inputs, bool StoreInMemory,
197 PreambleParsedCallback PreambleCallback) {
198 // Note that we don't need to copy the input contents, preamble can live
199 // without those.
200 auto ContentsBuffer =
201 llvm::MemoryBuffer::getMemBuffer(Inputs.Contents, FileName);
202 auto Bounds =
203 ComputePreambleBounds(*CI.getLangOpts(), ContentsBuffer.get(), 0);
204
Sam McCallcf3a5852019-09-04 07:35:00 +0000205 trace::Span Tracer("BuildPreamble");
206 SPAN_ATTACH(Tracer, "File", FileName);
207 StoreDiags PreambleDiagnostics;
208 llvm::IntrusiveRefCntPtr<DiagnosticsEngine> PreambleDiagsEngine =
209 CompilerInstance::createDiagnostics(&CI.getDiagnosticOpts(),
210 &PreambleDiagnostics, false);
211
212 // Skip function bodies when building the preamble to speed up building
213 // the preamble and make it smaller.
214 assert(!CI.getFrontendOpts().SkipFunctionBodies);
215 CI.getFrontendOpts().SkipFunctionBodies = true;
216 // We don't want to write comment locations into PCH. They are racy and slow
217 // to read back. We rely on dynamic index for the comments instead.
218 CI.getPreprocessorOpts().WriteCommentListToPCH = false;
219
Haojian Wu72439b62020-03-31 16:09:49 +0200220 // Recovery expression currently only works for C++.
221 if (CI.getLangOpts()->CPlusPlus)
222 CI.getLangOpts()->RecoveryAST = Inputs.Opts.BuildRecoveryAST;
223
Sam McCallcf3a5852019-09-04 07:35:00 +0000224 CppFilePreambleCallbacks SerializedDeclsCollector(FileName, PreambleCallback);
225 if (Inputs.FS->setCurrentWorkingDirectory(Inputs.CompileCommand.Directory)) {
226 log("Couldn't set working directory when building the preamble.");
227 // We proceed anyway, our lit-tests rely on results for non-existing working
228 // dirs.
229 }
230
231 llvm::SmallString<32> AbsFileName(FileName);
232 Inputs.FS->makeAbsolute(AbsFileName);
233 auto StatCache = std::make_unique<PreambleFileStatusCache>(AbsFileName);
234 auto BuiltPreamble = PrecompiledPreamble::Build(
235 CI, ContentsBuffer.get(), Bounds, *PreambleDiagsEngine,
236 StatCache->getProducingFS(Inputs.FS),
237 std::make_shared<PCHContainerOperations>(), StoreInMemory,
238 SerializedDeclsCollector);
239
240 // When building the AST for the main file, we do want the function
241 // bodies.
242 CI.getFrontendOpts().SkipFunctionBodies = false;
243
244 if (BuiltPreamble) {
Sam McCall2cd33e62020-03-04 00:33:29 +0100245 vlog("Built preamble of size {0} for file {1} version {2}",
246 BuiltPreamble->getSize(), FileName, Inputs.Version);
Sam McCallcf3a5852019-09-04 07:35:00 +0000247 std::vector<Diag> Diags = PreambleDiagnostics.take();
248 return std::make_shared<PreambleData>(
Kadir Cetinkayaecd3e672020-03-11 16:34:01 +0100249 Inputs, std::move(*BuiltPreamble), std::move(Diags),
Sam McCallcf3a5852019-09-04 07:35:00 +0000250 SerializedDeclsCollector.takeIncludes(),
Haojian Wu7e3c74b2019-09-24 11:14:06 +0000251 SerializedDeclsCollector.takeMacros(), std::move(StatCache),
Sam McCallcf3a5852019-09-04 07:35:00 +0000252 SerializedDeclsCollector.takeCanonicalIncludes());
253 } else {
Adam Czachorowski55b92dc2020-03-19 15:09:28 +0100254 elog("Could not build a preamble for file {0} version {1}", FileName,
Sam McCall2cd33e62020-03-04 00:33:29 +0100255 Inputs.Version);
Sam McCallcf3a5852019-09-04 07:35:00 +0000256 return nullptr;
257 }
258}
259
Kadir Cetinkayac31367e2020-03-15 21:43:00 +0100260bool isPreambleCompatible(const PreambleData &Preamble,
261 const ParseInputs &Inputs, PathRef FileName,
262 const CompilerInvocation &CI) {
263 auto ContentsBuffer =
264 llvm::MemoryBuffer::getMemBuffer(Inputs.Contents, FileName);
265 auto Bounds =
266 ComputePreambleBounds(*CI.getLangOpts(), ContentsBuffer.get(), 0);
267 return compileCommandsAreEqual(Inputs.CompileCommand,
268 Preamble.CompileCommand) &&
269 Preamble.Preamble.CanReuse(CI, ContentsBuffer.get(), Bounds,
270 Inputs.FS.get());
271}
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200272
273PreamblePatch PreamblePatch::create(llvm::StringRef FileName,
274 const ParseInputs &Modified,
275 const PreambleData &Baseline) {
276 // First scan the include directives in Baseline and Modified. These will be
277 // used to figure out newly added directives in Modified. Scanning can fail,
278 // the code just bails out and creates an empty patch in such cases, as:
279 // - If scanning for Baseline fails, no knowledge of existing includes hence
280 // patch will contain all the includes in Modified. Leading to rebuild of
281 // whole preamble, which is terribly slow.
282 // - If scanning for Modified fails, cannot figure out newly added ones so
283 // there's nothing to do but generate an empty patch.
284 auto BaselineIncludes = scanPreambleIncludes(
285 // Contents needs to be null-terminated.
286 Baseline.Preamble.getContents().str(),
287 Baseline.StatCache->getConsumingFS(Modified.FS), Modified.CompileCommand);
288 if (!BaselineIncludes) {
289 elog("Failed to scan includes for baseline of {0}: {1}", FileName,
290 BaselineIncludes.takeError());
291 return {};
292 }
293 auto ModifiedIncludes = scanPreambleIncludes(
294 Modified.Contents, Baseline.StatCache->getConsumingFS(Modified.FS),
295 Modified.CompileCommand);
296 if (!ModifiedIncludes) {
297 elog("Failed to scan includes for modified contents of {0}: {1}", FileName,
298 ModifiedIncludes.takeError());
299 return {};
300 }
301
302 PreamblePatch PP;
303 // This shouldn't coincide with any real file name.
304 llvm::SmallString<128> PatchName;
305 llvm::sys::path::append(PatchName, llvm::sys::path::parent_path(FileName),
306 "__preamble_patch__.h");
307 PP.PatchFileName = PatchName.str().str();
308
309 // We are only interested in newly added includes, record the ones in Baseline
310 // for exclusion.
311 llvm::DenseSet<std::pair<tok::PPKeywordKind, llvm::StringRef>>
312 ExistingIncludes;
313 for (const auto &Inc : *BaselineIncludes)
314 ExistingIncludes.insert({Inc.Directive, Inc.Written});
315 // Calculate extra includes that needs to be inserted.
316 llvm::raw_string_ostream Patch(PP.PatchContents);
317 for (const auto &Inc : *ModifiedIncludes) {
318 if (ExistingIncludes.count({Inc.Directive, Inc.Written}))
319 continue;
320 Patch << llvm::formatv("#{0} {1}\n", spellingForIncDirective(Inc.Directive),
321 Inc.Written);
322 }
323 Patch.flush();
324
325 // FIXME: Handle more directives, e.g. define/undef.
326 return PP;
327}
328
329void PreamblePatch::apply(CompilerInvocation &CI) const {
330 // No need to map an empty file.
331 if (PatchContents.empty())
332 return;
333 auto &PPOpts = CI.getPreprocessorOpts();
334 auto PatchBuffer =
335 // we copy here to ensure contents are still valid if CI outlives the
336 // PreamblePatch.
337 llvm::MemoryBuffer::getMemBufferCopy(PatchContents, PatchFileName);
338 // CI will take care of the lifetime of the buffer.
339 PPOpts.addRemappedFile(PatchFileName, PatchBuffer.release());
340 // The patch will be parsed after loading the preamble ast and before parsing
341 // the main file.
342 PPOpts.Includes.push_back(PatchFileName);
343}
344
Sam McCallcf3a5852019-09-04 07:35:00 +0000345} // namespace clangd
346} // namespace clang