blob: 97cd22a5d1fc3550cea3b47d8c064b381a34776b [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>
Kadir Cetinkaya276a95b2020-03-13 11:52:19 +010092buildPreamble(PathRef FileName, CompilerInvocation CI,
Sam McCallcf3a5852019-09-04 07:35:00 +000093 const ParseInputs &Inputs, bool StoreInMemory,
94 PreambleParsedCallback PreambleCallback) {
95 // Note that we don't need to copy the input contents, preamble can live
96 // without those.
97 auto ContentsBuffer =
98 llvm::MemoryBuffer::getMemBuffer(Inputs.Contents, FileName);
99 auto Bounds =
100 ComputePreambleBounds(*CI.getLangOpts(), ContentsBuffer.get(), 0);
101
Sam McCallcf3a5852019-09-04 07:35:00 +0000102 trace::Span Tracer("BuildPreamble");
103 SPAN_ATTACH(Tracer, "File", FileName);
104 StoreDiags PreambleDiagnostics;
105 llvm::IntrusiveRefCntPtr<DiagnosticsEngine> PreambleDiagsEngine =
106 CompilerInstance::createDiagnostics(&CI.getDiagnosticOpts(),
107 &PreambleDiagnostics, false);
108
109 // Skip function bodies when building the preamble to speed up building
110 // the preamble and make it smaller.
111 assert(!CI.getFrontendOpts().SkipFunctionBodies);
112 CI.getFrontendOpts().SkipFunctionBodies = true;
113 // We don't want to write comment locations into PCH. They are racy and slow
114 // to read back. We rely on dynamic index for the comments instead.
115 CI.getPreprocessorOpts().WriteCommentListToPCH = false;
116
Haojian Wu72439b62020-03-31 16:09:49 +0200117 // Recovery expression currently only works for C++.
118 if (CI.getLangOpts()->CPlusPlus)
119 CI.getLangOpts()->RecoveryAST = Inputs.Opts.BuildRecoveryAST;
120
Sam McCallcf3a5852019-09-04 07:35:00 +0000121 CppFilePreambleCallbacks SerializedDeclsCollector(FileName, PreambleCallback);
122 if (Inputs.FS->setCurrentWorkingDirectory(Inputs.CompileCommand.Directory)) {
123 log("Couldn't set working directory when building the preamble.");
124 // We proceed anyway, our lit-tests rely on results for non-existing working
125 // dirs.
126 }
127
128 llvm::SmallString<32> AbsFileName(FileName);
129 Inputs.FS->makeAbsolute(AbsFileName);
130 auto StatCache = std::make_unique<PreambleFileStatusCache>(AbsFileName);
131 auto BuiltPreamble = PrecompiledPreamble::Build(
132 CI, ContentsBuffer.get(), Bounds, *PreambleDiagsEngine,
133 StatCache->getProducingFS(Inputs.FS),
134 std::make_shared<PCHContainerOperations>(), StoreInMemory,
135 SerializedDeclsCollector);
136
137 // When building the AST for the main file, we do want the function
138 // bodies.
139 CI.getFrontendOpts().SkipFunctionBodies = false;
140
141 if (BuiltPreamble) {
Sam McCall2cd33e62020-03-04 00:33:29 +0100142 vlog("Built preamble of size {0} for file {1} version {2}",
143 BuiltPreamble->getSize(), FileName, Inputs.Version);
Sam McCallcf3a5852019-09-04 07:35:00 +0000144 std::vector<Diag> Diags = PreambleDiagnostics.take();
145 return std::make_shared<PreambleData>(
Kadir Cetinkayaecd3e672020-03-11 16:34:01 +0100146 Inputs, std::move(*BuiltPreamble), std::move(Diags),
Sam McCallcf3a5852019-09-04 07:35:00 +0000147 SerializedDeclsCollector.takeIncludes(),
Haojian Wu7e3c74b2019-09-24 11:14:06 +0000148 SerializedDeclsCollector.takeMacros(), std::move(StatCache),
Sam McCallcf3a5852019-09-04 07:35:00 +0000149 SerializedDeclsCollector.takeCanonicalIncludes());
150 } else {
Adam Czachorowski55b92dc2020-03-19 15:09:28 +0100151 elog("Could not build a preamble for file {0} version {1}", FileName,
Sam McCall2cd33e62020-03-04 00:33:29 +0100152 Inputs.Version);
Sam McCallcf3a5852019-09-04 07:35:00 +0000153 return nullptr;
154 }
155}
156
Kadir Cetinkayac31367e2020-03-15 21:43:00 +0100157bool isPreambleCompatible(const PreambleData &Preamble,
158 const ParseInputs &Inputs, PathRef FileName,
159 const CompilerInvocation &CI) {
160 auto ContentsBuffer =
161 llvm::MemoryBuffer::getMemBuffer(Inputs.Contents, FileName);
162 auto Bounds =
163 ComputePreambleBounds(*CI.getLangOpts(), ContentsBuffer.get(), 0);
164 return compileCommandsAreEqual(Inputs.CompileCommand,
165 Preamble.CompileCommand) &&
166 Preamble.Preamble.CanReuse(CI, ContentsBuffer.get(), Bounds,
167 Inputs.FS.get());
168}
Sam McCallcf3a5852019-09-04 07:35:00 +0000169} // namespace clangd
170} // namespace clang