clang-tools 8.0.0

IndexAction.cpp
Go to the documentation of this file.
1 #include "IndexAction.h"
2 #include "clang/Frontend/CompilerInstance.h"
3 #include "clang/Index/IndexDataConsumer.h"
4 #include "clang/Index/IndexingAction.h"
5 #include "clang/Tooling/Tooling.h"
6
7 namespace clang {
8 namespace clangd {
9 namespace {
10
11 llvm::Optional<std::string> toURI(const FileEntry *File) {
12 if (!File)
13 return llvm::None;
14 auto AbsolutePath = File->tryGetRealPathName();
15 if (AbsolutePath.empty())
16 return llvm::None;
17 return URI::create(AbsolutePath).toString();
18 }
19
20 // Collects the nodes and edges of include graph during indexing action.
21 // Important: The graph generated by those callbacks might contain cycles and
22 // self edges.
23 struct IncludeGraphCollector : public PPCallbacks {
24 public:
25 IncludeGraphCollector(const SourceManager &SM, IncludeGraph &IG)
26 : SM(SM), IG(IG) {}
27
28 // Populates everything except direct includes for a node, which represents
29 // edges in the include graph and populated in inclusion directive.
30 // We cannot populate the fields in InclusionDirective because it does not
31 // have access to the contents of the included file.
32 void FileChanged(SourceLocation Loc, FileChangeReason Reason,
33 SrcMgr::CharacteristicKind FileType,
34 FileID PrevFID) override {
35 // We only need to process each file once. So we don't care about anything
36 // but entries.
37 if (Reason != FileChangeReason::EnterFile)
38 return;
39
40 const auto FileID = SM.getFileID(Loc);
41 const auto File = SM.getFileEntryForID(FileID);
42 auto URI = toURI(File);
43 if (!URI)
44 return;
45 auto I = IG.try_emplace(*URI).first;
46
47 auto &Node = I->getValue();
48 // Node has already been populated.
49 if (Node.URI.data() == I->getKeyData()) {
50 #ifndef NDEBUG
51 auto Digest = digestFile(SM, FileID);
52 assert(Digest && Node.Digest == *Digest &&
53 "Same file, different digest?");
54 #endif
55 return;
56 }
57 if (auto Digest = digestFile(SM, FileID))
58 Node.Digest = std::move(*Digest);
59 Node.IsTU = FileID == SM.getMainFileID();
60 Node.URI = I->getKey();
61 }
62
63 // Add edges from including files to includes.
64 void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
65 llvm::StringRef FileName, bool IsAngled,
66 CharSourceRange FilenameRange, const FileEntry *File,
67 llvm::StringRef SearchPath,
68 llvm::StringRef RelativePath, const Module *Imported,
69 SrcMgr::CharacteristicKind FileType) override {
70 auto IncludeURI = toURI(File);
71 if (!IncludeURI)
72 return;
73
74 auto IncludingURI = toURI(SM.getFileEntryForID(SM.getFileID(HashLoc)));
75 if (!IncludingURI)
76 return;
77
78 auto NodeForInclude = IG.try_emplace(*IncludeURI).first->getKey();
79 auto NodeForIncluding = IG.try_emplace(*IncludingURI);
80
81 NodeForIncluding.first->getValue().DirectIncludes.push_back(NodeForInclude);
82 }
83
84 // Sanity check to ensure we have already populated a skipped file.
85 void FileSkipped(const FileEntry &SkippedFile, const Token &FilenameTok,
86 SrcMgr::CharacteristicKind FileType) override {
87 #ifndef NDEBUG
88 auto URI = toURI(&SkippedFile);
89 if (!URI)
90 return;
91 auto I = IG.try_emplace(*URI);
92 assert(!I.second && "File inserted for the first time on skip.");
93 assert(I.first->getKeyData() == I.first->getValue().URI.data() &&
94 "Node have not been populated yet");
95 #endif
96 }
97
98 private:
99 const SourceManager &SM;
100 IncludeGraph &IG;
101 };
102
103 // Wraps the index action and reports index data after each translation unit.
104 class IndexAction : public WrapperFrontendAction {
105 public:
106 IndexAction(std::shared_ptr<SymbolCollector> C,
107 std::unique_ptr<CanonicalIncludes> Includes,
108 const index::IndexingOptions &Opts,
109 std::function<void(SymbolSlab)> SymbolsCallback,
110 std::function<void(RefSlab)> RefsCallback,
111 std::function<void(IncludeGraph)> IncludeGraphCallback)
112 : WrapperFrontendAction(index::createIndexingAction(C, Opts, nullptr)),
113 SymbolsCallback(SymbolsCallback), RefsCallback(RefsCallback),
114 IncludeGraphCallback(IncludeGraphCallback), Collector(C),
115 Includes(std::move(Includes)),
116 PragmaHandler(collectIWYUHeaderMaps(this->Includes.get())) {}
117
118 std::unique_ptr<ASTConsumer>
119 CreateASTConsumer(CompilerInstance &CI, llvm::StringRef InFile) override {
120 CI.getPreprocessor().addCommentHandler(PragmaHandler.get());
121 if (IncludeGraphCallback != nullptr)
122 CI.getPreprocessor().addPPCallbacks(
123 llvm::make_unique<IncludeGraphCollector>(CI.getSourceManager(), IG));
124 return WrapperFrontendAction::CreateASTConsumer(CI, InFile);
125 }
126
127 bool BeginInvocation(CompilerInstance &CI) override {
128 // We want all comments, not just the doxygen ones.
129 CI.getLangOpts().CommentOpts.ParseAllComments = true;
130 return WrapperFrontendAction::BeginInvocation(CI);
131 }
132
133 void EndSourceFileAction() override {
134 WrapperFrontendAction::EndSourceFileAction();
135
136 const auto &CI = getCompilerInstance();
137 if (CI.hasDiagnostics() &&
138 CI.getDiagnostics().hasUncompilableErrorOccurred()) {
139 llvm::errs() << "Skipping TU due to uncompilable errors\n";
140 return;
141 }
142 SymbolsCallback(Collector->takeSymbols());
143 if (RefsCallback != nullptr)
144 RefsCallback(Collector->takeRefs());
145 if (IncludeGraphCallback != nullptr) {
146 #ifndef NDEBUG
147 // This checks if all nodes are initialized.
148 for (const auto &Node : IG)
149 assert(Node.getKeyData() == Node.getValue().URI.data());
150 #endif
151 IncludeGraphCallback(std::move(IG));
152 }
153 }
154
155 private:
156 std::function<void(SymbolSlab)> SymbolsCallback;
157 std::function<void(RefSlab)> RefsCallback;
158 std::function<void(IncludeGraph)> IncludeGraphCallback;
159 std::shared_ptr<SymbolCollector> Collector;
160 std::unique_ptr<CanonicalIncludes> Includes;
161 std::unique_ptr<CommentHandler> PragmaHandler;
162 IncludeGraph IG;
163 };
164
165 } // namespace
166
167 std::unique_ptr<FrontendAction> createStaticIndexingAction(
168 SymbolCollector::Options Opts,
169 std::function<void(SymbolSlab)> SymbolsCallback,
170 std::function<void(RefSlab)> RefsCallback,
171 std::function<void(IncludeGraph)> IncludeGraphCallback) {
172 index::IndexingOptions IndexOpts;
173 IndexOpts.SystemSymbolFilter =
174 index::IndexingOptions::SystemSymbolFilterKind::All;
175 Opts.CollectIncludePath = true;
176 Opts.CountReferences = true;
177 Opts.Origin = SymbolOrigin::Static;
178 if (RefsCallback != nullptr) {
179 Opts.RefFilter = RefKind::All;
180 Opts.RefsInHeaders = true;
181 }
182 auto Includes = llvm::make_unique<CanonicalIncludes>();
183 addSystemHeadersMapping(Includes.get());
184 Opts.Includes = Includes.get();
185 return llvm::make_unique<IndexAction>(
186 std::make_shared<SymbolCollector>(std::move(Opts)), std::move(Includes),
187 IndexOpts, SymbolsCallback, RefsCallback, IncludeGraphCallback);
188 }
189
190 } // namespace clangd
191 } // 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:84
Loc
SourceLocation Loc
&#39;#&#39; location in the include directive
Definition: IncludeOrderCheck.cpp:37
clang::clangd::SymbolSlab
Definition: Index.h:292
clang::clangd::RefSlab
Definition: Index.h:388
clang::clangd::RefKind::All
clang::clangd::TextDocumentSyncKind::None
Documents should not be synced at all.
clang::clangd::SymbolCollector::Options::CountReferences
bool CountReferences
Definition: SymbolCollector.h:57
clang::clangd::IncludeGraph
llvm::StringMap< IncludeGraphNode > IncludeGraph
Definition: Headers.h:65
clang::clangd::SymbolCollector::Options::CollectIncludePath
bool CollectIncludePath
Definition: SymbolCollector.h:52
IsAngled
bool IsAngled
true if this was an include with angle brackets
Definition: IncludeOrderCheck.cpp:40
clang::clangd::SymbolOrigin::Static
clang::clangd::SymbolCollector::Options
Definition: SymbolCollector.h:46
FileName
PathRef FileName
Definition: CodeComplete.cpp:1004
clang::clangd::digestFile
llvm::Optional< FileDigest > digestFile(const SourceManager &SM, FileID FID)
Definition: SourceCode.cpp:244
clang::clangd::SymbolCollector::Options::Origin
SymbolOrigin Origin
Definition: SymbolCollector.h:70
PPCallbacks
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:188
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:68
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ChangeNamespace.cpp:18
clang::clangd::CompletionItemKind::Module
clang::clangd::createStaticIndexingAction
std::unique_ptr< FrontendAction > createStaticIndexingAction(SymbolCollector::Options Opts, std::function< void(SymbolSlab)> SymbolsCallback, std::function< void(RefSlab)> RefsCallback, std::function< void(IncludeGraph)> IncludeGraphCallback)
Definition: IndexAction.cpp:167
IndexAction.h
clang::clangd::addSystemHeadersMapping
void addSystemHeadersMapping(CanonicalIncludes *Includes)
Adds mapping for system headers and some special symbols (e.g.
Definition: CanonicalIncludes.cpp:109
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:55
clang::clangd::SymbolCollector::Options::RefFilter
RefKind RefFilter
The symbol ref kinds that will be collected.
Definition: SymbolCollector.h:63