blob: 72f55e00392203e3915dd2935d781686016668f9 [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
27// This collects macro definitions in the *preamble region* of the main file.
Sam McCall915f9782019-09-04 09:46:06 +000028// (Contrast with CollectMainFileMacroExpansions in ParsedAST.cpp, which
Sam McCallcf3a5852019-09-04 07:35:00 +000029// collects macro *expansions* in the rest of the main file.
30class CollectMainFileMacros : public PPCallbacks {
31public:
32 explicit CollectMainFileMacros(const SourceManager &SM,
33 std::vector<std::string> *Out)
34 : SM(SM), Out(Out) {}
35
36 void FileChanged(SourceLocation Loc, FileChangeReason,
37 SrcMgr::CharacteristicKind, FileID Prev) {
38 InMainFile = SM.isWrittenInMainFile(Loc);
39 }
40
41 void MacroDefined(const Token &MacroName, const MacroDirective *MD) {
42 if (InMainFile)
43 MainFileMacros.insert(MacroName.getIdentifierInfo()->getName());
44 }
45
46 void EndOfMainFile() {
47 for (const auto &Entry : MainFileMacros)
48 Out->push_back(Entry.getKey());
49 llvm::sort(*Out);
50 }
51
52private:
53 const SourceManager &SM;
54 bool InMainFile = true;
55 llvm::StringSet<> MainFileMacros;
56 std::vector<std::string> *Out;
57};
58
59class CppFilePreambleCallbacks : public PreambleCallbacks {
60public:
61 CppFilePreambleCallbacks(PathRef File, PreambleParsedCallback ParsedCallback)
62 : File(File), ParsedCallback(ParsedCallback) {
63 }
64
65 IncludeStructure takeIncludes() { return std::move(Includes); }
66
67 std::vector<std::string> takeMainFileMacros() {
68 return std::move(MainFileMacros);
69 }
70
71 CanonicalIncludes takeCanonicalIncludes() { return std::move(CanonIncludes); }
72
73 void AfterExecute(CompilerInstance &CI) override {
74 if (!ParsedCallback)
75 return;
76 trace::Span Tracer("Running PreambleCallback");
77 ParsedCallback(CI.getASTContext(), CI.getPreprocessorPtr(), CanonIncludes);
78 }
79
80 void BeforeExecute(CompilerInstance &CI) override {
81 addSystemHeadersMapping(&CanonIncludes, CI.getLangOpts());
82 SourceMgr = &CI.getSourceManager();
83 }
84
85 std::unique_ptr<PPCallbacks> createPPCallbacks() override {
86 assert(SourceMgr && "SourceMgr must be set at this point");
87 return std::make_unique<PPChainedCallbacks>(
88 collectIncludeStructureCallback(*SourceMgr, &Includes),
89 std::make_unique<CollectMainFileMacros>(*SourceMgr, &MainFileMacros));
90 }
91
92 CommentHandler *getCommentHandler() override {
93 IWYUHandler = collectIWYUHeaderMaps(&CanonIncludes);
94 return IWYUHandler.get();
95 }
96
97private:
98 PathRef File;
99 PreambleParsedCallback ParsedCallback;
100 IncludeStructure Includes;
101 CanonicalIncludes CanonIncludes;
102 std::vector<std::string> MainFileMacros;
103 std::unique_ptr<CommentHandler> IWYUHandler = nullptr;
104 SourceManager *SourceMgr = nullptr;
105};
106
107} // namespace
108
109PreambleData::PreambleData(PrecompiledPreamble Preamble,
110 std::vector<Diag> Diags, IncludeStructure Includes,
111 std::vector<std::string> MainFileMacros,
112 std::unique_ptr<PreambleFileStatusCache> StatCache,
113 CanonicalIncludes CanonIncludes)
114 : Preamble(std::move(Preamble)), Diags(std::move(Diags)),
115 Includes(std::move(Includes)), MainFileMacros(std::move(MainFileMacros)),
116 StatCache(std::move(StatCache)), CanonIncludes(std::move(CanonIncludes)) {
117}
118
119std::shared_ptr<const PreambleData>
120buildPreamble(PathRef FileName, CompilerInvocation &CI,
121 std::shared_ptr<const PreambleData> OldPreamble,
122 const tooling::CompileCommand &OldCompileCommand,
123 const ParseInputs &Inputs, bool StoreInMemory,
124 PreambleParsedCallback PreambleCallback) {
125 // Note that we don't need to copy the input contents, preamble can live
126 // without those.
127 auto ContentsBuffer =
128 llvm::MemoryBuffer::getMemBuffer(Inputs.Contents, FileName);
129 auto Bounds =
130 ComputePreambleBounds(*CI.getLangOpts(), ContentsBuffer.get(), 0);
131
132 if (OldPreamble &&
133 compileCommandsAreEqual(Inputs.CompileCommand, OldCompileCommand) &&
134 OldPreamble->Preamble.CanReuse(CI, ContentsBuffer.get(), Bounds,
135 Inputs.FS.get())) {
136 vlog("Reusing preamble for file {0}", llvm::Twine(FileName));
137 return OldPreamble;
138 }
139 vlog("Preamble for file {0} cannot be reused. Attempting to rebuild it.",
140 FileName);
141
142 trace::Span Tracer("BuildPreamble");
143 SPAN_ATTACH(Tracer, "File", FileName);
144 StoreDiags PreambleDiagnostics;
145 llvm::IntrusiveRefCntPtr<DiagnosticsEngine> PreambleDiagsEngine =
146 CompilerInstance::createDiagnostics(&CI.getDiagnosticOpts(),
147 &PreambleDiagnostics, false);
148
149 // Skip function bodies when building the preamble to speed up building
150 // the preamble and make it smaller.
151 assert(!CI.getFrontendOpts().SkipFunctionBodies);
152 CI.getFrontendOpts().SkipFunctionBodies = true;
153 // We don't want to write comment locations into PCH. They are racy and slow
154 // to read back. We rely on dynamic index for the comments instead.
155 CI.getPreprocessorOpts().WriteCommentListToPCH = false;
156
157 CppFilePreambleCallbacks SerializedDeclsCollector(FileName, PreambleCallback);
158 if (Inputs.FS->setCurrentWorkingDirectory(Inputs.CompileCommand.Directory)) {
159 log("Couldn't set working directory when building the preamble.");
160 // We proceed anyway, our lit-tests rely on results for non-existing working
161 // dirs.
162 }
163
164 llvm::SmallString<32> AbsFileName(FileName);
165 Inputs.FS->makeAbsolute(AbsFileName);
166 auto StatCache = std::make_unique<PreambleFileStatusCache>(AbsFileName);
167 auto BuiltPreamble = PrecompiledPreamble::Build(
168 CI, ContentsBuffer.get(), Bounds, *PreambleDiagsEngine,
169 StatCache->getProducingFS(Inputs.FS),
170 std::make_shared<PCHContainerOperations>(), StoreInMemory,
171 SerializedDeclsCollector);
172
173 // When building the AST for the main file, we do want the function
174 // bodies.
175 CI.getFrontendOpts().SkipFunctionBodies = false;
176
177 if (BuiltPreamble) {
178 vlog("Built preamble of size {0} for file {1}", BuiltPreamble->getSize(),
179 FileName);
180 std::vector<Diag> Diags = PreambleDiagnostics.take();
181 return std::make_shared<PreambleData>(
182 std::move(*BuiltPreamble), std::move(Diags),
183 SerializedDeclsCollector.takeIncludes(),
184 SerializedDeclsCollector.takeMainFileMacros(), std::move(StatCache),
185 SerializedDeclsCollector.takeCanonicalIncludes());
186 } else {
187 elog("Could not build a preamble for file {0}", FileName);
188 return nullptr;
189 }
190}
191
192} // namespace clangd
193} // namespace clang