1 //===--- IndexAction.cpp -----------------------------------------*- C++-*-===//
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
7 //===----------------------------------------------------------------------===//
9 #include "IndexAction.h"
12 #include "clang-include-cleaner/Record.h"
13 #include "index/Relation.h"
14 #include "index/SymbolCollector.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"
34 std::optional
<std::string
> toURI(OptionalFileEntryRef File
) {
37 auto AbsolutePath
= File
->getFileEntry().tryGetRealPathName();
38 if (AbsolutePath
.empty())
40 return URI::create(AbsolutePath
).toString();
43 // Collects the nodes and edges of include graph during indexing action.
44 // Important: The graph generated by those callbacks might contain cycles and
46 struct IncludeGraphCollector
: public PPCallbacks
{
48 IncludeGraphCollector(const SourceManager
&SM
, IncludeGraph
&IG
)
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
60 if (Reason
!= FileChangeReason::EnterFile
)
63 const auto FileID
= SM
.getFileID(Loc
);
64 auto File
= SM
.getFileEntryRefForID(FileID
);
65 auto URI
= toURI(File
);
68 auto I
= IG
.try_emplace(*URI
).first
;
70 auto &Node
= I
->getValue();
71 // Node has already been populated.
72 if (Node
.URI
.data() == I
->getKeyData()) {
74 auto Digest
= digestFile(SM
, FileID
);
75 assert(Digest
&& Node
.Digest
== *Digest
&&
76 "Same file, different digest?");
80 if (auto Digest
= digestFile(SM
, FileID
))
81 Node
.Digest
= std::move(*Digest
);
82 if (FileID
== SM
.getMainFileID())
83 Node
.Flags
|= IncludeGraphNode::SourceFlag::IsTU
;
84 Node
.URI
= I
->getKey();
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
);
99 auto IncludingURI
= toURI(SM
.getFileEntryRefForID(SM
.getFileID(HashLoc
)));
103 auto NodeForInclude
= IG
.try_emplace(*IncludeURI
).first
->getKey();
104 auto NodeForIncluding
= IG
.try_emplace(*IncludingURI
);
106 NodeForIncluding
.first
->getValue().DirectIncludes
.push_back(NodeForInclude
);
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
{
113 auto URI
= toURI(SkippedFile
);
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");
124 const SourceManager
&SM
;
128 // Wraps the index action and reports index data after each translation unit.
129 class IndexAction
: public ASTFrontendAction
{
131 IndexAction(std::shared_ptr
<SymbolCollector
> C
,
132 std::unique_ptr
<include_cleaner::PragmaIncludes
> PI
,
133 const index::IndexingOptions
&Opts
,
134 std::function
<void(SymbolSlab
)> SymbolsCallback
,
135 std::function
<void(RefSlab
)> RefsCallback
,
136 std::function
<void(RelationSlab
)> RelationsCallback
,
137 std::function
<void(IncludeGraph
)> IncludeGraphCallback
)
138 : SymbolsCallback(SymbolsCallback
), RefsCallback(RefsCallback
),
139 RelationsCallback(RelationsCallback
),
140 IncludeGraphCallback(IncludeGraphCallback
), Collector(C
),
141 PI(std::move(PI
)), Opts(Opts
) {
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
))
149 auto &SM
= D
->getASTContext().getSourceManager();
150 auto FID
= SM
.getFileID(SM
.getExpansionLoc(D
->getLocation()));
153 return Collector
->shouldIndexFile(FID
);
157 std::unique_ptr
<ASTConsumer
>
158 CreateASTConsumer(CompilerInstance
&CI
, llvm::StringRef InFile
) override
{
159 PI
->record(CI
.getPreprocessor());
160 if (IncludeGraphCallback
!= nullptr)
161 CI
.getPreprocessor().addPPCallbacks(
162 std::make_unique
<IncludeGraphCollector
>(CI
.getSourceManager(), IG
));
164 return index::createIndexingASTConsumer(Collector
, Opts
,
165 CI
.getPreprocessorPtr());
168 bool BeginInvocation(CompilerInstance
&CI
) override
{
169 // We want all comments, not just the doxygen ones.
170 CI
.getLangOpts().CommentOpts
.ParseAllComments
= true;
171 CI
.getLangOpts().RetainCommentsFromSystemHeaders
= true;
172 // Index the whole file even if there are warnings and -Werror is set.
173 // Avoids some analyses too. Set in two places as we're late to the party.
174 CI
.getDiagnosticOpts().IgnoreWarnings
= true;
175 CI
.getDiagnostics().setIgnoreAllWarnings(true);
176 // Instruct the parser to ask our ASTConsumer if it should skip function
177 // bodies. The ASTConsumer will take care of skipping only functions inside
178 // the files that we have already processed.
179 CI
.getFrontendOpts().SkipFunctionBodies
= true;
183 void EndSourceFileAction() override
{
184 SymbolsCallback(Collector
->takeSymbols());
185 if (RefsCallback
!= nullptr)
186 RefsCallback(Collector
->takeRefs());
187 if (RelationsCallback
!= nullptr)
188 RelationsCallback(Collector
->takeRelations());
189 if (IncludeGraphCallback
!= nullptr) {
191 // This checks if all nodes are initialized.
192 for (const auto &Node
: IG
)
193 assert(Node
.getKeyData() == Node
.getValue().URI
.data());
195 IncludeGraphCallback(std::move(IG
));
200 std::function
<void(SymbolSlab
)> SymbolsCallback
;
201 std::function
<void(RefSlab
)> RefsCallback
;
202 std::function
<void(RelationSlab
)> RelationsCallback
;
203 std::function
<void(IncludeGraph
)> IncludeGraphCallback
;
204 std::shared_ptr
<SymbolCollector
> Collector
;
205 std::unique_ptr
<include_cleaner::PragmaIncludes
> PI
;
206 index::IndexingOptions Opts
;
212 std::unique_ptr
<FrontendAction
> createStaticIndexingAction(
213 SymbolCollector::Options Opts
,
214 std::function
<void(SymbolSlab
)> SymbolsCallback
,
215 std::function
<void(RefSlab
)> RefsCallback
,
216 std::function
<void(RelationSlab
)> RelationsCallback
,
217 std::function
<void(IncludeGraph
)> IncludeGraphCallback
) {
218 index::IndexingOptions IndexOpts
;
219 IndexOpts
.SystemSymbolFilter
=
220 index::IndexingOptions::SystemSymbolFilterKind::All
;
221 // We index function-local classes and its member functions only.
222 IndexOpts
.IndexFunctionLocals
= true;
223 Opts
.CollectIncludePath
= true;
224 if (Opts
.Origin
== SymbolOrigin::Unknown
)
225 Opts
.Origin
= SymbolOrigin::Static
;
226 Opts
.StoreAllDocumentation
= false;
227 if (RefsCallback
!= nullptr) {
228 Opts
.RefFilter
= RefKind::All
;
229 Opts
.RefsInHeaders
= true;
231 auto PragmaIncludes
= std::make_unique
<include_cleaner::PragmaIncludes
>();
232 Opts
.PragmaIncludes
= PragmaIncludes
.get();
233 return std::make_unique
<IndexAction
>(std::make_shared
<SymbolCollector
>(Opts
),
234 std::move(PragmaIncludes
), IndexOpts
,
235 SymbolsCallback
, RefsCallback
,
236 RelationsCallback
, IncludeGraphCallback
);
239 } // namespace clangd