blob: 86fa7905a61e5f49d3af516cd234cf6f27e95dd6 [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"
10#include "Logger.h"
11#include "Trace.h"
12#include "clang/Basic/SourceLocation.h"
13#include "clang/Lex/PPCallbacks.h"
14#include "clang/Lex/PreprocessorOptions.h"
15
16namespace clang {
17namespace clangd {
18namespace {
19
20bool compileCommandsAreEqual(const tooling::CompileCommand &LHS,
21 const tooling::CompileCommand &RHS) {
22 // We don't check for Output, it should not matter to clangd.
23 return LHS.Directory == RHS.Directory && LHS.Filename == RHS.Filename &&
24 llvm::makeArrayRef(LHS.CommandLine).equals(RHS.CommandLine);
25}
26
Sam McCallcf3a5852019-09-04 07:35:00 +000027class CppFilePreambleCallbacks : public PreambleCallbacks {
28public:
29 CppFilePreambleCallbacks(PathRef File, PreambleParsedCallback ParsedCallback)
Haojian Wu7e3c74b2019-09-24 11:14:06 +000030 : File(File), ParsedCallback(ParsedCallback) {}
Sam McCallcf3a5852019-09-04 07:35:00 +000031
32 IncludeStructure takeIncludes() { return std::move(Includes); }
33
Haojian Wu7e3c74b2019-09-24 11:14:06 +000034 MainFileMacros takeMacros() { return std::move(Macros); }
Sam McCallcf3a5852019-09-04 07:35:00 +000035
36 CanonicalIncludes takeCanonicalIncludes() { return std::move(CanonIncludes); }
37
38 void AfterExecute(CompilerInstance &CI) override {
39 if (!ParsedCallback)
40 return;
41 trace::Span Tracer("Running PreambleCallback");
42 ParsedCallback(CI.getASTContext(), CI.getPreprocessorPtr(), CanonIncludes);
43 }
44
45 void BeforeExecute(CompilerInstance &CI) override {
Ilya Biryukov8b767092019-09-09 15:32:51 +000046 CanonIncludes.addSystemHeadersMapping(CI.getLangOpts());
Haojian Wu7e3c74b2019-09-24 11:14:06 +000047 LangOpts = &CI.getLangOpts();
Sam McCallcf3a5852019-09-04 07:35:00 +000048 SourceMgr = &CI.getSourceManager();
49 }
50
51 std::unique_ptr<PPCallbacks> createPPCallbacks() override {
Haojian Wu7e3c74b2019-09-24 11:14:06 +000052 assert(SourceMgr && LangOpts &&
53 "SourceMgr and LangOpts must be set at this point");
54
Sam McCallcf3a5852019-09-04 07:35:00 +000055 return std::make_unique<PPChainedCallbacks>(
56 collectIncludeStructureCallback(*SourceMgr, &Includes),
Kadir Cetinkaya37550392020-03-01 16:05:12 +010057 std::make_unique<CollectMainFileMacros>(*SourceMgr, Macros));
Sam McCallcf3a5852019-09-04 07:35:00 +000058 }
59
60 CommentHandler *getCommentHandler() override {
61 IWYUHandler = collectIWYUHeaderMaps(&CanonIncludes);
62 return IWYUHandler.get();
63 }
64
65private:
66 PathRef File;
67 PreambleParsedCallback ParsedCallback;
68 IncludeStructure Includes;
69 CanonicalIncludes CanonIncludes;
Haojian Wu7e3c74b2019-09-24 11:14:06 +000070 MainFileMacros Macros;
Sam McCallcf3a5852019-09-04 07:35:00 +000071 std::unique_ptr<CommentHandler> IWYUHandler = nullptr;
Haojian Wu7e3c74b2019-09-24 11:14:06 +000072 const clang::LangOptions *LangOpts = nullptr;
73 const SourceManager *SourceMgr = nullptr;
Sam McCallcf3a5852019-09-04 07:35:00 +000074};
75
76} // namespace
77
Sam McCall2cd33e62020-03-04 00:33:29 +010078PreambleData::PreambleData(llvm::StringRef Version,
79 PrecompiledPreamble Preamble,
Sam McCallcf3a5852019-09-04 07:35:00 +000080 std::vector<Diag> Diags, IncludeStructure Includes,
Haojian Wu7e3c74b2019-09-24 11:14:06 +000081 MainFileMacros Macros,
Sam McCallcf3a5852019-09-04 07:35:00 +000082 std::unique_ptr<PreambleFileStatusCache> StatCache,
83 CanonicalIncludes CanonIncludes)
Sam McCall2cd33e62020-03-04 00:33:29 +010084 : Version(Version), Preamble(std::move(Preamble)), Diags(std::move(Diags)),
Haojian Wu7e3c74b2019-09-24 11:14:06 +000085 Includes(std::move(Includes)), Macros(std::move(Macros)),
Sam McCallcf3a5852019-09-04 07:35:00 +000086 StatCache(std::move(StatCache)), CanonIncludes(std::move(CanonIncludes)) {
87}
88
89std::shared_ptr<const PreambleData>
90buildPreamble(PathRef FileName, CompilerInvocation &CI,
91 std::shared_ptr<const PreambleData> OldPreamble,
92 const tooling::CompileCommand &OldCompileCommand,
93 const ParseInputs &Inputs, bool StoreInMemory,
94 PreambleParsedCallback PreambleCallback) {
95 // Note that we don't need to copy the input contents, preamble can live
96 // without those.
97 auto ContentsBuffer =
98 llvm::MemoryBuffer::getMemBuffer(Inputs.Contents, FileName);
99 auto Bounds =
100 ComputePreambleBounds(*CI.getLangOpts(), ContentsBuffer.get(), 0);
101
102 if (OldPreamble &&
103 compileCommandsAreEqual(Inputs.CompileCommand, OldCompileCommand) &&
104 OldPreamble->Preamble.CanReuse(CI, ContentsBuffer.get(), Bounds,
105 Inputs.FS.get())) {
Sam McCall2cd33e62020-03-04 00:33:29 +0100106 vlog("Reusing preamble version {0} for version {1} of {2}",
107 OldPreamble->Version, Inputs.Version, FileName);
Sam McCallcf3a5852019-09-04 07:35:00 +0000108 return OldPreamble;
109 }
Sam McCall2cd33e62020-03-04 00:33:29 +0100110 if (OldPreamble)
111 vlog("Rebuilding invalidated preamble for {0} version {1} "
112 "(previous was version {2})",
113 FileName, Inputs.Version, OldPreamble->Version);
114 else
115 vlog("Building first preamble for {0} verson {1}", FileName,
116 Inputs.Version);
Sam McCallcf3a5852019-09-04 07:35:00 +0000117
118 trace::Span Tracer("BuildPreamble");
119 SPAN_ATTACH(Tracer, "File", FileName);
120 StoreDiags PreambleDiagnostics;
121 llvm::IntrusiveRefCntPtr<DiagnosticsEngine> PreambleDiagsEngine =
122 CompilerInstance::createDiagnostics(&CI.getDiagnosticOpts(),
123 &PreambleDiagnostics, false);
124
125 // Skip function bodies when building the preamble to speed up building
126 // the preamble and make it smaller.
127 assert(!CI.getFrontendOpts().SkipFunctionBodies);
128 CI.getFrontendOpts().SkipFunctionBodies = true;
129 // We don't want to write comment locations into PCH. They are racy and slow
130 // to read back. We rely on dynamic index for the comments instead.
131 CI.getPreprocessorOpts().WriteCommentListToPCH = false;
132
133 CppFilePreambleCallbacks SerializedDeclsCollector(FileName, PreambleCallback);
134 if (Inputs.FS->setCurrentWorkingDirectory(Inputs.CompileCommand.Directory)) {
135 log("Couldn't set working directory when building the preamble.");
136 // We proceed anyway, our lit-tests rely on results for non-existing working
137 // dirs.
138 }
139
140 llvm::SmallString<32> AbsFileName(FileName);
141 Inputs.FS->makeAbsolute(AbsFileName);
142 auto StatCache = std::make_unique<PreambleFileStatusCache>(AbsFileName);
143 auto BuiltPreamble = PrecompiledPreamble::Build(
144 CI, ContentsBuffer.get(), Bounds, *PreambleDiagsEngine,
145 StatCache->getProducingFS(Inputs.FS),
146 std::make_shared<PCHContainerOperations>(), StoreInMemory,
147 SerializedDeclsCollector);
148
149 // When building the AST for the main file, we do want the function
150 // bodies.
151 CI.getFrontendOpts().SkipFunctionBodies = false;
152
153 if (BuiltPreamble) {
Sam McCall2cd33e62020-03-04 00:33:29 +0100154 vlog("Built preamble of size {0} for file {1} version {2}",
155 BuiltPreamble->getSize(), FileName, Inputs.Version);
Sam McCallcf3a5852019-09-04 07:35:00 +0000156 std::vector<Diag> Diags = PreambleDiagnostics.take();
157 return std::make_shared<PreambleData>(
Sam McCall2cd33e62020-03-04 00:33:29 +0100158 Inputs.Version, std::move(*BuiltPreamble), std::move(Diags),
Sam McCallcf3a5852019-09-04 07:35:00 +0000159 SerializedDeclsCollector.takeIncludes(),
Haojian Wu7e3c74b2019-09-24 11:14:06 +0000160 SerializedDeclsCollector.takeMacros(), std::move(StatCache),
Sam McCallcf3a5852019-09-04 07:35:00 +0000161 SerializedDeclsCollector.takeCanonicalIncludes());
162 } else {
Sam McCall2cd33e62020-03-04 00:33:29 +0100163 elog("Could not build a preamble for file {0} version {2}", FileName,
164 Inputs.Version);
Sam McCallcf3a5852019-09-04 07:35:00 +0000165 return nullptr;
166 }
167}
168
169} // namespace clangd
170} // namespace clang