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