clang-tools 10.0.0

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 "Headers.h"
11 #include "Logger.h"
12 #include "index/Relation.h"
13 #include "index/SymbolOrigin.h"
14 #include "clang/AST/ASTConsumer.h"
15 #include "clang/AST/ASTContext.h"
16 #include "clang/Basic/SourceLocation.h"
17 #include "clang/Basic/SourceManager.h"
18 #include "clang/Frontend/CompilerInstance.h"
19 #include "clang/Frontend/MultiplexConsumer.h"
20 #include "clang/Index/IndexingAction.h"
21 #include "clang/Index/IndexingOptions.h"
22 #include "clang/Tooling/Tooling.h"
23 #include "llvm/ADT/STLExtras.h"
24 #include <functional>
25 #include <memory>
26 #include <utility>
27
28 namespace clang {
29 namespace clangd {
30 namespace {
31
32 llvm::Optional<std::string> toURI(const FileEntry *File) {
33 if (!File)
34 return llvm::None;
35 auto AbsolutePath = File->tryGetRealPathName();
36 if (AbsolutePath.empty())
37 return llvm::None;
38 return URI::create(AbsolutePath).toString();
39 }
40
41 // Collects the nodes and edges of include graph during indexing action.
42 // Important: The graph generated by those callbacks might contain cycles and
43 // self edges.
44 struct IncludeGraphCollector : public PPCallbacks {
45 public:
46 IncludeGraphCollector(const SourceManager &SM, IncludeGraph &IG)
47 : SM(SM), IG(IG) {}
48
49 // Populates everything except direct includes for a node, which represents
50 // edges in the include graph and populated in inclusion directive.
51 // We cannot populate the fields in InclusionDirective because it does not
52 // have access to the contents of the included file.
53 void FileChanged(SourceLocation Loc, FileChangeReason Reason,
54 SrcMgr::CharacteristicKind FileType,
55 FileID PrevFID) override {
56 // We only need to process each file once. So we don't care about anything
57 // but entries.
58 if (Reason != FileChangeReason::EnterFile)
59 return;
60
61 const auto FileID = SM.getFileID(Loc);
62 const auto File = SM.getFileEntryForID(FileID);
63 auto URI = toURI(File);
64 if (!URI)
65 return;
66 auto I = IG.try_emplace(*URI).first;
67
68 auto &Node = I->getValue();
69 // Node has already been populated.
70 if (Node.URI.data() == I->getKeyData()) {
71 #ifndef NDEBUG
72 auto Digest = digestFile(SM, FileID);
73 assert(Digest && Node.Digest == *Digest &&
74 "Same file, different digest?");
75 #endif
76 return;
77 }
78 if (auto Digest = digestFile(SM, FileID))
79 Node.Digest = std::move(*Digest);
80 if (FileID == SM.getMainFileID())
81 Node.Flags |= IncludeGraphNode::SourceFlag::IsTU;
82 Node.URI = I->getKey();
83 }
84
85 // Add edges from including files to includes.
86 void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
87 llvm::StringRef FileName, bool IsAngled,
88 CharSourceRange FilenameRange, const FileEntry *File,
89 llvm::StringRef SearchPath,
90 llvm::StringRef RelativePath, const Module *Imported,
91 SrcMgr::CharacteristicKind FileType) override {
92 auto IncludeURI = toURI(File);
93 if (!IncludeURI)
94 return;
95
96 auto IncludingURI = toURI(SM.getFileEntryForID(SM.getFileID(HashLoc)));
97 if (!IncludingURI)
98 return;
99
100 auto NodeForInclude = IG.try_emplace(*IncludeURI).first->getKey();
101 auto NodeForIncluding = IG.try_emplace(*IncludingURI);
102
103 NodeForIncluding.first->getValue().DirectIncludes.push_back(NodeForInclude);
104 }
105
106 // Sanity check to ensure we have already populated a skipped file.
107 void FileSkipped(const FileEntryRef &SkippedFile, const Token &FilenameTok,
108 SrcMgr::CharacteristicKind FileType) override {
109 #ifndef NDEBUG
110 auto URI = toURI(&SkippedFile.getFileEntry());
111 if (!URI)
112 return;
113 auto I = IG.try_emplace(*URI);
114 assert(!I.second && "File inserted for the first time on skip.");
115 assert(I.first->getKeyData() == I.first->getValue().URI.data() &&
116 "Node have not been populated yet");
117 #endif
118 }
119
120 private:
121 const SourceManager &SM;
122 IncludeGraph &IG;
123 };
124
125 // Wraps the index action and reports index data after each translation unit.
126 class IndexAction : public ASTFrontendAction {
127 public:
128 IndexAction(std::shared_ptr<SymbolCollector> C,
129 std::unique_ptr<CanonicalIncludes> Includes,
130 const index::IndexingOptions &Opts,
131 std::function<void(SymbolSlab)> SymbolsCallback,
132 std::function<void(RefSlab)> RefsCallback,
133 std::function<void(RelationSlab)> RelationsCallback,
134 std::function<void(IncludeGraph)> IncludeGraphCallback)
135 : SymbolsCallback(SymbolsCallback),
136 RefsCallback(RefsCallback), RelationsCallback(RelationsCallback),
137 IncludeGraphCallback(IncludeGraphCallback), Collector(C),
138 Includes(std::move(Includes)), Opts(Opts),
139 PragmaHandler(collectIWYUHeaderMaps(this->Includes.get())) {}
140
141 std::unique_ptr<ASTConsumer>
142 CreateASTConsumer(CompilerInstance &CI, llvm::StringRef InFile) override {
143 CI.getPreprocessor().addCommentHandler(PragmaHandler.get());
144 Includes->addSystemHeadersMapping(CI.getLangOpts());
145 if (IncludeGraphCallback != nullptr)
146 CI.getPreprocessor().addPPCallbacks(
147 std::make_unique<IncludeGraphCollector>(CI.getSourceManager(), IG));
148
149 return index::createIndexingASTConsumer(
150 Collector, Opts, CI.getPreprocessorPtr(),
151 /*ShouldSkipFunctionBody=*/[this](const Decl *D) {
152 auto &SM = D->getASTContext().getSourceManager();
153 auto FID = SM.getFileID(SM.getExpansionLoc(D->getLocation()));
154 if (!FID.isValid())
155 return false;
156 return !Collector->shouldIndexFile(FID);
157 });
158 }
159
160 bool BeginInvocation(CompilerInstance &CI) override {
161 // We want all comments, not just the doxygen ones.
162 CI.getLangOpts().CommentOpts.ParseAllComments = true;
163 CI.getLangOpts().RetainCommentsFromSystemHeaders = true;
164 // Index the whole file even if there are warnings and -Werror is set.
165 // Avoids some analyses too. Set in two places as we're late to the party.
166 CI.getDiagnosticOpts().IgnoreWarnings = true;
167 CI.getDiagnostics().setIgnoreAllWarnings(true);
168 // Instruct the parser to ask our ASTConsumer if it should skip function
169 // bodies. The ASTConsumer will take care of skipping only functions inside
170 // the files that we have already processed.
171 CI.getFrontendOpts().SkipFunctionBodies = true;
172 return true;
173 }
174
175 void EndSourceFileAction() override {
176 SymbolsCallback(Collector->takeSymbols());
177 if (RefsCallback != nullptr)
178 RefsCallback(Collector->takeRefs());
179 if (RelationsCallback != nullptr)
180 RelationsCallback(Collector->takeRelations());
181 if (IncludeGraphCallback != nullptr) {
182 #ifndef NDEBUG
183 // This checks if all nodes are initialized.
184 for (const auto &Node : IG)
185 assert(Node.getKeyData() == Node.getValue().URI.data());
186 #endif
187 IncludeGraphCallback(std::move(IG));
188 }
189 }
190
191 private:
192 std::function<void(SymbolSlab)> SymbolsCallback;
193 std::function<void(RefSlab)> RefsCallback;
194 std::function<void(RelationSlab)> RelationsCallback;
195 std::function<void(IncludeGraph)> IncludeGraphCallback;
196 std::shared_ptr<SymbolCollector> Collector;
197 std::unique_ptr<CanonicalIncludes> Includes;
198 index::IndexingOptions Opts;
199 std::unique_ptr<CommentHandler> PragmaHandler;
200 IncludeGraph IG;
201 };
202
203 } // namespace
204
205 std::unique_ptr<FrontendAction> createStaticIndexingAction(
206 SymbolCollector::Options Opts,
207 std::function<void(SymbolSlab)> SymbolsCallback,
208 std::function<void(RefSlab)> RefsCallback,
209 std::function<void(RelationSlab)> RelationsCallback,
210 std::function<void(IncludeGraph)> IncludeGraphCallback) {
211 index::IndexingOptions IndexOpts;
212 IndexOpts.SystemSymbolFilter =
213 index::IndexingOptions::SystemSymbolFilterKind::All;
214 Opts.CollectIncludePath = true;
215 if (Opts.Origin == SymbolOrigin::Unknown)
216 Opts.Origin = SymbolOrigin::Static;
217 Opts.StoreAllDocumentation = false;
218 if (RefsCallback != nullptr) {
219 Opts.RefFilter = RefKind::All;
220 Opts.RefsInHeaders = true;
221 }
222 auto Includes = std::make_unique<CanonicalIncludes>();
223 Opts.Includes = Includes.get();
224 return std::make_unique<IndexAction>(
225 std::make_shared<SymbolCollector>(std::move(Opts)), std::move(Includes),
226 IndexOpts, SymbolsCallback, RefsCallback, RelationsCallback,
227 IncludeGraphCallback);
228 }
229
230 } // namespace clangd
231 } // namespace clang
clang::clangd::collectIWYUHeaderMaps
std::unique_ptr< CommentHandler > collectIWYUHeaderMaps(CanonicalIncludes *Includes)
Returns a CommentHandler that parses pragma comment on include files to determine when we should incl...
Definition: CanonicalIncludes.cpp:63
Loc
SourceLocation Loc
&#39;#&#39; location in the include directive
Definition: IncludeOrderCheck.cpp:37
Decl
const FunctionDecl * Decl
Definition: AvoidBindCheck.cpp:93
clang::clangd::SymbolSlab
An immutable symbol container that stores a set of symbols.
Definition: Symbol.h:177
clang::clangd::RefSlab
An efficient structure of storing large set of symbol references in memory.
Definition: Ref.h:69
clang::clangd::RelationSlab
Definition: Relation.h:45
clang::clangd::RefKind::All
clang::clangd::TextDocumentSyncKind::None
Documents should not be synced at all.
Headers.h
clang::clangd::IncludeGraph
llvm::StringMap< IncludeGraphNode > IncludeGraph
Definition: Headers.h:82
clang::clangd::SymbolCollector::Options::CollectIncludePath
bool CollectIncludePath
Definition: SymbolCollector.h:54
clang::clangd::SymbolCollector::Options::StoreAllDocumentation
bool StoreAllDocumentation
If set to true, SymbolCollector will collect doc for all symbols.
Definition: SymbolCollector.h:84
IsAngled
bool IsAngled
true if this was an include with angle brackets
Definition: IncludeOrderCheck.cpp:40
Relation.h
clang::clangd::SymbolOrigin::Static
clang::clangd::SymbolCollector::Options
Definition: SymbolCollector.h:48
Collector
std::shared_ptr< SymbolCollector > Collector
Definition: SymbolCollectorTests.cpp:242
FileName
PathRef FileName
Definition: CodeComplete.cpp:1018
clang::clangd::digestFile
llvm::Optional< FileDigest > digestFile(const SourceManager &SM, FileID FID)
Definition: SourceCode.cpp:685
clang::clangd::SymbolCollector::Options::Origin
SymbolOrigin Origin
Definition: SymbolCollector.h:72
PPCallbacks
Logger.h
clang::clangd::URI::create
static llvm::Expected< URI > create(llvm::StringRef AbsolutePath, llvm::StringRef Scheme)
Creates a URI for a file in the given scheme.
Definition: URI.cpp:197
clang::clangd::SymbolCollector::Options::RefsInHeaders
bool RefsInHeaders
If set to true, SymbolCollector will collect all refs (from main file and included headers); otherwis...
Definition: SymbolCollector.h:70
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::clangd::createStaticIndexingAction
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)
Definition: IndexAction.cpp:205
clang::clangd::CompletionItemKind::Module
SymbolOrigin.h
clang::clangd::SymbolOrigin::Unknown
IndexAction.h
clang::clangd::IncludeGraphNode::SourceFlag::IsTU
clang::clangd::SymbolCollector::Options::Includes
const CanonicalIncludes * Includes
If set, this is used to map symbol #include path to a potentially different #include path...
Definition: SymbolCollector.h:57
clang::clangd::SymbolCollector::Options::RefFilter
RefKind RefFilter
The symbol ref kinds that will be collected.
Definition: SymbolCollector.h:65
PragmaHandler
CommentHandler * PragmaHandler
Definition: SymbolCollectorTests.cpp:244