clang-tools  10.0.0svn
Preamble.cpp
Go to the documentation of this file.
1 //===--- 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 
16 namespace clang {
17 namespace clangd {
18 namespace {
19 
20 bool 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 class CppFilePreambleCallbacks : public PreambleCallbacks {
28 public:
29  CppFilePreambleCallbacks(PathRef File, PreambleParsedCallback ParsedCallback)
30  : File(File), ParsedCallback(ParsedCallback) {}
31 
32  IncludeStructure takeIncludes() { return std::move(Includes); }
33 
34  MainFileMacros takeMacros() { return std::move(Macros); }
35 
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 {
46  CanonIncludes.addSystemHeadersMapping(CI.getLangOpts());
47  LangOpts = &CI.getLangOpts();
48  SourceMgr = &CI.getSourceManager();
49  }
50 
51  std::unique_ptr<PPCallbacks> createPPCallbacks() override {
52  assert(SourceMgr && LangOpts &&
53  "SourceMgr and LangOpts must be set at this point");
54 
55  return std::make_unique<PPChainedCallbacks>(
56  collectIncludeStructureCallback(*SourceMgr, &Includes),
57  std::make_unique<CollectMainFileMacros>(*SourceMgr, *LangOpts, Macros));
58  }
59 
60  CommentHandler *getCommentHandler() override {
61  IWYUHandler = collectIWYUHeaderMaps(&CanonIncludes);
62  return IWYUHandler.get();
63  }
64 
65 private:
66  PathRef File;
67  PreambleParsedCallback ParsedCallback;
68  IncludeStructure Includes;
69  CanonicalIncludes CanonIncludes;
70  MainFileMacros Macros;
71  std::unique_ptr<CommentHandler> IWYUHandler = nullptr;
72  const clang::LangOptions *LangOpts = nullptr;
73  const SourceManager *SourceMgr = nullptr;
74 };
75 
76 } // namespace
77 
79  std::vector<Diag> Diags, IncludeStructure Includes,
80  MainFileMacros Macros,
81  std::unique_ptr<PreambleFileStatusCache> StatCache,
82  CanonicalIncludes CanonIncludes)
83  : Preamble(std::move(Preamble)), Diags(std::move(Diags)),
84  Includes(std::move(Includes)), Macros(std::move(Macros)),
85  StatCache(std::move(StatCache)), CanonIncludes(std::move(CanonIncludes)) {
86 }
87 
88 std::shared_ptr<const PreambleData>
89 buildPreamble(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(),
153  SerializedDeclsCollector.takeMacros(), std::move(StatCache),
154  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
std::unique_ptr< CommentHandler > collectIWYUHeaderMaps(CanonicalIncludes *Includes)
Returns a CommentHandler that parses pragma comment on include files to determine when we should incl...
PreambleData(PrecompiledPreamble Preamble, std::vector< Diag > Diags, IncludeStructure Includes, MainFileMacros Macros, std::unique_ptr< PreambleFileStatusCache > StatCache, CanonicalIncludes CanonIncludes)
Definition: Preamble.cpp:78
StoreDiags collects the diagnostics that can later be reported by clangd.
Definition: Diagnostics.h:119
llvm::StringRef PathRef
A typedef to represent a ref to file path.
Definition: Path.h:23
void vlog(const char *Fmt, Ts &&... Vals)
Definition: Logger.h:67
void elog(const char *Fmt, Ts &&... Vals)
Definition: Logger.h:56
std::shared_ptr< const PreambleData > buildPreamble(PathRef FileName, CompilerInvocation &CI, std::shared_ptr< const PreambleData > OldPreamble, const tooling::CompileCommand &OldCompileCommand, const ParseInputs &Inputs, bool StoreInMemory, PreambleParsedCallback PreambleCallback)
Build a preamble for the new inputs unless an old one can be reused.
Definition: Preamble.cpp:89
Maps a definition location onto an #include file, based on a set of filename rules.
std::unique_ptr< PreambleFileStatusCache > StatCache
Definition: Preamble.h:63
void log(const char *Fmt, Ts &&... Vals)
Definition: Logger.h:62
tooling::CompileCommand CompileCommand
Definition: Compiler.h:45
PathRef FileName
std::function< void(ASTContext &, std::shared_ptr< clang::Preprocessor >, const CanonicalIncludes &)> PreambleParsedCallback
Definition: Preamble.h:69
Information required to run clang, e.g. to parse AST or do code completion.
Definition: Compiler.h:44
const PreambleData * Preamble
std::unique_ptr< PPCallbacks > collectIncludeStructureCallback(const SourceManager &SM, IncludeStructure *Out)
Returns a PPCallback that visits all inclusions in the main file.
Definition: Headers.cpp:113
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
std::vector< Diag > Diags
Definition: Preamble.h:53
IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS
Definition: Compiler.h:46
Records an event whose duration is the lifetime of the Span object.
Definition: Trace.h:81
#define SPAN_ATTACH(S, Name, Expr)
Attach a key-value pair to a Span event.
Definition: Trace.h:97
std::vector< Diag > take(const clang::tidy::ClangTidyContext *Tidy=nullptr)