12#include "clang-include-cleaner/Record.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"
34std::optional<std::string> toURI(OptionalFileEntryRef
File) {
37 auto AbsolutePath =
File->getFileEntry().tryGetRealPathName();
38 if (AbsolutePath.empty())
46struct IncludeGraphCollector :
public PPCallbacks {
48 IncludeGraphCollector(
const SourceManager &SM,
IncludeGraph &IG)
55 void FileChanged(SourceLocation Loc, FileChangeReason Reason,
56 SrcMgr::CharacteristicKind FileType,
57 FileID PrevFID)
override {
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();
72 if (Node.URI.data() == I->getKeyData()) {
75 assert(Digest && Node.Digest == *Digest &&
76 "Same file, different digest?");
81 Node.Digest = std::move(*Digest);
82 if (FileID == SM.getMainFileID())
84 Node.URI = I->getKey();
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);
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;
129class 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,
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) {
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 {
170 CI.getLangOpts().CommentOpts.ParseAllComments =
true;
171 CI.getLangOpts().RetainCommentsFromSystemHeaders =
true;
174 CI.getDiagnosticOpts().IgnoreWarnings =
true;
175 CI.getDiagnostics().setIgnoreAllWarnings(
true);
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) {
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;
204 std::shared_ptr<SymbolCollector> Collector;
205 std::unique_ptr<include_cleaner::PragmaIncludes> PI;
206 index::IndexingOptions Opts;
214 std::function<
void(
SymbolSlab)> SymbolsCallback,
215 std::function<
void(
RefSlab)> RefsCallback,
217 std::function<
void(
IncludeGraph)> IncludeGraphCallback) {
218 index::IndexingOptions IndexOpts;
219 IndexOpts.SystemSymbolFilter =
220 index::IndexingOptions::SystemSymbolFilterKind::All;
222 IndexOpts.IndexFunctionLocals =
true;
227 if (RefsCallback !=
nullptr) {
231 auto PragmaIncludes = std::make_unique<include_cleaner::PragmaIncludes>();
233 return std::make_unique<IndexAction>(std::make_shared<SymbolCollector>(Opts),
234 std::move(PragmaIncludes), IndexOpts,
235 SymbolsCallback, RefsCallback,
236 RelationsCallback, IncludeGraphCallback);
An efficient structure of storing large set of symbol references in memory.
An immutable symbol container that stores a set of symbols.
static llvm::Expected< URI > create(llvm::StringRef AbsolutePath, llvm::StringRef Scheme)
Creates a URI for a file in the given scheme.
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
std::optional< FileDigest > digestFile(const SourceManager &SM, FileID FID)
llvm::StringMap< IncludeGraphNode > IncludeGraph
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)
bool isDeeplyNested(const Decl *D, unsigned MaxDepth)
Checks whether D is more than MaxDepth away from translation unit scope.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
RefKind RefFilter
The symbol ref kinds that will be collected.
bool StoreAllDocumentation
If set to true, SymbolCollector will collect doc for all symbols.
bool RefsInHeaders
If set to true, SymbolCollector will collect all refs (from main file and included headers); otherwis...
const include_cleaner::PragmaIncludes * PragmaIncludes
If set, this is used to map symbol include path to a potentially different include path specified by ...