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