• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "index/Relation.h"
12 #include "index/SymbolOrigin.h"
13 #include "support/Logger.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 
toURI(const FileEntry * File)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:
IncludeGraphCollectorclang::clangd::__anon54562b8f0111::IncludeGraphCollector46   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.
FileChangedclang::clangd::__anon54562b8f0111::IncludeGraphCollector53   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.
InclusionDirectiveclang::clangd::__anon54562b8f0111::IncludeGraphCollector86   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.
FileSkippedclang::clangd::__anon54562b8f0111::IncludeGraphCollector107   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:
IndexAction(std::shared_ptr<SymbolCollector> C,std::unique_ptr<CanonicalIncludes> Includes,const index::IndexingOptions & Opts,std::function<void (SymbolSlab)> SymbolsCallback,std::function<void (RefSlab)> RefsCallback,std::function<void (RelationSlab)> RelationsCallback,std::function<void (IncludeGraph)> IncludeGraphCallback)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), RefsCallback(RefsCallback),
136         RelationsCallback(RelationsCallback),
137         IncludeGraphCallback(IncludeGraphCallback), Collector(C),
138         Includes(std::move(Includes)), Opts(Opts),
139         PragmaHandler(collectIWYUHeaderMaps(this->Includes.get())) {
140     this->Opts.ShouldTraverseDecl = [this](const Decl *D) {
141       auto &SM = D->getASTContext().getSourceManager();
142       auto FID = SM.getFileID(SM.getExpansionLoc(D->getLocation()));
143       if (!FID.isValid())
144         return true;
145       return Collector->shouldIndexFile(FID);
146     };
147   }
148 
149   std::unique_ptr<ASTConsumer>
CreateASTConsumer(CompilerInstance & CI,llvm::StringRef InFile)150   CreateASTConsumer(CompilerInstance &CI, llvm::StringRef InFile) override {
151     CI.getPreprocessor().addCommentHandler(PragmaHandler.get());
152     Includes->addSystemHeadersMapping(CI.getLangOpts());
153     if (IncludeGraphCallback != nullptr)
154       CI.getPreprocessor().addPPCallbacks(
155           std::make_unique<IncludeGraphCollector>(CI.getSourceManager(), IG));
156 
157     return index::createIndexingASTConsumer(Collector, Opts,
158                                             CI.getPreprocessorPtr());
159   }
160 
BeginInvocation(CompilerInstance & CI)161   bool BeginInvocation(CompilerInstance &CI) override {
162     // We want all comments, not just the doxygen ones.
163     CI.getLangOpts().CommentOpts.ParseAllComments = true;
164     CI.getLangOpts().RetainCommentsFromSystemHeaders = true;
165     // Index the whole file even if there are warnings and -Werror is set.
166     // Avoids some analyses too. Set in two places as we're late to the party.
167     CI.getDiagnosticOpts().IgnoreWarnings = true;
168     CI.getDiagnostics().setIgnoreAllWarnings(true);
169     // Instruct the parser to ask our ASTConsumer if it should skip function
170     // bodies. The ASTConsumer will take care of skipping only functions inside
171     // the files that we have already processed.
172     CI.getFrontendOpts().SkipFunctionBodies = true;
173     return true;
174   }
175 
EndSourceFileAction()176   void EndSourceFileAction() override {
177     SymbolsCallback(Collector->takeSymbols());
178     if (RefsCallback != nullptr)
179       RefsCallback(Collector->takeRefs());
180     if (RelationsCallback != nullptr)
181       RelationsCallback(Collector->takeRelations());
182     if (IncludeGraphCallback != nullptr) {
183 #ifndef NDEBUG
184       // This checks if all nodes are initialized.
185       for (const auto &Node : IG)
186         assert(Node.getKeyData() == Node.getValue().URI.data());
187 #endif
188       IncludeGraphCallback(std::move(IG));
189     }
190   }
191 
192 private:
193   std::function<void(SymbolSlab)> SymbolsCallback;
194   std::function<void(RefSlab)> RefsCallback;
195   std::function<void(RelationSlab)> RelationsCallback;
196   std::function<void(IncludeGraph)> IncludeGraphCallback;
197   std::shared_ptr<SymbolCollector> Collector;
198   std::unique_ptr<CanonicalIncludes> Includes;
199   index::IndexingOptions Opts;
200   std::unique_ptr<CommentHandler> PragmaHandler;
201   IncludeGraph IG;
202 };
203 
204 } // namespace
205 
createStaticIndexingAction(SymbolCollector::Options Opts,std::function<void (SymbolSlab)> SymbolsCallback,std::function<void (RefSlab)> RefsCallback,std::function<void (RelationSlab)> RelationsCallback,std::function<void (IncludeGraph)> IncludeGraphCallback)206 std::unique_ptr<FrontendAction> createStaticIndexingAction(
207     SymbolCollector::Options Opts,
208     std::function<void(SymbolSlab)> SymbolsCallback,
209     std::function<void(RefSlab)> RefsCallback,
210     std::function<void(RelationSlab)> RelationsCallback,
211     std::function<void(IncludeGraph)> IncludeGraphCallback) {
212   index::IndexingOptions IndexOpts;
213   IndexOpts.SystemSymbolFilter =
214       index::IndexingOptions::SystemSymbolFilterKind::All;
215   Opts.CollectIncludePath = true;
216   if (Opts.Origin == SymbolOrigin::Unknown)
217     Opts.Origin = SymbolOrigin::Static;
218   Opts.StoreAllDocumentation = false;
219   if (RefsCallback != nullptr) {
220     Opts.RefFilter = RefKind::All;
221     Opts.RefsInHeaders = true;
222   }
223   auto Includes = std::make_unique<CanonicalIncludes>();
224   Opts.Includes = Includes.get();
225   return std::make_unique<IndexAction>(
226       std::make_shared<SymbolCollector>(std::move(Opts)), std::move(Includes),
227       IndexOpts, SymbolsCallback, RefsCallback, RelationsCallback,
228       IncludeGraphCallback);
229 }
230 
231 } // namespace clangd
232 } // namespace clang
233