clang-tools 22.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 <functional>
25#include <memory>
26#include <optional>
27#include <utility>
28
29namespace clang {
30namespace clangd {
31namespace {
32
33std::optional<std::string> toURI(OptionalFileEntryRef File) {
34 if (!File)
35 return std::nullopt;
36 auto AbsolutePath = File->getFileEntry().tryGetRealPathName();
37 if (AbsolutePath.empty())
38 return std::nullopt;
39 return URI::create(AbsolutePath).toString();
40}
41
42// Collects the nodes and edges of include graph during indexing action.
43// Important: The graph generated by those callbacks might contain cycles and
44// self edges.
45struct IncludeGraphCollector : public PPCallbacks {
46public:
47 IncludeGraphCollector(const SourceManager &SM, IncludeGraph &IG)
48 : SM(SM), IG(IG) {}
49
50 // Populates everything except direct includes for a node, which represents
51 // edges in the include graph and populated in inclusion directive.
52 // We cannot populate the fields in InclusionDirective because it does not
53 // have access to the contents of the included file.
54 void FileChanged(SourceLocation Loc, FileChangeReason Reason,
55 SrcMgr::CharacteristicKind FileType,
56 FileID PrevFID) override {
57 // We only need to process each file once. So we don't care about anything
58 // but entries.
59 if (Reason != FileChangeReason::EnterFile)
60 return;
61
62 const auto FileID = SM.getFileID(Loc);
63 auto File = SM.getFileEntryRefForID(FileID);
64 auto URI = toURI(File);
65 if (!URI)
66 return;
67 auto I = IG.try_emplace(*URI).first;
68
69 auto &Node = I->getValue();
70 // Node has already been populated.
71 if (Node.URI.data() == I->getKeyData()) {
72#ifndef NDEBUG
73 auto Digest = digestFile(SM, FileID);
74 assert(Digest && Node.Digest == *Digest &&
75 "Same file, different digest?");
76#endif
77 return;
78 }
79 if (auto Digest = digestFile(SM, FileID))
80 Node.Digest = std::move(*Digest);
81 if (FileID == SM.getMainFileID())
83 Node.URI = I->getKey();
84 }
85
86 // Add edges from including files to includes.
87 void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
88 llvm::StringRef FileName, bool IsAngled,
89 CharSourceRange FilenameRange,
90 OptionalFileEntryRef File, llvm::StringRef SearchPath,
91 llvm::StringRef RelativePath,
92 const Module *SuggestedModule, bool ModuleImported,
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 // If D is a likely forwarding function we need the body to index indirect
149 // constructor calls (e.g. `make_unique`)
150 if (auto *FT = llvm::dyn_cast<clang::FunctionTemplateDecl>(D);
152 return true;
153 auto &SM = D->getASTContext().getSourceManager();
154 auto FID = SM.getFileID(SM.getExpansionLoc(D->getLocation()));
155 if (!FID.isValid())
156 return true;
157 return Collector->shouldIndexFile(FID);
158 };
159 }
160
161 std::unique_ptr<ASTConsumer>
162 CreateASTConsumer(CompilerInstance &CI, llvm::StringRef InFile) override {
163 PI->record(CI.getPreprocessor());
164 if (IncludeGraphCallback != nullptr)
165 CI.getPreprocessor().addPPCallbacks(
166 std::make_unique<IncludeGraphCollector>(CI.getSourceManager(), IG));
167
168 return index::createIndexingASTConsumer(Collector, Opts,
169 CI.getPreprocessorPtr());
170 }
171
172 bool BeginInvocation(CompilerInstance &CI) override {
173 // We want all comments, not just the doxygen ones.
174 CI.getLangOpts().CommentOpts.ParseAllComments = true;
175 CI.getLangOpts().RetainCommentsFromSystemHeaders = true;
176 // Index the whole file even if there are warnings and -Werror is set.
177 // Avoids some analyses too. Set in two places as we're late to the party.
178 CI.getDiagnosticOpts().IgnoreWarnings = true;
179 CI.getDiagnostics().setIgnoreAllWarnings(true);
180 // Instruct the parser to ask our ASTConsumer if it should skip function
181 // bodies. The ASTConsumer will take care of skipping only functions inside
182 // the files that we have already processed.
183 CI.getFrontendOpts().SkipFunctionBodies = true;
184 return true;
185 }
186
187 void EndSourceFileAction() override {
188 SymbolsCallback(Collector->takeSymbols());
189 if (RefsCallback != nullptr)
190 RefsCallback(Collector->takeRefs());
191 if (RelationsCallback != nullptr)
192 RelationsCallback(Collector->takeRelations());
193 if (IncludeGraphCallback != nullptr) {
194#ifndef NDEBUG
195 // This checks if all nodes are initialized.
196 for (const auto &Node : IG)
197 assert(Node.getKeyData() == Node.getValue().URI.data());
198#endif
199 IncludeGraphCallback(std::move(IG));
200 }
201 }
202
203private:
204 std::function<void(SymbolSlab)> SymbolsCallback;
205 std::function<void(RefSlab)> RefsCallback;
206 std::function<void(RelationSlab)> RelationsCallback;
207 std::function<void(IncludeGraph)> IncludeGraphCallback;
208 std::shared_ptr<SymbolCollector> Collector;
209 std::unique_ptr<include_cleaner::PragmaIncludes> PI;
210 index::IndexingOptions Opts;
211 IncludeGraph IG;
212};
213
214} // namespace
215
216std::unique_ptr<FrontendAction> createStaticIndexingAction(
218 std::function<void(SymbolSlab)> SymbolsCallback,
219 std::function<void(RefSlab)> RefsCallback,
220 std::function<void(RelationSlab)> RelationsCallback,
221 std::function<void(IncludeGraph)> IncludeGraphCallback) {
222 index::IndexingOptions IndexOpts;
223 IndexOpts.SystemSymbolFilter =
224 index::IndexingOptions::SystemSymbolFilterKind::All;
225 // We index function-local classes and its member functions only.
226 IndexOpts.IndexFunctionLocals = true;
227 // We need to delay indexing so instantiations of function bodies become
228 // available, this is so we can find constructor calls through `make_unique`.
229 IndexOpts.DeferIndexingToEndOfTranslationUnit = true;
230 Opts.CollectIncludePath = true;
231 if (Opts.Origin == SymbolOrigin::Unknown)
233 Opts.StoreAllDocumentation = false;
234 if (RefsCallback != nullptr) {
235 Opts.RefFilter = RefKind::All;
236 Opts.RefsInHeaders = true;
237 }
238 auto PragmaIncludes = std::make_unique<include_cleaner::PragmaIncludes>();
239 Opts.PragmaIncludes = PragmaIncludes.get();
240 return std::make_unique<IndexAction>(std::make_shared<SymbolCollector>(Opts),
241 std::move(PragmaIncludes), IndexOpts,
242 SymbolsCallback, RefsCallback,
243 RelationsCallback, IncludeGraphCallback);
244}
245
246} // namespace clangd
247} // namespace clang
An efficient structure of storing large set of symbol references in memory.
Definition Ref.h:111
An immutable symbol container that stores a set of symbols.
Definition Symbol.h:201
static llvm::Expected< URI > create(llvm::StringRef AbsolutePath, llvm::StringRef Scheme)
Creates a URI for a file in the given scheme.
Definition URI.cpp:208
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
Definition AST.cpp:44
bool isLikelyForwardingFunction(const FunctionTemplateDecl *FT)
Heuristic that checks if FT is likely to be forwarding a parameter pack to another function (e....
Definition AST.cpp:1042
std::optional< FileDigest > digestFile(const SourceManager &SM, FileID FID)
llvm::StringMap< IncludeGraphNode > IncludeGraph
Definition Headers.h:103
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:741
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
RefKind RefFilter
The symbol ref kinds that will be collected.
bool StoreAllDocumentation
If set to true, SymbolCollector will collect doc for all symbols.
bool RefsInHeaders
If set to true, SymbolCollector will collect all refs (from main file and included headers); otherwis...
const include_cleaner::PragmaIncludes * PragmaIncludes
If set, this is used to map symbol include path to a potentially different include path specified by ...