blob: dcfafc914d107d47bcb47fb83a0b23f495a9ead5 [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"
Kadir Cetinkaya717bef62020-04-23 17:44:51 +020012#include "SourceCode.h"
Sam McCallad97ccf2020-04-28 17:49:17 +020013#include "support/Logger.h"
14#include "support/Trace.h"
Kadir Cetinkaya2214b902020-04-02 10:53:23 +020015#include "clang/Basic/Diagnostic.h"
16#include "clang/Basic/LangOptions.h"
Sam McCallcf3a5852019-09-04 07:35:00 +000017#include "clang/Basic/SourceLocation.h"
Kadir Cetinkayafcde3d52020-05-14 12:20:33 +020018#include "clang/Basic/SourceManager.h"
Kadir Cetinkaya2214b902020-04-02 10:53:23 +020019#include "clang/Basic/TokenKinds.h"
20#include "clang/Frontend/CompilerInvocation.h"
21#include "clang/Frontend/FrontendActions.h"
22#include "clang/Lex/Lexer.h"
Sam McCallcf3a5852019-09-04 07:35:00 +000023#include "clang/Lex/PPCallbacks.h"
Kadir Cetinkaya2214b902020-04-02 10:53:23 +020024#include "clang/Lex/Preprocessor.h"
Sam McCallcf3a5852019-09-04 07:35:00 +000025#include "clang/Lex/PreprocessorOptions.h"
Kadir Cetinkaya2214b902020-04-02 10:53:23 +020026#include "clang/Tooling/CompilationDatabase.h"
27#include "llvm/ADT/ArrayRef.h"
Kadir Cetinkayab742eaa2020-04-02 10:53:45 +020028#include "llvm/ADT/DenseMap.h"
29#include "llvm/ADT/DenseSet.h"
Kadir Cetinkaya2214b902020-04-02 10:53:23 +020030#include "llvm/ADT/IntrusiveRefCntPtr.h"
31#include "llvm/ADT/STLExtras.h"
32#include "llvm/ADT/SmallString.h"
Kadir Cetinkaya717bef62020-04-23 17:44:51 +020033#include "llvm/ADT/StringExtras.h"
Kadir Cetinkaya2214b902020-04-02 10:53:23 +020034#include "llvm/ADT/StringRef.h"
35#include "llvm/ADT/StringSet.h"
36#include "llvm/Support/Error.h"
37#include "llvm/Support/ErrorHandling.h"
38#include "llvm/Support/FormatVariadic.h"
39#include "llvm/Support/MemoryBuffer.h"
40#include "llvm/Support/Path.h"
41#include "llvm/Support/VirtualFileSystem.h"
42#include "llvm/Support/raw_ostream.h"
43#include <iterator>
44#include <memory>
45#include <string>
46#include <system_error>
47#include <utility>
48#include <vector>
Sam McCallcf3a5852019-09-04 07:35:00 +000049
50namespace clang {
51namespace clangd {
52namespace {
53
54bool compileCommandsAreEqual(const tooling::CompileCommand &LHS,
55 const tooling::CompileCommand &RHS) {
56 // We don't check for Output, it should not matter to clangd.
57 return LHS.Directory == RHS.Directory && LHS.Filename == RHS.Filename &&
58 llvm::makeArrayRef(LHS.CommandLine).equals(RHS.CommandLine);
59}
60
Sam McCallcf3a5852019-09-04 07:35:00 +000061class CppFilePreambleCallbacks : public PreambleCallbacks {
62public:
63 CppFilePreambleCallbacks(PathRef File, PreambleParsedCallback ParsedCallback)
Haojian Wu7e3c74b2019-09-24 11:14:06 +000064 : File(File), ParsedCallback(ParsedCallback) {}
Sam McCallcf3a5852019-09-04 07:35:00 +000065
66 IncludeStructure takeIncludes() { return std::move(Includes); }
67
Haojian Wu7e3c74b2019-09-24 11:14:06 +000068 MainFileMacros takeMacros() { return std::move(Macros); }
Sam McCallcf3a5852019-09-04 07:35:00 +000069
70 CanonicalIncludes takeCanonicalIncludes() { return std::move(CanonIncludes); }
71
72 void AfterExecute(CompilerInstance &CI) override {
73 if (!ParsedCallback)
74 return;
75 trace::Span Tracer("Running PreambleCallback");
76 ParsedCallback(CI.getASTContext(), CI.getPreprocessorPtr(), CanonIncludes);
77 }
78
79 void BeforeExecute(CompilerInstance &CI) override {
Ilya Biryukov8b767092019-09-09 15:32:51 +000080 CanonIncludes.addSystemHeadersMapping(CI.getLangOpts());
Haojian Wu7e3c74b2019-09-24 11:14:06 +000081 LangOpts = &CI.getLangOpts();
Sam McCallcf3a5852019-09-04 07:35:00 +000082 SourceMgr = &CI.getSourceManager();
83 }
84
85 std::unique_ptr<PPCallbacks> createPPCallbacks() override {
Haojian Wu7e3c74b2019-09-24 11:14:06 +000086 assert(SourceMgr && LangOpts &&
87 "SourceMgr and LangOpts must be set at this point");
88
Sam McCallcf3a5852019-09-04 07:35:00 +000089 return std::make_unique<PPChainedCallbacks>(
90 collectIncludeStructureCallback(*SourceMgr, &Includes),
Kadir Cetinkaya37550392020-03-01 16:05:12 +010091 std::make_unique<CollectMainFileMacros>(*SourceMgr, Macros));
Sam McCallcf3a5852019-09-04 07:35:00 +000092 }
93
94 CommentHandler *getCommentHandler() override {
95 IWYUHandler = collectIWYUHeaderMaps(&CanonIncludes);
96 return IWYUHandler.get();
97 }
98
99private:
100 PathRef File;
101 PreambleParsedCallback ParsedCallback;
102 IncludeStructure Includes;
103 CanonicalIncludes CanonIncludes;
Haojian Wu7e3c74b2019-09-24 11:14:06 +0000104 MainFileMacros Macros;
Sam McCallcf3a5852019-09-04 07:35:00 +0000105 std::unique_ptr<CommentHandler> IWYUHandler = nullptr;
Haojian Wu7e3c74b2019-09-24 11:14:06 +0000106 const clang::LangOptions *LangOpts = nullptr;
107 const SourceManager *SourceMgr = nullptr;
Sam McCallcf3a5852019-09-04 07:35:00 +0000108};
109
Kadir Cetinkayafcde3d52020-05-14 12:20:33 +0200110// Represents directives other than includes, where basic textual information is
111// enough.
112struct TextualPPDirective {
113 unsigned DirectiveLine;
114 // Full text that's representing the directive, including the `#`.
115 std::string Text;
116
117 bool operator==(const TextualPPDirective &RHS) const {
118 return std::tie(DirectiveLine, Text) ==
119 std::tie(RHS.DirectiveLine, RHS.Text);
120 }
121};
122
123// Collects #define directives inside the main file.
124struct DirectiveCollector : public PPCallbacks {
125 DirectiveCollector(const Preprocessor &PP,
126 std::vector<TextualPPDirective> &TextualDirectives)
127 : LangOpts(PP.getLangOpts()), SM(PP.getSourceManager()),
128 TextualDirectives(TextualDirectives) {}
129
130 void FileChanged(SourceLocation Loc, FileChangeReason Reason,
131 SrcMgr::CharacteristicKind FileType,
132 FileID PrevFID) override {
133 InMainFile = SM.isWrittenInMainFile(Loc);
134 }
135
136 void MacroDefined(const Token &MacroNameTok,
137 const MacroDirective *MD) override {
138 if (!InMainFile)
139 return;
140 TextualDirectives.emplace_back();
141 TextualPPDirective &TD = TextualDirectives.back();
142
143 auto DecompLoc = SM.getDecomposedLoc(MacroNameTok.getLocation());
144 TD.DirectiveLine = SM.getLineNumber(DecompLoc.first, DecompLoc.second);
145
146 SourceRange DefRange(
147 MD->getMacroInfo()->getDefinitionLoc(),
148 Lexer::getLocForEndOfToken(MD->getMacroInfo()->getDefinitionEndLoc(), 0,
149 SM, LangOpts));
150 llvm::raw_string_ostream OS(TD.Text);
151 OS << "#define " << toSourceCode(SM, DefRange);
152 }
153
154private:
155 bool InMainFile = true;
156 const LangOptions &LangOpts;
157 const SourceManager &SM;
158 std::vector<TextualPPDirective> &TextualDirectives;
159};
160
161struct ScannedPreamble {
162 std::vector<Inclusion> Includes;
163 std::vector<TextualPPDirective> TextualDirectives;
164};
165
166/// Scans the preprocessor directives in the preamble section of the file by
167/// running preprocessor over \p Contents. Returned includes do not contain
168/// resolved paths. \p VFS and \p Cmd is used to build the compiler invocation,
169/// which might stat/read files.
170llvm::Expected<ScannedPreamble>
171scanPreamble(llvm::StringRef Contents,
172 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
173 const tooling::CompileCommand &Cmd) {
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200174 // Build and run Preprocessor over the preamble.
175 ParseInputs PI;
176 PI.Contents = Contents.str();
177 PI.FS = std::move(VFS);
178 PI.CompileCommand = Cmd;
179 IgnoringDiagConsumer IgnoreDiags;
180 auto CI = buildCompilerInvocation(PI, IgnoreDiags);
181 if (!CI)
182 return llvm::createStringError(llvm::inconvertibleErrorCode(),
183 "failed to create compiler invocation");
184 CI->getDiagnosticOpts().IgnoreWarnings = true;
185 auto ContentsBuffer = llvm::MemoryBuffer::getMemBuffer(Contents);
Kadir Cetinkaya34e39eb2020-05-05 17:55:11 +0200186 // This means we're scanning (though not preprocessing) the preamble section
187 // twice. However, it's important to precisely follow the preamble bounds used
188 // elsewhere.
189 auto Bounds =
190 ComputePreambleBounds(*CI->getLangOpts(), ContentsBuffer.get(), 0);
191 auto PreambleContents =
192 llvm::MemoryBuffer::getMemBufferCopy(Contents.substr(0, Bounds.Size));
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200193 auto Clang = prepareCompilerInstance(
Kadir Cetinkaya34e39eb2020-05-05 17:55:11 +0200194 std::move(CI), nullptr, std::move(PreambleContents),
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200195 // Provide an empty FS to prevent preprocessor from performing IO. This
196 // also implies missing resolved paths for includes.
197 new llvm::vfs::InMemoryFileSystem, IgnoreDiags);
198 if (Clang->getFrontendOpts().Inputs.empty())
199 return llvm::createStringError(llvm::inconvertibleErrorCode(),
200 "compiler instance had no inputs");
201 // We are only interested in main file includes.
202 Clang->getPreprocessorOpts().SingleFileParseMode = true;
Kadir Cetinkaya34e39eb2020-05-05 17:55:11 +0200203 PreprocessOnlyAction Action;
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200204 if (!Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0]))
205 return llvm::createStringError(llvm::inconvertibleErrorCode(),
206 "failed BeginSourceFile");
Kadir Cetinkayafcde3d52020-05-14 12:20:33 +0200207 const auto &SM = Clang->getSourceManager();
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200208 Preprocessor &PP = Clang->getPreprocessor();
209 IncludeStructure Includes;
Kadir Cetinkayafcde3d52020-05-14 12:20:33 +0200210 PP.addPPCallbacks(collectIncludeStructureCallback(SM, &Includes));
211 ScannedPreamble SP;
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200212 PP.addPPCallbacks(
Kadir Cetinkayafcde3d52020-05-14 12:20:33 +0200213 std::make_unique<DirectiveCollector>(PP, SP.TextualDirectives));
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200214 if (llvm::Error Err = Action.Execute())
215 return std::move(Err);
216 Action.EndSourceFile();
Kadir Cetinkayafcde3d52020-05-14 12:20:33 +0200217 SP.Includes = std::move(Includes.MainFileIncludes);
218 return SP;
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200219}
220
221const char *spellingForIncDirective(tok::PPKeywordKind IncludeDirective) {
222 switch (IncludeDirective) {
223 case tok::pp_include:
224 return "include";
225 case tok::pp_import:
226 return "import";
227 case tok::pp_include_next:
228 return "include_next";
229 default:
230 break;
231 }
232 llvm_unreachable("not an include directive");
233}
Sam McCallcf3a5852019-09-04 07:35:00 +0000234} // namespace
235
Kadir Cetinkayaecd3e672020-03-11 16:34:01 +0100236PreambleData::PreambleData(const ParseInputs &Inputs,
Sam McCall2cd33e62020-03-04 00:33:29 +0100237 PrecompiledPreamble Preamble,
Sam McCallcf3a5852019-09-04 07:35:00 +0000238 std::vector<Diag> Diags, IncludeStructure Includes,
Haojian Wu7e3c74b2019-09-24 11:14:06 +0000239 MainFileMacros Macros,
Sam McCallcf3a5852019-09-04 07:35:00 +0000240 std::unique_ptr<PreambleFileStatusCache> StatCache,
241 CanonicalIncludes CanonIncludes)
Kadir Cetinkayaecd3e672020-03-11 16:34:01 +0100242 : Version(Inputs.Version), CompileCommand(Inputs.CompileCommand),
243 Preamble(std::move(Preamble)), Diags(std::move(Diags)),
Haojian Wu7e3c74b2019-09-24 11:14:06 +0000244 Includes(std::move(Includes)), Macros(std::move(Macros)),
Sam McCallcf3a5852019-09-04 07:35:00 +0000245 StatCache(std::move(StatCache)), CanonIncludes(std::move(CanonIncludes)) {
246}
247
248std::shared_ptr<const PreambleData>
Kadir Cetinkaya276a95b2020-03-13 11:52:19 +0100249buildPreamble(PathRef FileName, CompilerInvocation CI,
Sam McCallcf3a5852019-09-04 07:35:00 +0000250 const ParseInputs &Inputs, bool StoreInMemory,
251 PreambleParsedCallback PreambleCallback) {
252 // Note that we don't need to copy the input contents, preamble can live
253 // without those.
254 auto ContentsBuffer =
255 llvm::MemoryBuffer::getMemBuffer(Inputs.Contents, FileName);
256 auto Bounds =
257 ComputePreambleBounds(*CI.getLangOpts(), ContentsBuffer.get(), 0);
258
Sam McCallcf3a5852019-09-04 07:35:00 +0000259 trace::Span Tracer("BuildPreamble");
260 SPAN_ATTACH(Tracer, "File", FileName);
261 StoreDiags PreambleDiagnostics;
262 llvm::IntrusiveRefCntPtr<DiagnosticsEngine> PreambleDiagsEngine =
263 CompilerInstance::createDiagnostics(&CI.getDiagnosticOpts(),
264 &PreambleDiagnostics, false);
265
266 // Skip function bodies when building the preamble to speed up building
267 // the preamble and make it smaller.
268 assert(!CI.getFrontendOpts().SkipFunctionBodies);
269 CI.getFrontendOpts().SkipFunctionBodies = true;
270 // We don't want to write comment locations into PCH. They are racy and slow
271 // to read back. We rely on dynamic index for the comments instead.
272 CI.getPreprocessorOpts().WriteCommentListToPCH = false;
273
274 CppFilePreambleCallbacks SerializedDeclsCollector(FileName, PreambleCallback);
275 if (Inputs.FS->setCurrentWorkingDirectory(Inputs.CompileCommand.Directory)) {
276 log("Couldn't set working directory when building the preamble.");
277 // We proceed anyway, our lit-tests rely on results for non-existing working
278 // dirs.
279 }
280
281 llvm::SmallString<32> AbsFileName(FileName);
282 Inputs.FS->makeAbsolute(AbsFileName);
283 auto StatCache = std::make_unique<PreambleFileStatusCache>(AbsFileName);
284 auto BuiltPreamble = PrecompiledPreamble::Build(
285 CI, ContentsBuffer.get(), Bounds, *PreambleDiagsEngine,
286 StatCache->getProducingFS(Inputs.FS),
287 std::make_shared<PCHContainerOperations>(), StoreInMemory,
288 SerializedDeclsCollector);
289
290 // When building the AST for the main file, we do want the function
291 // bodies.
292 CI.getFrontendOpts().SkipFunctionBodies = false;
293
294 if (BuiltPreamble) {
Sam McCall2cd33e62020-03-04 00:33:29 +0100295 vlog("Built preamble of size {0} for file {1} version {2}",
296 BuiltPreamble->getSize(), FileName, Inputs.Version);
Sam McCallcf3a5852019-09-04 07:35:00 +0000297 std::vector<Diag> Diags = PreambleDiagnostics.take();
298 return std::make_shared<PreambleData>(
Kadir Cetinkayaecd3e672020-03-11 16:34:01 +0100299 Inputs, std::move(*BuiltPreamble), std::move(Diags),
Sam McCallcf3a5852019-09-04 07:35:00 +0000300 SerializedDeclsCollector.takeIncludes(),
Haojian Wu7e3c74b2019-09-24 11:14:06 +0000301 SerializedDeclsCollector.takeMacros(), std::move(StatCache),
Sam McCallcf3a5852019-09-04 07:35:00 +0000302 SerializedDeclsCollector.takeCanonicalIncludes());
303 } else {
Adam Czachorowski55b92dc2020-03-19 15:09:28 +0100304 elog("Could not build a preamble for file {0} version {1}", FileName,
Sam McCall2cd33e62020-03-04 00:33:29 +0100305 Inputs.Version);
Sam McCallcf3a5852019-09-04 07:35:00 +0000306 return nullptr;
307 }
308}
309
Kadir Cetinkayac31367e2020-03-15 21:43:00 +0100310bool isPreambleCompatible(const PreambleData &Preamble,
311 const ParseInputs &Inputs, PathRef FileName,
312 const CompilerInvocation &CI) {
313 auto ContentsBuffer =
314 llvm::MemoryBuffer::getMemBuffer(Inputs.Contents, FileName);
315 auto Bounds =
316 ComputePreambleBounds(*CI.getLangOpts(), ContentsBuffer.get(), 0);
317 return compileCommandsAreEqual(Inputs.CompileCommand,
318 Preamble.CompileCommand) &&
319 Preamble.Preamble.CanReuse(CI, ContentsBuffer.get(), Bounds,
320 Inputs.FS.get());
321}
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200322
Kadir Cetinkaya717bef62020-04-23 17:44:51 +0200323void escapeBackslashAndQuotes(llvm::StringRef Text, llvm::raw_ostream &OS) {
324 for (char C : Text) {
325 switch (C) {
326 case '\\':
327 case '"':
328 OS << '\\';
329 break;
330 default:
331 break;
332 }
333 OS << C;
334 }
335}
336
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200337PreamblePatch PreamblePatch::create(llvm::StringRef FileName,
338 const ParseInputs &Modified,
339 const PreambleData &Baseline) {
Kadir Cetinkayab742eaa2020-04-02 10:53:45 +0200340 assert(llvm::sys::path::is_absolute(FileName) && "relative FileName!");
Kadir Cetinkayafcde3d52020-05-14 12:20:33 +0200341 // First scan preprocessor directives in Baseline and Modified. These will be
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200342 // used to figure out newly added directives in Modified. Scanning can fail,
343 // the code just bails out and creates an empty patch in such cases, as:
344 // - If scanning for Baseline fails, no knowledge of existing includes hence
345 // patch will contain all the includes in Modified. Leading to rebuild of
346 // whole preamble, which is terribly slow.
347 // - If scanning for Modified fails, cannot figure out newly added ones so
348 // there's nothing to do but generate an empty patch.
Kadir Cetinkayafcde3d52020-05-14 12:20:33 +0200349 auto BaselineScan = scanPreamble(
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200350 // Contents needs to be null-terminated.
351 Baseline.Preamble.getContents().str(),
352 Baseline.StatCache->getConsumingFS(Modified.FS), Modified.CompileCommand);
Kadir Cetinkayafcde3d52020-05-14 12:20:33 +0200353 if (!BaselineScan) {
354 elog("Failed to scan baseline of {0}: {1}", FileName,
355 BaselineScan.takeError());
356 return PreamblePatch::unmodified(Baseline);
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200357 }
Kadir Cetinkayafcde3d52020-05-14 12:20:33 +0200358 auto ModifiedScan = scanPreamble(
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200359 Modified.Contents, Baseline.StatCache->getConsumingFS(Modified.FS),
360 Modified.CompileCommand);
Kadir Cetinkayafcde3d52020-05-14 12:20:33 +0200361 if (!ModifiedScan) {
362 elog("Failed to scan modified contents of {0}: {1}", FileName,
363 ModifiedScan.takeError());
364 return PreamblePatch::unmodified(Baseline);
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200365 }
Kadir Cetinkayafcde3d52020-05-14 12:20:33 +0200366
367 bool IncludesChanged = BaselineScan->Includes != ModifiedScan->Includes;
368 bool DirectivesChanged =
369 BaselineScan->TextualDirectives != ModifiedScan->TextualDirectives;
370 if (!IncludesChanged && !DirectivesChanged)
Kadir Cetinkayab742eaa2020-04-02 10:53:45 +0200371 return PreamblePatch::unmodified(Baseline);
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200372
373 PreamblePatch PP;
374 // This shouldn't coincide with any real file name.
375 llvm::SmallString<128> PatchName;
376 llvm::sys::path::append(PatchName, llvm::sys::path::parent_path(FileName),
377 "__preamble_patch__.h");
378 PP.PatchFileName = PatchName.str().str();
379
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200380 llvm::raw_string_ostream Patch(PP.PatchContents);
Kadir Cetinkaya717bef62020-04-23 17:44:51 +0200381 // Set default filename for subsequent #line directives
382 Patch << "#line 0 \"";
383 // FileName part of a line directive is subject to backslash escaping, which
384 // might lead to problems on windows especially.
385 escapeBackslashAndQuotes(FileName, Patch);
386 Patch << "\"\n";
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200387
Kadir Cetinkayafcde3d52020-05-14 12:20:33 +0200388 if (IncludesChanged) {
389 // We are only interested in newly added includes, record the ones in
390 // Baseline for exclusion.
391 llvm::DenseMap<std::pair<tok::PPKeywordKind, llvm::StringRef>,
392 /*Resolved=*/llvm::StringRef>
393 ExistingIncludes;
394 for (const auto &Inc : Baseline.Includes.MainFileIncludes)
395 ExistingIncludes[{Inc.Directive, Inc.Written}] = Inc.Resolved;
396 // There might be includes coming from disabled regions, record these for
397 // exclusion too. note that we don't have resolved paths for those.
398 for (const auto &Inc : BaselineScan->Includes)
399 ExistingIncludes.try_emplace({Inc.Directive, Inc.Written});
400 // Calculate extra includes that needs to be inserted.
401 for (auto &Inc : ModifiedScan->Includes) {
402 auto It = ExistingIncludes.find({Inc.Directive, Inc.Written});
403 // Include already present in the baseline preamble. Set resolved path and
404 // put into preamble includes.
405 if (It != ExistingIncludes.end()) {
406 Inc.Resolved = It->second.str();
407 PP.PreambleIncludes.push_back(Inc);
408 continue;
409 }
410 // Include is new in the modified preamble. Inject it into the patch and
411 // use #line to set the presumed location to where it is spelled.
412 auto LineCol = offsetToClangLineColumn(Modified.Contents, Inc.HashOffset);
413 Patch << llvm::formatv("#line {0}\n", LineCol.first);
414 Patch << llvm::formatv(
415 "#{0} {1}\n", spellingForIncDirective(Inc.Directive), Inc.Written);
416 }
417 }
418
419 if (DirectivesChanged) {
420 // We need to patch all the directives, since they are order dependent. e.g:
421 // #define BAR(X) NEW(X) // Newly introduced in Modified
422 // #define BAR(X) OLD(X) // Exists in the Baseline
423 //
424 // If we've patched only the first directive, the macro definition would've
425 // been wrong for the rest of the file, since patch is applied after the
426 // baseline preamble.
427 //
428 // Note that we deliberately ignore conditional directives and undefs to
429 // reduce complexity. The former might cause problems because scanning is
430 // imprecise and might pick directives from disabled regions.
431 for (const auto &TD : ModifiedScan->TextualDirectives)
432 Patch << TD.Text << '\n';
433 }
434 dlog("Created preamble patch: {0}", Patch.str());
435 Patch.flush();
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200436 return PP;
437}
438
439void PreamblePatch::apply(CompilerInvocation &CI) const {
440 // No need to map an empty file.
441 if (PatchContents.empty())
442 return;
443 auto &PPOpts = CI.getPreprocessorOpts();
444 auto PatchBuffer =
445 // we copy here to ensure contents are still valid if CI outlives the
446 // PreamblePatch.
447 llvm::MemoryBuffer::getMemBufferCopy(PatchContents, PatchFileName);
448 // CI will take care of the lifetime of the buffer.
449 PPOpts.addRemappedFile(PatchFileName, PatchBuffer.release());
450 // The patch will be parsed after loading the preamble ast and before parsing
451 // the main file.
452 PPOpts.Includes.push_back(PatchFileName);
453}
454
Kadir Cetinkayab742eaa2020-04-02 10:53:45 +0200455std::vector<Inclusion> PreamblePatch::preambleIncludes() const {
456 return PreambleIncludes;
457}
458
459PreamblePatch PreamblePatch::unmodified(const PreambleData &Preamble) {
460 PreamblePatch PP;
461 PP.PreambleIncludes = Preamble.Includes.MainFileIncludes;
462 return PP;
463}
464
Sam McCallcf3a5852019-09-04 07:35:00 +0000465} // namespace clangd
466} // namespace clang