clang-tools 23.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"
14#include "index/Serialization.h"
16#include "index/SymbolOrigin.h"
17#include "clang/AST/ASTConsumer.h"
18#include "clang/AST/ASTContext.h"
19#include "clang/Basic/SourceLocation.h"
20#include "clang/Basic/SourceManager.h"
21#include "clang/Frontend/CompilerInstance.h"
22#include "clang/Frontend/FrontendAction.h"
23#include "clang/Index/IndexingAction.h"
24#include "clang/Index/IndexingOptions.h"
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,
93 const Module *SuggestedModule, bool ModuleImported,
94 SrcMgr::CharacteristicKind FileType) override {
95 auto IncludeURI = toURI(File);
96 if (!IncludeURI)
97 return;
98
99 auto IncludingURI = toURI(SM.getFileEntryRefForID(SM.getFileID(HashLoc)));
100 if (!IncludingURI)
101 return;
102
103 auto NodeForInclude = IG.try_emplace(*IncludeURI).first->getKey();
104 auto NodeForIncluding = IG.try_emplace(*IncludingURI);
105
106 NodeForIncluding.first->getValue().DirectIncludes.push_back(NodeForInclude);
107 }
108
109 // Sanity check to ensure we have already populated a skipped file.
110 void FileSkipped(const FileEntryRef &SkippedFile, const Token &FilenameTok,
111 SrcMgr::CharacteristicKind FileType) override {
112#ifndef NDEBUG
113 auto URI = toURI(SkippedFile);
114 if (!URI)
115 return;
116 auto I = IG.try_emplace(*URI);
117 assert(!I.second && "File inserted for the first time on skip.");
118 assert(I.first->getKeyData() == I.first->getValue().URI.data() &&
119 "Node have not been populated yet");
120#endif
121 }
122
123private:
124 const SourceManager &SM;
125 IncludeGraph &IG;
126};
127
128// Wraps the index action and reports index data after each translation unit.
129class IndexAction : public ASTFrontendAction {
130public:
131 IndexAction(std::shared_ptr<SymbolCollector> C,
132 std::unique_ptr<include_cleaner::PragmaIncludes> PI,
133 const index::IndexingOptions &Opts,
134 std::function<void(IndexFileIn)> IndexContentsCallback)
135 : IndexContentsCallback(IndexContentsCallback), Collector(C),
136 PI(std::move(PI)), Opts(Opts) {
137 this->Opts.ShouldTraverseDecl = [this](const Decl *D) {
138 // Many operations performed during indexing is linear in terms of depth
139 // of the decl (USR generation, name lookups, figuring out role of a
140 // reference are some examples). Since we index all the decls nested
141 // inside, it becomes quadratic. So we give up on nested symbols.
142 if (isDeeplyNested(D))
143 return false;
144 // If D is a likely forwarding function we need the body to index indirect
145 // constructor calls (e.g. `make_unique`)
146 if (auto *FT = llvm::dyn_cast<clang::FunctionTemplateDecl>(D);
148 return true;
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 PI->record(CI.getPreprocessor());
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 IndexFileIn Result;
184 Result.Symbols = Collector->takeSymbols();
185 Result.Refs = Collector->takeRefs();
186 Result.Relations = Collector->takeRelations();
187#ifndef NDEBUG
188 // This checks if all nodes are initialized.
189 for (const auto &Node : IG)
190 assert(Node.getKeyData() == Node.getValue().URI.data());
191#endif
192 Result.Sources = std::move(IG);
193 IndexContentsCallback(std::move(Result));
194 }
195
196private:
197 std::function<void(IndexFileIn)> IndexContentsCallback;
198 std::shared_ptr<SymbolCollector> Collector;
199 std::unique_ptr<include_cleaner::PragmaIncludes> PI;
200 index::IndexingOptions Opts;
201 IncludeGraph IG;
202};
203
204} // namespace
205
206std::unique_ptr<FrontendAction> createStaticIndexingAction(
208 std::function<void(IndexFileIn)> IndexContentsCallback) {
209 index::IndexingOptions IndexOpts;
210 IndexOpts.SystemSymbolFilter =
211 index::IndexingOptions::SystemSymbolFilterKind::All;
212 // We index function-local classes and its member functions only.
213 IndexOpts.IndexFunctionLocals = true;
214 // We need to delay indexing so instantiations of function bodies become
215 // available, this is so we can find constructor calls through `make_unique`.
216 IndexOpts.DeferIndexingToEndOfTranslationUnit = true;
217 Opts.CollectIncludePath = true;
218 if (Opts.Origin == SymbolOrigin::Unknown)
220 Opts.StoreAllDocumentation = false;
221 Opts.RefFilter = RefKind::All;
222 Opts.RefsInHeaders = true;
223 auto PragmaIncludes = std::make_unique<include_cleaner::PragmaIncludes>();
224 Opts.PragmaIncludes = PragmaIncludes.get();
225 return std::make_unique<IndexAction>(std::make_shared<SymbolCollector>(Opts),
226 std::move(PragmaIncludes), IndexOpts,
227 IndexContentsCallback);
228}
229
230} // namespace clangd
231} // namespace clang
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(IndexFileIn)> IndexContentsCallback)
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 ...