blob: 41e3c1368eb7217d292b71f1b2915e0043d06898 [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),
Haojian Wu7e3c74b2019-09-24 11:14:06 +000057 std::make_unique<CollectMainFileMacros>(*SourceMgr, *LangOpts, 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
78PreambleData::PreambleData(PrecompiledPreamble Preamble,
79 std::vector<Diag> Diags, IncludeStructure Includes,
Haojian Wu7e3c74b2019-09-24 11:14:06 +000080 MainFileMacros Macros,
Sam McCallcf3a5852019-09-04 07:35:00 +000081 std::unique_ptr<PreambleFileStatusCache> StatCache,
82 CanonicalIncludes CanonIncludes)
83 : Preamble(std::move(Preamble)), Diags(std::move(Diags)),
Haojian Wu7e3c74b2019-09-24 11:14:06 +000084 Includes(std::move(Includes)), Macros(std::move(Macros)),
Sam McCallcf3a5852019-09-04 07:35:00 +000085 StatCache(std::move(StatCache)), CanonIncludes(std::move(CanonIncludes)) {
86}
87
88std::shared_ptr<const PreambleData>
89buildPreamble(PathRef FileName, CompilerInvocation &CI,
90 std::shared_ptr<const PreambleData> OldPreamble,
91 const tooling::CompileCommand &OldCompileCommand,
92 const ParseInputs &Inputs, bool StoreInMemory,
93 PreambleParsedCallback PreambleCallback) {
94 // Note that we don't need to copy the input contents, preamble can live
95 // without those.
96 auto ContentsBuffer =
97 llvm::MemoryBuffer::getMemBuffer(Inputs.Contents, FileName);
98 auto Bounds =
99 ComputePreambleBounds(*CI.getLangOpts(), ContentsBuffer.get(), 0);
100
101 if (OldPreamble &&
102 compileCommandsAreEqual(Inputs.CompileCommand, OldCompileCommand) &&
103 OldPreamble->Preamble.CanReuse(CI, ContentsBuffer.get(), Bounds,
104 Inputs.FS.get())) {
105 vlog("Reusing preamble for file {0}", llvm::Twine(FileName));
106 return OldPreamble;
107 }
108 vlog("Preamble for file {0} cannot be reused. Attempting to rebuild it.",
109 FileName);
110
111 trace::Span Tracer("BuildPreamble");
112 SPAN_ATTACH(Tracer, "File", FileName);
113 StoreDiags PreambleDiagnostics;
114 llvm::IntrusiveRefCntPtr<DiagnosticsEngine> PreambleDiagsEngine =
115 CompilerInstance::createDiagnostics(&CI.getDiagnosticOpts(),
116 &PreambleDiagnostics, false);
117
118 // Skip function bodies when building the preamble to speed up building
119 // the preamble and make it smaller.
120 assert(!CI.getFrontendOpts().SkipFunctionBodies);
121 CI.getFrontendOpts().SkipFunctionBodies = true;
122 // We don't want to write comment locations into PCH. They are racy and slow
123 // to read back. We rely on dynamic index for the comments instead.
124 CI.getPreprocessorOpts().WriteCommentListToPCH = false;
125
126 CppFilePreambleCallbacks SerializedDeclsCollector(FileName, PreambleCallback);
127 if (Inputs.FS->setCurrentWorkingDirectory(Inputs.CompileCommand.Directory)) {
128 log("Couldn't set working directory when building the preamble.");
129 // We proceed anyway, our lit-tests rely on results for non-existing working
130 // dirs.
131 }
132
133 llvm::SmallString<32> AbsFileName(FileName);
134 Inputs.FS->makeAbsolute(AbsFileName);
135 auto StatCache = std::make_unique<PreambleFileStatusCache>(AbsFileName);
136 auto BuiltPreamble = PrecompiledPreamble::Build(
137 CI, ContentsBuffer.get(), Bounds, *PreambleDiagsEngine,
138 StatCache->getProducingFS(Inputs.FS),
139 std::make_shared<PCHContainerOperations>(), StoreInMemory,
140 SerializedDeclsCollector);
141
142 // When building the AST for the main file, we do want the function
143 // bodies.
144 CI.getFrontendOpts().SkipFunctionBodies = false;
145
146 if (BuiltPreamble) {
147 vlog("Built preamble of size {0} for file {1}", BuiltPreamble->getSize(),
148 FileName);
149 std::vector<Diag> Diags = PreambleDiagnostics.take();
150 return std::make_shared<PreambleData>(
151 std::move(*BuiltPreamble), std::move(Diags),
152 SerializedDeclsCollector.takeIncludes(),
Haojian Wu7e3c74b2019-09-24 11:14:06 +0000153 SerializedDeclsCollector.takeMacros(), std::move(StatCache),
Sam McCallcf3a5852019-09-04 07:35:00 +0000154 SerializedDeclsCollector.takeCanonicalIncludes());
155 } else {
156 elog("Could not build a preamble for file {0}", FileName);
157 return nullptr;
158 }
159}
160
161} // namespace clangd
162} // namespace clang