clang-tools  14.0.0git
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 "AST.h"
11 #include "Headers.h"
12 #include "index/Relation.h"
13 #include "index/SymbolOrigin.h"
14 #include "support/Logger.h"
15 #include "clang/AST/ASTConsumer.h"
16 #include "clang/AST/ASTContext.h"
17 #include "clang/Basic/SourceLocation.h"
18 #include "clang/Basic/SourceManager.h"
19 #include "clang/Frontend/CompilerInstance.h"
20 #include "clang/Frontend/MultiplexConsumer.h"
21 #include "clang/Index/IndexingAction.h"
22 #include "clang/Index/IndexingOptions.h"
23 #include "clang/Tooling/Tooling.h"
24 #include "llvm/ADT/STLExtras.h"
25 #include <cstddef>
26 #include <functional>
27 #include <memory>
28 #include <utility>
29 
30 namespace clang {
31 namespace clangd {
32 namespace {
33 
34 llvm::Optional<std::string> toURI(const FileEntry *File) {
35  if (!File)
36  return llvm::None;
37  auto AbsolutePath = File->tryGetRealPathName();
38  if (AbsolutePath.empty())
39  return llvm::None;
40  return URI::create(AbsolutePath).toString();
41 }
42 
43 // Collects the nodes and edges of include graph during indexing action.
44 // Important: The graph generated by those callbacks might contain cycles and
45 // self edges.
46 struct IncludeGraphCollector : public PPCallbacks {
47 public:
48  IncludeGraphCollector(const SourceManager &SM, IncludeGraph &IG)
49  : SM(SM), IG(IG) {}
50 
51  // Populates everything except direct includes for a node, which represents
52  // edges in the include graph and populated in inclusion directive.
53  // We cannot populate the fields in InclusionDirective because it does not
54  // have access to the contents of the included file.
55  void FileChanged(SourceLocation Loc, FileChangeReason Reason,
56  SrcMgr::CharacteristicKind FileType,
57  FileID PrevFID) override {
58  // We only need to process each file once. So we don't care about anything
59  // but entries.
60  if (Reason != FileChangeReason::EnterFile)
61  return;
62 
63  const auto FileID = SM.getFileID(Loc);
64  const auto File = SM.getFileEntryForID(FileID);
65  auto URI = toURI(File);
66  if (!URI)
67  return;
68  auto I = IG.try_emplace(*URI).first;
69 
70  auto &Node = I->getValue();
71  // Node has already been populated.
72  if (Node.URI.data() == I->getKeyData()) {
73 #ifndef NDEBUG
74  auto Digest = digestFile(SM, FileID);
75  assert(Digest && Node.Digest == *Digest &&
76  "Same file, different digest?");
77 #endif
78  return;
79  }
80  if (auto Digest = digestFile(SM, FileID))
81  Node.Digest = std::move(*Digest);
82  if (FileID == SM.getMainFileID())
84  Node.URI = I->getKey();
85  }
86 
87  // Add edges from including files to includes.
88  void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
89  llvm::StringRef FileName, bool IsAngled,
90  CharSourceRange FilenameRange, const FileEntry *File,
91  llvm::StringRef SearchPath,
92  llvm::StringRef RelativePath, const Module *Imported,
93  SrcMgr::CharacteristicKind FileType) override {
94  auto IncludeURI = toURI(File);
95  if (!IncludeURI)
96  return;
97 
98  auto IncludingURI = toURI(SM.getFileEntryForID(SM.getFileID(HashLoc)));
99  if (!IncludingURI)
100  return;
101 
102  auto NodeForInclude = IG.try_emplace(*IncludeURI).first->getKey();
103  auto NodeForIncluding = IG.try_emplace(*IncludingURI);
104 
105  NodeForIncluding.first->getValue().DirectIncludes.push_back(NodeForInclude);
106  }
107 
108  // Sanity check to ensure we have already populated a skipped file.
109  void FileSkipped(const FileEntryRef &SkippedFile, const Token &FilenameTok,
110  SrcMgr::CharacteristicKind FileType) override {
111 #ifndef NDEBUG
112  auto URI = toURI(&SkippedFile.getFileEntry());
113  if (!URI)
114  return;
115  auto I = IG.try_emplace(*URI);
116  assert(!I.second && "File inserted for the first time on skip.");
117  assert(I.first->getKeyData() == I.first->getValue().URI.data() &&
118  "Node have not been populated yet");
119 #endif
120  }
121 
122 private:
123  const SourceManager &SM;
124  IncludeGraph &IG;
125 };
126 
127 // Wraps the index action and reports index data after each translation unit.
128 class IndexAction : public ASTFrontendAction {
129 public:
130  IndexAction(std::shared_ptr<SymbolCollector> C,
131  std::unique_ptr<CanonicalIncludes> Includes,
132  const index::IndexingOptions &Opts,
133  std::function<void(SymbolSlab)> SymbolsCallback,
134  std::function<void(RefSlab)> RefsCallback,
135  std::function<void(RelationSlab)> RelationsCallback,
136  std::function<void(IncludeGraph)> IncludeGraphCallback)
137  : SymbolsCallback(SymbolsCallback), RefsCallback(RefsCallback),
138  RelationsCallback(RelationsCallback),
139  IncludeGraphCallback(IncludeGraphCallback), Collector(C),
140  Includes(std::move(Includes)), Opts(Opts),
141  PragmaHandler(collectIWYUHeaderMaps(this->Includes.get())) {
142  this->Opts.ShouldTraverseDecl = [this](const Decl *D) {
143  // Many operations performed during indexing is linear in terms of depth
144  // of the decl (USR generation, name lookups, figuring out role of a
145  // reference are some examples). Since we index all the decls nested
146  // inside, it becomes quadratic. So we give up on nested symbols.
147  if (isDeeplyNested(D))
148  return false;
149  auto &SM = D->getASTContext().getSourceManager();
150  auto FID = SM.getFileID(SM.getExpansionLoc(D->getLocation()));
151  if (!FID.isValid())
152  return true;
153  return Collector->shouldIndexFile(FID);
154  };
155  }
156 
157  std::unique_ptr<ASTConsumer>
158  CreateASTConsumer(CompilerInstance &CI, llvm::StringRef InFile) override {
159  CI.getPreprocessor().addCommentHandler(PragmaHandler.get());
160  Includes->addSystemHeadersMapping(CI.getLangOpts());
161  if (IncludeGraphCallback != nullptr)
162  CI.getPreprocessor().addPPCallbacks(
163  std::make_unique<IncludeGraphCollector>(CI.getSourceManager(), IG));
164 
165  return index::createIndexingASTConsumer(Collector, Opts,
166  CI.getPreprocessorPtr());
167  }
168 
169  bool BeginInvocation(CompilerInstance &CI) override {
170  // We want all comments, not just the doxygen ones.
171  CI.getLangOpts().CommentOpts.ParseAllComments = true;
172  CI.getLangOpts().RetainCommentsFromSystemHeaders = true;
173  // Index the whole file even if there are warnings and -Werror is set.
174  // Avoids some analyses too. Set in two places as we're late to the party.
175  CI.getDiagnosticOpts().IgnoreWarnings = true;
176  CI.getDiagnostics().setIgnoreAllWarnings(true);
177  // Instruct the parser to ask our ASTConsumer if it should skip function
178  // bodies. The ASTConsumer will take care of skipping only functions inside
179  // the files that we have already processed.
180  CI.getFrontendOpts().SkipFunctionBodies = true;
181  return true;
182  }
183 
184  void EndSourceFileAction() override {
185  SymbolsCallback(Collector->takeSymbols());
186  if (RefsCallback != nullptr)
187  RefsCallback(Collector->takeRefs());
188  if (RelationsCallback != nullptr)
189  RelationsCallback(Collector->takeRelations());
190  if (IncludeGraphCallback != nullptr) {
191 #ifndef NDEBUG
192  // This checks if all nodes are initialized.
193  for (const auto &Node : IG)
194  assert(Node.getKeyData() == Node.getValue().URI.data());
195 #endif
196  IncludeGraphCallback(std::move(IG));
197  }
198  }
199 
200 private:
201  std::function<void(SymbolSlab)> SymbolsCallback;
202  std::function<void(RefSlab)> RefsCallback;
203  std::function<void(RelationSlab)> RelationsCallback;
204  std::function<void(IncludeGraph)> IncludeGraphCallback;
205  std::shared_ptr<SymbolCollector> Collector;
206  std::unique_ptr<CanonicalIncludes> Includes;
207  index::IndexingOptions Opts;
208  std::unique_ptr<CommentHandler> PragmaHandler;
209  IncludeGraph IG;
210 };
211 
212 } // namespace
213 
214 std::unique_ptr<FrontendAction> createStaticIndexingAction(
216  std::function<void(SymbolSlab)> SymbolsCallback,
217  std::function<void(RefSlab)> RefsCallback,
218  std::function<void(RelationSlab)> RelationsCallback,
219  std::function<void(IncludeGraph)> IncludeGraphCallback) {
220  index::IndexingOptions IndexOpts;
221  IndexOpts.SystemSymbolFilter =
222  index::IndexingOptions::SystemSymbolFilterKind::All;
223  // We index function-local classes and its member functions only.
224  IndexOpts.IndexFunctionLocals = true;
225  Opts.CollectIncludePath = true;
226  if (Opts.Origin == SymbolOrigin::Unknown)
227  Opts.Origin = SymbolOrigin::Static;
228  Opts.StoreAllDocumentation = false;
229  if (RefsCallback != nullptr) {
230  Opts.RefFilter = RefKind::All;
231  Opts.RefsInHeaders = true;
232  }
233  auto Includes = std::make_unique<CanonicalIncludes>();
234  Opts.Includes = Includes.get();
235  return std::make_unique<IndexAction>(
236  std::make_shared<SymbolCollector>(std::move(Opts)), std::move(Includes),
237  IndexOpts, SymbolsCallback, RefsCallback, RelationsCallback,
238  IncludeGraphCallback);
239 }
240 
241 } // namespace clangd
242 } // namespace clang
Loc
SourceLocation Loc
Definition: KernelNameRestrictionCheck.cpp:45
clang::clangd::collectIWYUHeaderMaps
std::unique_ptr< CommentHandler > collectIWYUHeaderMaps(CanonicalIncludes *Includes)
Returns a CommentHandler that parses pragma comment on include files to determine when we should incl...
Definition: CanonicalIncludes.cpp:59
Headers.h
IndexAction.h
SymbolOrigin.h
clang::clangd::RefKind::All
@ All
CI
std::unique_ptr< CompilerInvocation > CI
Definition: TUScheduler.cpp:456
clang::clangd::URI::create
static llvm::Expected< URI > create(llvm::StringRef AbsolutePath, llvm::StringRef Scheme)
Creates a URI for a file in the given scheme.
Definition: URI.cpp:211
clang::clangd::IncludeGraphNode::SourceFlag::IsTU
@ IsTU
clang::clangd::RefSlab
An efficient structure of storing large set of symbol references in memory.
Definition: Ref.h:110
PragmaHandler
CommentHandler * PragmaHandler
Definition: SymbolCollectorTests.cpp:259
Relation.h
ns1::ns2::D
@ D
Definition: CategoricalFeature.h:3
clang::clangd::SymbolOrigin::Static
@ Static
Decl
const FunctionDecl * Decl
Definition: AvoidBindCheck.cpp:100
clang::clangd::RelationSlab
Definition: Relation.h:52
Logger.h
FileName
StringRef FileName
Definition: KernelNameRestrictionCheck.cpp:46
IsAngled
bool IsAngled
true if this was an include with angle brackets
Definition: IncludeOrderCheck.cpp:40
Collector
std::shared_ptr< SymbolCollector > Collector
Definition: SymbolCollectorTests.cpp:257
clang::clangd::isDeeplyNested
bool isDeeplyNested(const Decl *D, unsigned MaxDepth)
Checks whether D is more than MaxDepth away from translation unit scope.
Definition: AST.cpp:556
clang::clangd::digestFile
llvm::Optional< FileDigest > digestFile(const SourceManager &SM, FileID FID)
Definition: SourceCode.cpp:572
C
const Criteria C
Definition: FunctionCognitiveComplexityCheck.cpp:93
PPCallbacks
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::clangd::SymbolCollector::Options
Definition: SymbolCollector.h:48
clang::clangd::IncludeGraph
llvm::StringMap< IncludeGraphNode > IncludeGraph
Definition: Headers.h:92
clang::clangd::SymbolOrigin::Unknown
@ Unknown
clang::clangd::SymbolSlab
An immutable symbol container that stores a set of symbols.
Definition: Symbol.h:177
clang::clangd::createStaticIndexingAction
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)
Definition: IndexAction.cpp:214
AST.h