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
, const Module
*Imported
,
93 SrcMgr::CharacteristicKind FileType
) override
{
94 auto IncludeURI
= toURI(File
);
98 auto IncludingURI
= toURI(SM
.getFileEntryRefForID(SM
.getFileID(HashLoc
)));
102 auto NodeForInclude
= IG
.try_emplace(*IncludeURI
).first
->getKey();
103 auto NodeForIncluding
= IG
.try_emplace(*IncludingURI
);
105 NodeForIncluding
.first
->getValue().DirectIncludes
.push_back(NodeForInclude
);
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
{
112 auto URI
= toURI(SkippedFile
);
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");
123 const SourceManager
&SM
;
127 // Wraps the index action and reports index data after each translation unit.
128 class IndexAction
: public ASTFrontendAction
{
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
))
148 auto &SM
= D
->getASTContext().getSourceManager();
149 auto FID
= SM
.getFileID(SM
.getExpansionLoc(D
->getLocation()));
152 return Collector
->shouldIndexFile(FID
);
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
));
163 return index::createIndexingASTConsumer(Collector
, Opts
,
164 CI
.getPreprocessorPtr());
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;
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) {
190 // This checks if all nodes are initialized.
191 for (const auto &Node
: IG
)
192 assert(Node
.getKeyData() == Node
.getValue().URI
.data());
194 IncludeGraphCallback(std::move(IG
));
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
;
211 std::unique_ptr
<FrontendAction
> createStaticIndexingAction(
212 SymbolCollector::Options Opts
,
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;
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
);
238 } // namespace clangd