clang-tools 18.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 "clang-include-cleaner/Record.h"
13#include "index/Relation.h"
15#include "index/SymbolOrigin.h"
16#include "clang/AST/ASTConsumer.h"
17#include "clang/AST/ASTContext.h"
18#include "clang/Basic/SourceLocation.h"
19#include "clang/Basic/SourceManager.h"
20#include "clang/Frontend/CompilerInstance.h"
21#include "clang/Frontend/FrontendAction.h"
22#include "clang/Index/IndexingAction.h"
23#include "clang/Index/IndexingOptions.h"
24#include <cstddef>
25#include <functional>
26#include <memory>
27#include <optional>
28#include <utility>
29
30namespace clang {
31namespace clangd {
32namespace {
33
34std::optional<std::string> toURI(OptionalFileEntryRef File) {
35 if (!File)
36 return std::nullopt;
37 auto AbsolutePath = File->getFileEntry().tryGetRealPathName();
38 if (AbsolutePath.empty())
39 return std::nullopt;
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.
46struct IncludeGraphCollector : public PPCallbacks {
47public:
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 auto File = SM.getFileEntryRefForID(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,
91 OptionalFileEntryRef File, 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.getFileEntryRefForID(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);
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
122private:
123 const SourceManager &SM;
124 IncludeGraph &IG;
125};
126
127// Wraps the index action and reports index data after each translation unit.
128class IndexAction : public ASTFrontendAction {
129public:
130 IndexAction(std::shared_ptr<SymbolCollector> C,
131 std::unique_ptr<include_cleaner::PragmaIncludes> PI,
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 PI(std::move(PI)), Opts(Opts) {
141 this->Opts.ShouldTraverseDecl = [this](const Decl *D) {
142 // Many operations performed during indexing is linear in terms of depth
143 // of the decl (USR generation, name lookups, figuring out role of a
144 // reference are some examples). Since we index all the decls nested
145 // inside, it becomes quadratic. So we give up on nested symbols.
146 if (isDeeplyNested(D))
147 return false;
148 auto &SM = D->getASTContext().getSourceManager();
149 auto FID = SM.getFileID(SM.getExpansionLoc(D->getLocation()));
150 if (!FID.isValid())
151 return true;
152 return Collector->shouldIndexFile(FID);
153 };
154 }
155
156 std::unique_ptr<ASTConsumer>
157 CreateASTConsumer(CompilerInstance &CI, llvm::StringRef InFile) override {
158 PI->record(CI.getPreprocessor());
159 if (IncludeGraphCallback != nullptr)
160 CI.getPreprocessor().addPPCallbacks(
161 std::make_unique<IncludeGraphCollector>(CI.getSourceManager(), IG));
162
163 return index::createIndexingASTConsumer(Collector, Opts,
164 CI.getPreprocessorPtr());
165 }
166
167 bool BeginInvocation(CompilerInstance &CI) override {
168 // We want all comments, not just the doxygen ones.
169 CI.getLangOpts().CommentOpts.ParseAllComments = true;
170 CI.getLangOpts().RetainCommentsFromSystemHeaders = true;
171 // Index the whole file even if there are warnings and -Werror is set.
172 // Avoids some analyses too. Set in two places as we're late to the party.
173 CI.getDiagnosticOpts().IgnoreWarnings = true;
174 CI.getDiagnostics().setIgnoreAllWarnings(true);
175 // Instruct the parser to ask our ASTConsumer if it should skip function
176 // bodies. The ASTConsumer will take care of skipping only functions inside
177 // the files that we have already processed.
178 CI.getFrontendOpts().SkipFunctionBodies = true;
179 return true;
180 }
181
182 void EndSourceFileAction() override {
183 SymbolsCallback(Collector->takeSymbols());
184 if (RefsCallback != nullptr)
185 RefsCallback(Collector->takeRefs());
186 if (RelationsCallback != nullptr)
187 RelationsCallback(Collector->takeRelations());
188 if (IncludeGraphCallback != nullptr) {
189#ifndef NDEBUG
190 // This checks if all nodes are initialized.
191 for (const auto &Node : IG)
192 assert(Node.getKeyData() == Node.getValue().URI.data());
193#endif
194 IncludeGraphCallback(std::move(IG));
195 }
196 }
197
198private:
199 std::function<void(SymbolSlab)> SymbolsCallback;
200 std::function<void(RefSlab)> RefsCallback;
201 std::function<void(RelationSlab)> RelationsCallback;
202 std::function<void(IncludeGraph)> IncludeGraphCallback;
203 std::shared_ptr<SymbolCollector> Collector;
204 std::unique_ptr<include_cleaner::PragmaIncludes> PI;
205 index::IndexingOptions Opts;
206 IncludeGraph IG;
207};
208
209} // namespace
210
211std::unique_ptr<FrontendAction> createStaticIndexingAction(
213 std::function<void(SymbolSlab)> SymbolsCallback,
214 std::function<void(RefSlab)> RefsCallback,
215 std::function<void(RelationSlab)> RelationsCallback,
216 std::function<void(IncludeGraph)> IncludeGraphCallback) {
217 index::IndexingOptions IndexOpts;
218 IndexOpts.SystemSymbolFilter =
219 index::IndexingOptions::SystemSymbolFilterKind::All;
220 // We index function-local classes and its member functions only.
221 IndexOpts.IndexFunctionLocals = true;
222 Opts.CollectIncludePath = true;
223 if (Opts.Origin == SymbolOrigin::Unknown)
224 Opts.Origin = SymbolOrigin::Static;
225 Opts.StoreAllDocumentation = false;
226 if (RefsCallback != nullptr) {
227 Opts.RefFilter = RefKind::All;
228 Opts.RefsInHeaders = true;
229 }
230 auto PragmaIncludes = std::make_unique<include_cleaner::PragmaIncludes>();
231 Opts.PragmaIncludes = PragmaIncludes.get();
232 return std::make_unique<IndexAction>(std::make_shared<SymbolCollector>(Opts),
233 std::move(PragmaIncludes), IndexOpts,
234 SymbolsCallback, RefsCallback,
235 RelationsCallback, IncludeGraphCallback);
236}
237
238} // namespace clangd
239} // namespace clang
const FunctionDecl * Decl
const Criteria C
bool IsAngled
true if this was an include with angle brackets
StringRef FileName
SourceLocation Loc
::clang::DynTypedNode Node
std::unique_ptr< CompilerInvocation > CI
An efficient structure of storing large set of symbol references in memory.
Definition: Ref.h:108
An immutable symbol container that stores a set of symbols.
Definition: Symbol.h:199
static llvm::Expected< URI > create(llvm::StringRef AbsolutePath, llvm::StringRef Scheme)
Creates a URI for a file in the given scheme.
Definition: URI.cpp:209
std::optional< FileDigest > digestFile(const SourceManager &SM, FileID FID)
Definition: SourceCode.cpp:571
llvm::StringMap< IncludeGraphNode > IncludeGraph
Definition: Headers.h:101
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)
bool isDeeplyNested(const Decl *D, unsigned MaxDepth)
Checks whether D is more than MaxDepth away from translation unit scope.
Definition: AST.cpp:721
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//