blob: af7229ecb88871467391e6ab76d84a4708b6230b [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"
Kadir Cetinkayaf693ce42020-06-04 18:26:52 +020013#include "support/FSProvider.h"
Sam McCallad97ccf2020-04-28 17:49:17 +020014#include "support/Logger.h"
15#include "support/Trace.h"
Kadir Cetinkaya2214b902020-04-02 10:53:23 +020016#include "clang/Basic/Diagnostic.h"
17#include "clang/Basic/LangOptions.h"
Sam McCallcf3a5852019-09-04 07:35:00 +000018#include "clang/Basic/SourceLocation.h"
Kadir Cetinkayafcde3d52020-05-14 12:20:33 +020019#include "clang/Basic/SourceManager.h"
Kadir Cetinkaya2214b902020-04-02 10:53:23 +020020#include "clang/Basic/TokenKinds.h"
21#include "clang/Frontend/CompilerInvocation.h"
22#include "clang/Frontend/FrontendActions.h"
23#include "clang/Lex/Lexer.h"
Sam McCallcf3a5852019-09-04 07:35:00 +000024#include "clang/Lex/PPCallbacks.h"
Kadir Cetinkaya2214b902020-04-02 10:53:23 +020025#include "clang/Lex/Preprocessor.h"
Sam McCallcf3a5852019-09-04 07:35:00 +000026#include "clang/Lex/PreprocessorOptions.h"
Kadir Cetinkaya2214b902020-04-02 10:53:23 +020027#include "clang/Tooling/CompilationDatabase.h"
28#include "llvm/ADT/ArrayRef.h"
Kadir Cetinkayab742eaa2020-04-02 10:53:45 +020029#include "llvm/ADT/DenseMap.h"
30#include "llvm/ADT/DenseSet.h"
Kadir Cetinkaya2214b902020-04-02 10:53:23 +020031#include "llvm/ADT/IntrusiveRefCntPtr.h"
32#include "llvm/ADT/STLExtras.h"
33#include "llvm/ADT/SmallString.h"
Kadir Cetinkaya717bef62020-04-23 17:44:51 +020034#include "llvm/ADT/StringExtras.h"
Kadir Cetinkaya2214b902020-04-02 10:53:23 +020035#include "llvm/ADT/StringRef.h"
36#include "llvm/ADT/StringSet.h"
37#include "llvm/Support/Error.h"
38#include "llvm/Support/ErrorHandling.h"
39#include "llvm/Support/FormatVariadic.h"
40#include "llvm/Support/MemoryBuffer.h"
41#include "llvm/Support/Path.h"
42#include "llvm/Support/VirtualFileSystem.h"
43#include "llvm/Support/raw_ostream.h"
44#include <iterator>
45#include <memory>
46#include <string>
47#include <system_error>
48#include <utility>
49#include <vector>
Sam McCallcf3a5852019-09-04 07:35:00 +000050
51namespace clang {
52namespace clangd {
53namespace {
Kadir Cetinkaya538c2752020-05-14 12:26:47 +020054constexpr llvm::StringLiteral PreamblePatchHeaderName = "__preamble_patch__.h";
Sam McCallcf3a5852019-09-04 07:35:00 +000055
56bool compileCommandsAreEqual(const tooling::CompileCommand &LHS,
57 const tooling::CompileCommand &RHS) {
58 // We don't check for Output, it should not matter to clangd.
59 return LHS.Directory == RHS.Directory && LHS.Filename == RHS.Filename &&
60 llvm::makeArrayRef(LHS.CommandLine).equals(RHS.CommandLine);
61}
62
Sam McCallcf3a5852019-09-04 07:35:00 +000063class CppFilePreambleCallbacks : public PreambleCallbacks {
64public:
65 CppFilePreambleCallbacks(PathRef File, PreambleParsedCallback ParsedCallback)
Haojian Wu7e3c74b2019-09-24 11:14:06 +000066 : File(File), ParsedCallback(ParsedCallback) {}
Sam McCallcf3a5852019-09-04 07:35:00 +000067
68 IncludeStructure takeIncludes() { return std::move(Includes); }
69
Haojian Wu7e3c74b2019-09-24 11:14:06 +000070 MainFileMacros takeMacros() { return std::move(Macros); }
Sam McCallcf3a5852019-09-04 07:35:00 +000071
72 CanonicalIncludes takeCanonicalIncludes() { return std::move(CanonIncludes); }
73
74 void AfterExecute(CompilerInstance &CI) override {
75 if (!ParsedCallback)
76 return;
77 trace::Span Tracer("Running PreambleCallback");
78 ParsedCallback(CI.getASTContext(), CI.getPreprocessorPtr(), CanonIncludes);
79 }
80
81 void BeforeExecute(CompilerInstance &CI) override {
Ilya Biryukov8b767092019-09-09 15:32:51 +000082 CanonIncludes.addSystemHeadersMapping(CI.getLangOpts());
Haojian Wu7e3c74b2019-09-24 11:14:06 +000083 LangOpts = &CI.getLangOpts();
Sam McCallcf3a5852019-09-04 07:35:00 +000084 SourceMgr = &CI.getSourceManager();
85 }
86
87 std::unique_ptr<PPCallbacks> createPPCallbacks() override {
Haojian Wu7e3c74b2019-09-24 11:14:06 +000088 assert(SourceMgr && LangOpts &&
89 "SourceMgr and LangOpts must be set at this point");
90
Sam McCallcf3a5852019-09-04 07:35:00 +000091 return std::make_unique<PPChainedCallbacks>(
92 collectIncludeStructureCallback(*SourceMgr, &Includes),
Kadir Cetinkaya37550392020-03-01 16:05:12 +010093 std::make_unique<CollectMainFileMacros>(*SourceMgr, Macros));
Sam McCallcf3a5852019-09-04 07:35:00 +000094 }
95
96 CommentHandler *getCommentHandler() override {
97 IWYUHandler = collectIWYUHeaderMaps(&CanonIncludes);
98 return IWYUHandler.get();
99 }
100
101private:
102 PathRef File;
103 PreambleParsedCallback ParsedCallback;
104 IncludeStructure Includes;
105 CanonicalIncludes CanonIncludes;
Haojian Wu7e3c74b2019-09-24 11:14:06 +0000106 MainFileMacros Macros;
Sam McCallcf3a5852019-09-04 07:35:00 +0000107 std::unique_ptr<CommentHandler> IWYUHandler = nullptr;
Haojian Wu7e3c74b2019-09-24 11:14:06 +0000108 const clang::LangOptions *LangOpts = nullptr;
109 const SourceManager *SourceMgr = nullptr;
Sam McCallcf3a5852019-09-04 07:35:00 +0000110};
111
Kadir Cetinkayafcde3d52020-05-14 12:20:33 +0200112// Represents directives other than includes, where basic textual information is
113// enough.
114struct TextualPPDirective {
115 unsigned DirectiveLine;
116 // Full text that's representing the directive, including the `#`.
117 std::string Text;
118
119 bool operator==(const TextualPPDirective &RHS) const {
120 return std::tie(DirectiveLine, Text) ==
121 std::tie(RHS.DirectiveLine, RHS.Text);
122 }
123};
124
Kadir Cetinkaya538c2752020-05-14 12:26:47 +0200125// Formats a PP directive consisting of Prefix (e.g. "#define ") and Body ("X
126// 10"). The formatting is copied so that the tokens in Body have PresumedLocs
127// with correct columns and lines.
128std::string spellDirective(llvm::StringRef Prefix,
129 CharSourceRange DirectiveRange,
130 const LangOptions &LangOpts, const SourceManager &SM,
131 unsigned &DirectiveLine) {
132 std::string SpelledDirective;
133 llvm::raw_string_ostream OS(SpelledDirective);
134 OS << Prefix;
135
136 // Make sure DirectiveRange is a char range and doesn't contain macro ids.
137 DirectiveRange = SM.getExpansionRange(DirectiveRange);
138 if (DirectiveRange.isTokenRange()) {
139 DirectiveRange.setEnd(
140 Lexer::getLocForEndOfToken(DirectiveRange.getEnd(), 0, SM, LangOpts));
141 }
142
143 auto DecompLoc = SM.getDecomposedLoc(DirectiveRange.getBegin());
144 DirectiveLine = SM.getLineNumber(DecompLoc.first, DecompLoc.second);
145 auto TargetColumn = SM.getColumnNumber(DecompLoc.first, DecompLoc.second) - 1;
146
147 // Pad with spaces before DirectiveRange to make sure it will be on right
148 // column when patched.
149 if (Prefix.size() <= TargetColumn) {
150 // There is enough space for Prefix and space before directive, use it.
151 // We try to squeeze the Prefix into the same line whenever we can, as
152 // putting onto a separate line won't work at the beginning of the file.
153 OS << std::string(TargetColumn - Prefix.size(), ' ');
154 } else {
155 // Prefix was longer than the space we had. We produce e.g.:
156 // #line N-1
157 // #define \
158 // X 10
159 OS << "\\\n" << std::string(TargetColumn, ' ');
160 // Decrement because we put an additional line break before
161 // DirectiveRange.begin().
162 --DirectiveLine;
163 }
164 OS << toSourceCode(SM, DirectiveRange.getAsRange());
165 return OS.str();
166}
167
Kadir Cetinkayafcde3d52020-05-14 12:20:33 +0200168// Collects #define directives inside the main file.
169struct DirectiveCollector : public PPCallbacks {
170 DirectiveCollector(const Preprocessor &PP,
171 std::vector<TextualPPDirective> &TextualDirectives)
172 : LangOpts(PP.getLangOpts()), SM(PP.getSourceManager()),
173 TextualDirectives(TextualDirectives) {}
174
175 void FileChanged(SourceLocation Loc, FileChangeReason Reason,
176 SrcMgr::CharacteristicKind FileType,
177 FileID PrevFID) override {
178 InMainFile = SM.isWrittenInMainFile(Loc);
179 }
180
181 void MacroDefined(const Token &MacroNameTok,
182 const MacroDirective *MD) override {
183 if (!InMainFile)
184 return;
185 TextualDirectives.emplace_back();
186 TextualPPDirective &TD = TextualDirectives.back();
187
Kadir Cetinkaya538c2752020-05-14 12:26:47 +0200188 const auto *MI = MD->getMacroInfo();
189 TD.Text =
190 spellDirective("#define ",
191 CharSourceRange::getTokenRange(
192 MI->getDefinitionLoc(), MI->getDefinitionEndLoc()),
193 LangOpts, SM, TD.DirectiveLine);
Kadir Cetinkayafcde3d52020-05-14 12:20:33 +0200194 }
195
196private:
197 bool InMainFile = true;
198 const LangOptions &LangOpts;
199 const SourceManager &SM;
200 std::vector<TextualPPDirective> &TextualDirectives;
201};
202
203struct ScannedPreamble {
204 std::vector<Inclusion> Includes;
205 std::vector<TextualPPDirective> TextualDirectives;
206};
207
208/// Scans the preprocessor directives in the preamble section of the file by
209/// running preprocessor over \p Contents. Returned includes do not contain
210/// resolved paths. \p VFS and \p Cmd is used to build the compiler invocation,
211/// which might stat/read files.
212llvm::Expected<ScannedPreamble>
213scanPreamble(llvm::StringRef Contents,
214 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
215 const tooling::CompileCommand &Cmd) {
Kadir Cetinkayaf693ce42020-06-04 18:26:52 +0200216 // FIXME: Change PreambleStatCache to operate on FileSystemProvider rather
217 // than vfs::FileSystem, that way we can just use ParseInputs without this
218 // hack.
219 auto GetFSProvider = [](llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) {
220 class VFSProvider : public FileSystemProvider {
221 public:
222 VFSProvider(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)
223 : VFS(std::move(FS)) {}
224 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>
225 getFileSystem() const override {
226 return VFS;
227 }
228
229 private:
230 const llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS;
231 };
232 return std::make_unique<VFSProvider>(std::move(FS));
233 };
234 auto FSProvider = GetFSProvider(std::move(VFS));
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200235 // Build and run Preprocessor over the preamble.
236 ParseInputs PI;
237 PI.Contents = Contents.str();
Kadir Cetinkayaf693ce42020-06-04 18:26:52 +0200238 PI.FSProvider = FSProvider.get();
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200239 PI.CompileCommand = Cmd;
240 IgnoringDiagConsumer IgnoreDiags;
241 auto CI = buildCompilerInvocation(PI, IgnoreDiags);
242 if (!CI)
243 return llvm::createStringError(llvm::inconvertibleErrorCode(),
244 "failed to create compiler invocation");
245 CI->getDiagnosticOpts().IgnoreWarnings = true;
246 auto ContentsBuffer = llvm::MemoryBuffer::getMemBuffer(Contents);
Kadir Cetinkaya34e39eb2020-05-05 17:55:11 +0200247 // This means we're scanning (though not preprocessing) the preamble section
248 // twice. However, it's important to precisely follow the preamble bounds used
249 // elsewhere.
250 auto Bounds =
251 ComputePreambleBounds(*CI->getLangOpts(), ContentsBuffer.get(), 0);
252 auto PreambleContents =
253 llvm::MemoryBuffer::getMemBufferCopy(Contents.substr(0, Bounds.Size));
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200254 auto Clang = prepareCompilerInstance(
Kadir Cetinkaya34e39eb2020-05-05 17:55:11 +0200255 std::move(CI), nullptr, std::move(PreambleContents),
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200256 // Provide an empty FS to prevent preprocessor from performing IO. This
257 // also implies missing resolved paths for includes.
258 new llvm::vfs::InMemoryFileSystem, IgnoreDiags);
259 if (Clang->getFrontendOpts().Inputs.empty())
260 return llvm::createStringError(llvm::inconvertibleErrorCode(),
261 "compiler instance had no inputs");
262 // We are only interested in main file includes.
263 Clang->getPreprocessorOpts().SingleFileParseMode = true;
Kadir Cetinkaya34e39eb2020-05-05 17:55:11 +0200264 PreprocessOnlyAction Action;
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200265 if (!Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0]))
266 return llvm::createStringError(llvm::inconvertibleErrorCode(),
267 "failed BeginSourceFile");
Kadir Cetinkayafcde3d52020-05-14 12:20:33 +0200268 const auto &SM = Clang->getSourceManager();
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200269 Preprocessor &PP = Clang->getPreprocessor();
270 IncludeStructure Includes;
Kadir Cetinkayafcde3d52020-05-14 12:20:33 +0200271 PP.addPPCallbacks(collectIncludeStructureCallback(SM, &Includes));
272 ScannedPreamble SP;
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200273 PP.addPPCallbacks(
Kadir Cetinkayafcde3d52020-05-14 12:20:33 +0200274 std::make_unique<DirectiveCollector>(PP, SP.TextualDirectives));
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200275 if (llvm::Error Err = Action.Execute())
276 return std::move(Err);
277 Action.EndSourceFile();
Kadir Cetinkayafcde3d52020-05-14 12:20:33 +0200278 SP.Includes = std::move(Includes.MainFileIncludes);
279 return SP;
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200280}
281
282const char *spellingForIncDirective(tok::PPKeywordKind IncludeDirective) {
283 switch (IncludeDirective) {
284 case tok::pp_include:
285 return "include";
286 case tok::pp_import:
287 return "import";
288 case tok::pp_include_next:
289 return "include_next";
290 default:
291 break;
292 }
293 llvm_unreachable("not an include directive");
294}
Kadir Cetinkaya538c2752020-05-14 12:26:47 +0200295
296// Checks whether \p FileName is a valid spelling of main file.
297bool isMainFile(llvm::StringRef FileName, const SourceManager &SM) {
298 auto FE = SM.getFileManager().getFile(FileName);
299 return FE && *FE == SM.getFileEntryForID(SM.getMainFileID());
300}
301
Sam McCallcf3a5852019-09-04 07:35:00 +0000302} // namespace
303
Kadir Cetinkayaecd3e672020-03-11 16:34:01 +0100304PreambleData::PreambleData(const ParseInputs &Inputs,
Sam McCall2cd33e62020-03-04 00:33:29 +0100305 PrecompiledPreamble Preamble,
Sam McCallcf3a5852019-09-04 07:35:00 +0000306 std::vector<Diag> Diags, IncludeStructure Includes,
Haojian Wu7e3c74b2019-09-24 11:14:06 +0000307 MainFileMacros Macros,
Sam McCallcf3a5852019-09-04 07:35:00 +0000308 std::unique_ptr<PreambleFileStatusCache> StatCache,
309 CanonicalIncludes CanonIncludes)
Kadir Cetinkayaecd3e672020-03-11 16:34:01 +0100310 : Version(Inputs.Version), CompileCommand(Inputs.CompileCommand),
311 Preamble(std::move(Preamble)), Diags(std::move(Diags)),
Haojian Wu7e3c74b2019-09-24 11:14:06 +0000312 Includes(std::move(Includes)), Macros(std::move(Macros)),
Sam McCallcf3a5852019-09-04 07:35:00 +0000313 StatCache(std::move(StatCache)), CanonIncludes(std::move(CanonIncludes)) {
314}
315
316std::shared_ptr<const PreambleData>
Kadir Cetinkaya276a95b2020-03-13 11:52:19 +0100317buildPreamble(PathRef FileName, CompilerInvocation CI,
Sam McCallcf3a5852019-09-04 07:35:00 +0000318 const ParseInputs &Inputs, bool StoreInMemory,
319 PreambleParsedCallback PreambleCallback) {
320 // Note that we don't need to copy the input contents, preamble can live
321 // without those.
322 auto ContentsBuffer =
323 llvm::MemoryBuffer::getMemBuffer(Inputs.Contents, FileName);
324 auto Bounds =
325 ComputePreambleBounds(*CI.getLangOpts(), ContentsBuffer.get(), 0);
326
Sam McCallcf3a5852019-09-04 07:35:00 +0000327 trace::Span Tracer("BuildPreamble");
328 SPAN_ATTACH(Tracer, "File", FileName);
329 StoreDiags PreambleDiagnostics;
330 llvm::IntrusiveRefCntPtr<DiagnosticsEngine> PreambleDiagsEngine =
331 CompilerInstance::createDiagnostics(&CI.getDiagnosticOpts(),
332 &PreambleDiagnostics, false);
333
334 // Skip function bodies when building the preamble to speed up building
335 // the preamble and make it smaller.
336 assert(!CI.getFrontendOpts().SkipFunctionBodies);
337 CI.getFrontendOpts().SkipFunctionBodies = true;
338 // We don't want to write comment locations into PCH. They are racy and slow
339 // to read back. We rely on dynamic index for the comments instead.
340 CI.getPreprocessorOpts().WriteCommentListToPCH = false;
341
342 CppFilePreambleCallbacks SerializedDeclsCollector(FileName, PreambleCallback);
Kadir Cetinkayaf693ce42020-06-04 18:26:52 +0200343 auto VFS = Inputs.FSProvider->getFileSystem();
344 if (VFS->setCurrentWorkingDirectory(Inputs.CompileCommand.Directory)) {
Sam McCallcf3a5852019-09-04 07:35:00 +0000345 log("Couldn't set working directory when building the preamble.");
346 // We proceed anyway, our lit-tests rely on results for non-existing working
347 // dirs.
348 }
349
350 llvm::SmallString<32> AbsFileName(FileName);
Kadir Cetinkayaf693ce42020-06-04 18:26:52 +0200351 VFS->makeAbsolute(AbsFileName);
Sam McCallcf3a5852019-09-04 07:35:00 +0000352 auto StatCache = std::make_unique<PreambleFileStatusCache>(AbsFileName);
353 auto BuiltPreamble = PrecompiledPreamble::Build(
354 CI, ContentsBuffer.get(), Bounds, *PreambleDiagsEngine,
Kadir Cetinkayaf693ce42020-06-04 18:26:52 +0200355 StatCache->getProducingFS(VFS),
Sam McCallcf3a5852019-09-04 07:35:00 +0000356 std::make_shared<PCHContainerOperations>(), StoreInMemory,
357 SerializedDeclsCollector);
358
359 // When building the AST for the main file, we do want the function
360 // bodies.
361 CI.getFrontendOpts().SkipFunctionBodies = false;
362
363 if (BuiltPreamble) {
Sam McCall2cd33e62020-03-04 00:33:29 +0100364 vlog("Built preamble of size {0} for file {1} version {2}",
365 BuiltPreamble->getSize(), FileName, Inputs.Version);
Sam McCallcf3a5852019-09-04 07:35:00 +0000366 std::vector<Diag> Diags = PreambleDiagnostics.take();
367 return std::make_shared<PreambleData>(
Kadir Cetinkayaecd3e672020-03-11 16:34:01 +0100368 Inputs, std::move(*BuiltPreamble), std::move(Diags),
Sam McCallcf3a5852019-09-04 07:35:00 +0000369 SerializedDeclsCollector.takeIncludes(),
Haojian Wu7e3c74b2019-09-24 11:14:06 +0000370 SerializedDeclsCollector.takeMacros(), std::move(StatCache),
Sam McCallcf3a5852019-09-04 07:35:00 +0000371 SerializedDeclsCollector.takeCanonicalIncludes());
372 } else {
Adam Czachorowski55b92dc2020-03-19 15:09:28 +0100373 elog("Could not build a preamble for file {0} version {1}", FileName,
Sam McCall2cd33e62020-03-04 00:33:29 +0100374 Inputs.Version);
Sam McCallcf3a5852019-09-04 07:35:00 +0000375 return nullptr;
376 }
377}
378
Kadir Cetinkayac31367e2020-03-15 21:43:00 +0100379bool isPreambleCompatible(const PreambleData &Preamble,
380 const ParseInputs &Inputs, PathRef FileName,
381 const CompilerInvocation &CI) {
382 auto ContentsBuffer =
383 llvm::MemoryBuffer::getMemBuffer(Inputs.Contents, FileName);
384 auto Bounds =
385 ComputePreambleBounds(*CI.getLangOpts(), ContentsBuffer.get(), 0);
386 return compileCommandsAreEqual(Inputs.CompileCommand,
387 Preamble.CompileCommand) &&
388 Preamble.Preamble.CanReuse(CI, ContentsBuffer.get(), Bounds,
Kadir Cetinkayaf693ce42020-06-04 18:26:52 +0200389 Inputs.FSProvider->getFileSystem().get());
Kadir Cetinkayac31367e2020-03-15 21:43:00 +0100390}
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200391
Kadir Cetinkaya717bef62020-04-23 17:44:51 +0200392void escapeBackslashAndQuotes(llvm::StringRef Text, llvm::raw_ostream &OS) {
393 for (char C : Text) {
394 switch (C) {
395 case '\\':
396 case '"':
397 OS << '\\';
398 break;
399 default:
400 break;
401 }
402 OS << C;
403 }
404}
405
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200406PreamblePatch PreamblePatch::create(llvm::StringRef FileName,
407 const ParseInputs &Modified,
408 const PreambleData &Baseline) {
Kadir Cetinkaya20b2af32020-05-29 12:31:35 +0200409 trace::Span Tracer("CreatePreamblePatch");
410 SPAN_ATTACH(Tracer, "File", FileName);
Kadir Cetinkayab742eaa2020-04-02 10:53:45 +0200411 assert(llvm::sys::path::is_absolute(FileName) && "relative FileName!");
Kadir Cetinkayaf693ce42020-06-04 18:26:52 +0200412 auto VFS =
413 Baseline.StatCache->getConsumingFS(Modified.FSProvider->getFileSystem());
Kadir Cetinkayafcde3d52020-05-14 12:20:33 +0200414 // First scan preprocessor directives in Baseline and Modified. These will be
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200415 // used to figure out newly added directives in Modified. Scanning can fail,
416 // the code just bails out and creates an empty patch in such cases, as:
417 // - If scanning for Baseline fails, no knowledge of existing includes hence
418 // patch will contain all the includes in Modified. Leading to rebuild of
419 // whole preamble, which is terribly slow.
420 // - If scanning for Modified fails, cannot figure out newly added ones so
421 // there's nothing to do but generate an empty patch.
Kadir Cetinkayafcde3d52020-05-14 12:20:33 +0200422 auto BaselineScan = scanPreamble(
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200423 // Contents needs to be null-terminated.
Kadir Cetinkayaf693ce42020-06-04 18:26:52 +0200424 Baseline.Preamble.getContents().str(), VFS, Modified.CompileCommand);
Kadir Cetinkayafcde3d52020-05-14 12:20:33 +0200425 if (!BaselineScan) {
426 elog("Failed to scan baseline of {0}: {1}", FileName,
427 BaselineScan.takeError());
428 return PreamblePatch::unmodified(Baseline);
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200429 }
Kadir Cetinkayaf693ce42020-06-04 18:26:52 +0200430 auto ModifiedScan =
431 scanPreamble(Modified.Contents, std::move(VFS), Modified.CompileCommand);
Kadir Cetinkayafcde3d52020-05-14 12:20:33 +0200432 if (!ModifiedScan) {
433 elog("Failed to scan modified contents of {0}: {1}", FileName,
434 ModifiedScan.takeError());
435 return PreamblePatch::unmodified(Baseline);
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200436 }
Kadir Cetinkayafcde3d52020-05-14 12:20:33 +0200437
438 bool IncludesChanged = BaselineScan->Includes != ModifiedScan->Includes;
439 bool DirectivesChanged =
440 BaselineScan->TextualDirectives != ModifiedScan->TextualDirectives;
441 if (!IncludesChanged && !DirectivesChanged)
Kadir Cetinkayab742eaa2020-04-02 10:53:45 +0200442 return PreamblePatch::unmodified(Baseline);
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200443
444 PreamblePatch PP;
445 // This shouldn't coincide with any real file name.
446 llvm::SmallString<128> PatchName;
447 llvm::sys::path::append(PatchName, llvm::sys::path::parent_path(FileName),
Kadir Cetinkaya538c2752020-05-14 12:26:47 +0200448 PreamblePatchHeaderName);
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200449 PP.PatchFileName = PatchName.str().str();
450
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200451 llvm::raw_string_ostream Patch(PP.PatchContents);
Kadir Cetinkaya717bef62020-04-23 17:44:51 +0200452 // Set default filename for subsequent #line directives
453 Patch << "#line 0 \"";
454 // FileName part of a line directive is subject to backslash escaping, which
455 // might lead to problems on windows especially.
456 escapeBackslashAndQuotes(FileName, Patch);
457 Patch << "\"\n";
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200458
Kadir Cetinkayafcde3d52020-05-14 12:20:33 +0200459 if (IncludesChanged) {
460 // We are only interested in newly added includes, record the ones in
461 // Baseline for exclusion.
462 llvm::DenseMap<std::pair<tok::PPKeywordKind, llvm::StringRef>,
463 /*Resolved=*/llvm::StringRef>
464 ExistingIncludes;
465 for (const auto &Inc : Baseline.Includes.MainFileIncludes)
466 ExistingIncludes[{Inc.Directive, Inc.Written}] = Inc.Resolved;
467 // There might be includes coming from disabled regions, record these for
468 // exclusion too. note that we don't have resolved paths for those.
469 for (const auto &Inc : BaselineScan->Includes)
470 ExistingIncludes.try_emplace({Inc.Directive, Inc.Written});
471 // Calculate extra includes that needs to be inserted.
472 for (auto &Inc : ModifiedScan->Includes) {
473 auto It = ExistingIncludes.find({Inc.Directive, Inc.Written});
474 // Include already present in the baseline preamble. Set resolved path and
475 // put into preamble includes.
476 if (It != ExistingIncludes.end()) {
477 Inc.Resolved = It->second.str();
478 PP.PreambleIncludes.push_back(Inc);
479 continue;
480 }
481 // Include is new in the modified preamble. Inject it into the patch and
482 // use #line to set the presumed location to where it is spelled.
483 auto LineCol = offsetToClangLineColumn(Modified.Contents, Inc.HashOffset);
484 Patch << llvm::formatv("#line {0}\n", LineCol.first);
485 Patch << llvm::formatv(
486 "#{0} {1}\n", spellingForIncDirective(Inc.Directive), Inc.Written);
487 }
488 }
489
490 if (DirectivesChanged) {
491 // We need to patch all the directives, since they are order dependent. e.g:
492 // #define BAR(X) NEW(X) // Newly introduced in Modified
493 // #define BAR(X) OLD(X) // Exists in the Baseline
494 //
495 // If we've patched only the first directive, the macro definition would've
496 // been wrong for the rest of the file, since patch is applied after the
497 // baseline preamble.
498 //
499 // Note that we deliberately ignore conditional directives and undefs to
500 // reduce complexity. The former might cause problems because scanning is
501 // imprecise and might pick directives from disabled regions.
Kadir Cetinkaya538c2752020-05-14 12:26:47 +0200502 for (const auto &TD : ModifiedScan->TextualDirectives) {
503 Patch << "#line " << TD.DirectiveLine << '\n';
Kadir Cetinkayafcde3d52020-05-14 12:20:33 +0200504 Patch << TD.Text << '\n';
Kadir Cetinkaya538c2752020-05-14 12:26:47 +0200505 }
Kadir Cetinkayafcde3d52020-05-14 12:20:33 +0200506 }
507 dlog("Created preamble patch: {0}", Patch.str());
508 Patch.flush();
Kadir Cetinkaya2214b902020-04-02 10:53:23 +0200509 return PP;
510}
511
512void PreamblePatch::apply(CompilerInvocation &CI) const {
513 // No need to map an empty file.
514 if (PatchContents.empty())
515 return;
516 auto &PPOpts = CI.getPreprocessorOpts();
517 auto PatchBuffer =
518 // we copy here to ensure contents are still valid if CI outlives the
519 // PreamblePatch.
520 llvm::MemoryBuffer::getMemBufferCopy(PatchContents, PatchFileName);
521 // CI will take care of the lifetime of the buffer.
522 PPOpts.addRemappedFile(PatchFileName, PatchBuffer.release());
523 // The patch will be parsed after loading the preamble ast and before parsing
524 // the main file.
525 PPOpts.Includes.push_back(PatchFileName);
526}
527
Kadir Cetinkayab742eaa2020-04-02 10:53:45 +0200528std::vector<Inclusion> PreamblePatch::preambleIncludes() const {
529 return PreambleIncludes;
530}
531
532PreamblePatch PreamblePatch::unmodified(const PreambleData &Preamble) {
533 PreamblePatch PP;
534 PP.PreambleIncludes = Preamble.Includes.MainFileIncludes;
535 return PP;
536}
537
Kadir Cetinkaya538c2752020-05-14 12:26:47 +0200538SourceLocation translatePreamblePatchLocation(SourceLocation Loc,
539 const SourceManager &SM) {
540 auto DefFile = SM.getFileID(Loc);
541 if (auto *FE = SM.getFileEntryForID(DefFile)) {
542 auto IncludeLoc = SM.getIncludeLoc(DefFile);
543 // Preamble patch is included inside the builtin file.
544 if (IncludeLoc.isValid() && SM.isWrittenInBuiltinFile(IncludeLoc) &&
545 FE->getName().endswith(PreamblePatchHeaderName)) {
546 auto Presumed = SM.getPresumedLoc(Loc);
547 // Check that line directive is pointing at main file.
548 if (Presumed.isValid() && Presumed.getFileID().isInvalid() &&
549 isMainFile(Presumed.getFilename(), SM)) {
550 Loc = SM.translateLineCol(SM.getMainFileID(), Presumed.getLine(),
551 Presumed.getColumn());
552 }
553 }
554 }
555 return Loc;
556}
Sam McCallcf3a5852019-09-04 07:35:00 +0000557} // namespace clangd
558} // namespace clang