clang-tools  9.0.0
IndexAction.cpp
Go to the documentation of this file.
1 //===--- IndexAction.cpp -----------------------------------------*- C++-*-===//
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 "IndexAction.h"
10 #include "Headers.h"
11 #include "Logger.h"
12 #include "index/Relation.h"
13 #include "index/SymbolOrigin.h"
14 #include "clang/Frontend/CompilerInstance.h"
15 #include "clang/Index/IndexingAction.h"
16 #include "clang/Tooling/Tooling.h"
17 #include <utility>
18 
19 namespace clang {
20 namespace clangd {
21 namespace {
22 
23 llvm::Optional<std::string> toURI(const FileEntry *File) {
24  if (!File)
25  return llvm::None;
26  auto AbsolutePath = File->tryGetRealPathName();
27  if (AbsolutePath.empty())
28  return llvm::None;
29  return URI::create(AbsolutePath).toString();
30 }
31 
32 // Collects the nodes and edges of include graph during indexing action.
33 // Important: The graph generated by those callbacks might contain cycles and
34 // self edges.
35 struct IncludeGraphCollector : public PPCallbacks {
36 public:
37  IncludeGraphCollector(const SourceManager &SM, IncludeGraph &IG)
38  : SM(SM), IG(IG) {}
39 
40  // Populates everything except direct includes for a node, which represents
41  // edges in the include graph and populated in inclusion directive.
42  // We cannot populate the fields in InclusionDirective because it does not
43  // have access to the contents of the included file.
44  void FileChanged(SourceLocation Loc, FileChangeReason Reason,
45  SrcMgr::CharacteristicKind FileType,
46  FileID PrevFID) override {
47  // We only need to process each file once. So we don't care about anything
48  // but entries.
49  if (Reason != FileChangeReason::EnterFile)
50  return;
51 
52  const auto FileID = SM.getFileID(Loc);
53  const auto File = SM.getFileEntryForID(FileID);
54  auto URI = toURI(File);
55  if (!URI)
56  return;
57  auto I = IG.try_emplace(*URI).first;
58 
59  auto &Node = I->getValue();
60  // Node has already been populated.
61  if (Node.URI.data() == I->getKeyData()) {
62 #ifndef NDEBUG
63  auto Digest = digestFile(SM, FileID);
64  assert(Digest && Node.Digest == *Digest &&
65  "Same file, different digest?");
66 #endif
67  return;
68  }
69  if (auto Digest = digestFile(SM, FileID))
70  Node.Digest = std::move(*Digest);
71  if (FileID == SM.getMainFileID())
73  Node.URI = I->getKey();
74  }
75 
76  // Add edges from including files to includes.
77  void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
78  llvm::StringRef FileName, bool IsAngled,
79  CharSourceRange FilenameRange, const FileEntry *File,
80  llvm::StringRef SearchPath,
81  llvm::StringRef RelativePath, const Module *Imported,
82  SrcMgr::CharacteristicKind FileType) override {
83  auto IncludeURI = toURI(File);
84  if (!IncludeURI)
85  return;
86 
87  auto IncludingURI = toURI(SM.getFileEntryForID(SM.getFileID(HashLoc)));
88  if (!IncludingURI)
89  return;
90 
91  auto NodeForInclude = IG.try_emplace(*IncludeURI).first->getKey();
92  auto NodeForIncluding = IG.try_emplace(*IncludingURI);
93 
94  NodeForIncluding.first->getValue().DirectIncludes.push_back(NodeForInclude);
95  }
96 
97  // Sanity check to ensure we have already populated a skipped file.
98  void FileSkipped(const FileEntry &SkippedFile, const Token &FilenameTok,
99  SrcMgr::CharacteristicKind FileType) override {
100 #ifndef NDEBUG
101  auto URI = toURI(&SkippedFile);
102  if (!URI)
103  return;
104  auto I = IG.try_emplace(*URI);
105  assert(!I.second && "File inserted for the first time on skip.");
106  assert(I.first->getKeyData() == I.first->getValue().URI.data() &&
107  "Node have not been populated yet");
108 #endif
109  }
110 
111 private:
112  const SourceManager &SM;
113  IncludeGraph &IG;
114 };
115 
116 // Wraps the index action and reports index data after each translation unit.
117 class IndexAction : public WrapperFrontendAction {
118 public:
119  IndexAction(std::shared_ptr<SymbolCollector> C,
120  std::unique_ptr<CanonicalIncludes> Includes,
121  const index::IndexingOptions &Opts,
122  std::function<void(SymbolSlab)> SymbolsCallback,
123  std::function<void(RefSlab)> RefsCallback,
124  std::function<void(RelationSlab)> RelationsCallback,
125  std::function<void(IncludeGraph)> IncludeGraphCallback)
126  : WrapperFrontendAction(index::createIndexingAction(C, Opts, nullptr)),
127  SymbolsCallback(SymbolsCallback), RefsCallback(RefsCallback),
128  RelationsCallback(RelationsCallback),
129  IncludeGraphCallback(IncludeGraphCallback), Collector(C),
130  Includes(std::move(Includes)),
131  PragmaHandler(collectIWYUHeaderMaps(this->Includes.get())) {}
132 
133  std::unique_ptr<ASTConsumer>
134  CreateASTConsumer(CompilerInstance &CI, llvm::StringRef InFile) override {
135  CI.getPreprocessor().addCommentHandler(PragmaHandler.get());
136  addSystemHeadersMapping(Includes.get(), CI.getLangOpts());
137  if (IncludeGraphCallback != nullptr)
138  CI.getPreprocessor().addPPCallbacks(
139  llvm::make_unique<IncludeGraphCollector>(CI.getSourceManager(), IG));
140  return WrapperFrontendAction::CreateASTConsumer(CI, InFile);
141  }
142 
143  bool BeginInvocation(CompilerInstance &CI) override {
144  // We want all comments, not just the doxygen ones.
145  CI.getLangOpts().CommentOpts.ParseAllComments = true;
146  // Index the whole file even if there are warnings and -Werror is set.
147  // Avoids some analyses too. Set in two places as we're late to the party.
148  CI.getDiagnosticOpts().IgnoreWarnings = true;
149  CI.getDiagnostics().setIgnoreAllWarnings(true);
150 
151  return WrapperFrontendAction::BeginInvocation(CI);
152  }
153 
154  void EndSourceFileAction() override {
155  WrapperFrontendAction::EndSourceFileAction();
156 
157  SymbolsCallback(Collector->takeSymbols());
158  if (RefsCallback != nullptr)
159  RefsCallback(Collector->takeRefs());
160  if (RelationsCallback != nullptr)
161  RelationsCallback(Collector->takeRelations());
162  if (IncludeGraphCallback != nullptr) {
163 #ifndef NDEBUG
164  // This checks if all nodes are initialized.
165  for (const auto &Node : IG)
166  assert(Node.getKeyData() == Node.getValue().URI.data());
167 #endif
168  IncludeGraphCallback(std::move(IG));
169  }
170  }
171 
172 private:
173  std::function<void(SymbolSlab)> SymbolsCallback;
174  std::function<void(RefSlab)> RefsCallback;
175  std::function<void(RelationSlab)> RelationsCallback;
176  std::function<void(IncludeGraph)> IncludeGraphCallback;
177  std::shared_ptr<SymbolCollector> Collector;
178  std::unique_ptr<CanonicalIncludes> Includes;
179  std::unique_ptr<CommentHandler> PragmaHandler;
180  IncludeGraph IG;
181 };
182 
183 } // namespace
184 
185 std::unique_ptr<FrontendAction> createStaticIndexingAction(
187  std::function<void(SymbolSlab)> SymbolsCallback,
188  std::function<void(RefSlab)> RefsCallback,
189  std::function<void(RelationSlab)> RelationsCallback,
190  std::function<void(IncludeGraph)> IncludeGraphCallback) {
191  index::IndexingOptions IndexOpts;
192  IndexOpts.SystemSymbolFilter =
193  index::IndexingOptions::SystemSymbolFilterKind::All;
194  Opts.CollectIncludePath = true;
195  if (Opts.Origin == SymbolOrigin::Unknown)
197  Opts.StoreAllDocumentation = false;
198  if (RefsCallback != nullptr) {
199  Opts.RefFilter = RefKind::All;
200  Opts.RefsInHeaders = true;
201  }
202  auto Includes = llvm::make_unique<CanonicalIncludes>();
203  Opts.Includes = Includes.get();
204  return llvm::make_unique<IndexAction>(
205  std::make_shared<SymbolCollector>(std::move(Opts)), std::move(Includes),
206  IndexOpts, SymbolsCallback, RefsCallback, RelationsCallback,
207  IncludeGraphCallback);
208 }
209 
210 } // namespace clangd
211 } // 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...
SourceLocation Loc
&#39;#&#39; location in the include directive
An immutable symbol container that stores a set of symbols.
Definition: Symbol.h:177
An efficient structure of storing large set of symbol references in memory.
Definition: Ref.h:69
void addSystemHeadersMapping(CanonicalIncludes *Includes, const LangOptions &Language)
Adds mapping for system headers and some special symbols (e.g.
Documents should not be synced at all.
llvm::StringMap< IncludeGraphNode > IncludeGraph
Definition: Headers.h:82
bool StoreAllDocumentation
If set to true, SymbolCollector will collect doc for all symbols.
bool IsAngled
true if this was an include with angle brackets
std::shared_ptr< SymbolCollector > Collector
PathRef FileName
llvm::Optional< FileDigest > digestFile(const SourceManager &SM, FileID FID)
Definition: SourceCode.cpp:477
static llvm::Expected< URI > create(llvm::StringRef AbsolutePath, llvm::StringRef Scheme)
Creates a URI for a file in the given scheme.
Definition: URI.cpp:186
bool RefsInHeaders
If set to true, SymbolCollector will collect all refs (from main file and included headers); otherwis...
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
std::unique_ptr< FrontendAction > createStaticIndexingAction(SymbolCollector::Options Opts, std::function< void(SymbolSlab)> SymbolsCallback, std::function< void(RefSlab)> RefsCallback, std::function< void(RelationSlab)> RelationsCallback, std::function< void(IncludeGraph)> IncludeGraphCallback)
const CanonicalIncludes * Includes
If set, this is used to map symbol #include path to a potentially different #include path...
RefKind RefFilter
The symbol ref kinds that will be collected.
CommentHandler * PragmaHandler