189f6b26fSZixu Wang //===- ExtractAPI/ExtractAPIConsumer.cpp ------------------------*- C++ -*-===//
289f6b26fSZixu Wang //
389f6b26fSZixu Wang // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
489f6b26fSZixu Wang // See https://llvm.org/LICENSE.txt for license information.
589f6b26fSZixu Wang // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
689f6b26fSZixu Wang //
789f6b26fSZixu Wang //===----------------------------------------------------------------------===//
889f6b26fSZixu Wang ///
989f6b26fSZixu Wang /// \file
1089f6b26fSZixu Wang /// This file implements the ExtractAPIAction, and ASTVisitor/Consumer to
1189f6b26fSZixu Wang /// collect API information.
1289f6b26fSZixu Wang ///
1389f6b26fSZixu Wang //===----------------------------------------------------------------------===//
1489f6b26fSZixu Wang
159fc45ca0SDaniel Grumberg #include "TypedefUnderlyingTypeResolver.h"
1689f6b26fSZixu Wang #include "clang/AST/ASTConsumer.h"
1789f6b26fSZixu Wang #include "clang/AST/ASTContext.h"
1889f6b26fSZixu Wang #include "clang/AST/Decl.h"
1989f6b26fSZixu Wang #include "clang/AST/DeclCXX.h"
2089f6b26fSZixu Wang #include "clang/AST/ParentMapContext.h"
2189f6b26fSZixu Wang #include "clang/AST/RawCommentList.h"
2289f6b26fSZixu Wang #include "clang/AST/RecursiveASTVisitor.h"
23aebe5fc6SDaniel Grumberg #include "clang/Basic/SourceLocation.h"
24aebe5fc6SDaniel Grumberg #include "clang/Basic/SourceManager.h"
2589f6b26fSZixu Wang #include "clang/Basic/TargetInfo.h"
2689f6b26fSZixu Wang #include "clang/ExtractAPI/API.h"
2789f6b26fSZixu Wang #include "clang/ExtractAPI/AvailabilityInfo.h"
2889f6b26fSZixu Wang #include "clang/ExtractAPI/DeclarationFragments.h"
2989f6b26fSZixu Wang #include "clang/ExtractAPI/FrontendActions.h"
3089f6b26fSZixu Wang #include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h"
3189f6b26fSZixu Wang #include "clang/Frontend/ASTConsumers.h"
3289f6b26fSZixu Wang #include "clang/Frontend/CompilerInstance.h"
33f833aab0SDaniel Grumberg #include "clang/Frontend/FrontendOptions.h"
34529a0570SDaniel Grumberg #include "clang/Lex/MacroInfo.h"
35529a0570SDaniel Grumberg #include "clang/Lex/PPCallbacks.h"
36aebe5fc6SDaniel Grumberg #include "clang/Lex/Preprocessor.h"
37529a0570SDaniel Grumberg #include "clang/Lex/PreprocessorOptions.h"
38aebe5fc6SDaniel Grumberg #include "llvm/ADT/DenseSet.h"
39529a0570SDaniel Grumberg #include "llvm/ADT/STLExtras.h"
40f833aab0SDaniel Grumberg #include "llvm/ADT/SmallVector.h"
41cb5bb285SZixu Wang #include "llvm/Support/FileSystem.h"
42f833aab0SDaniel Grumberg #include "llvm/Support/MemoryBuffer.h"
43cb5bb285SZixu Wang #include "llvm/Support/Path.h"
44cb5bb285SZixu Wang #include "llvm/Support/Regex.h"
4589f6b26fSZixu Wang #include "llvm/Support/raw_ostream.h"
46aebe5fc6SDaniel Grumberg #include <memory>
47aebe5fc6SDaniel Grumberg #include <utility>
4889f6b26fSZixu Wang
4989f6b26fSZixu Wang using namespace clang;
5089f6b26fSZixu Wang using namespace extractapi;
5189f6b26fSZixu Wang
5289f6b26fSZixu Wang namespace {
5389f6b26fSZixu Wang
getTypedefName(const TagDecl * Decl)549fc45ca0SDaniel Grumberg StringRef getTypedefName(const TagDecl *Decl) {
559fc45ca0SDaniel Grumberg if (const auto *TypedefDecl = Decl->getTypedefNameForAnonDecl())
569fc45ca0SDaniel Grumberg return TypedefDecl->getName();
579fc45ca0SDaniel Grumberg
589fc45ca0SDaniel Grumberg return {};
599fc45ca0SDaniel Grumberg }
609fc45ca0SDaniel Grumberg
getRelativeIncludeName(const CompilerInstance & CI,StringRef File,bool * IsQuoted=nullptr)61cb5bb285SZixu Wang Optional<std::string> getRelativeIncludeName(const CompilerInstance &CI,
62cb5bb285SZixu Wang StringRef File,
63cb5bb285SZixu Wang bool *IsQuoted = nullptr) {
64cb5bb285SZixu Wang assert(CI.hasFileManager() &&
65cb5bb285SZixu Wang "CompilerInstance does not have a FileNamager!");
66cb5bb285SZixu Wang
67cb5bb285SZixu Wang using namespace llvm::sys;
68cb5bb285SZixu Wang // Matches framework include patterns
69cb5bb285SZixu Wang const llvm::Regex Rule("/(.+)\\.framework/(.+)?Headers/(.+)");
70cb5bb285SZixu Wang
71cb5bb285SZixu Wang const auto &FS = CI.getVirtualFileSystem();
72cb5bb285SZixu Wang
73cb5bb285SZixu Wang SmallString<128> FilePath(File.begin(), File.end());
74cb5bb285SZixu Wang FS.makeAbsolute(FilePath);
75cb5bb285SZixu Wang path::remove_dots(FilePath, true);
76cb5bb285SZixu Wang FilePath = path::convert_to_slash(FilePath);
77cb5bb285SZixu Wang File = FilePath;
78cb5bb285SZixu Wang
79cb5bb285SZixu Wang // Checks whether `Dir` is a strict path prefix of `File`. If so returns
80cb5bb285SZixu Wang // the prefix length. Otherwise return 0.
81cb5bb285SZixu Wang auto CheckDir = [&](llvm::StringRef Dir) -> unsigned {
82cb5bb285SZixu Wang llvm::SmallString<32> DirPath(Dir.begin(), Dir.end());
83cb5bb285SZixu Wang FS.makeAbsolute(DirPath);
84cb5bb285SZixu Wang path::remove_dots(DirPath, true);
85cb5bb285SZixu Wang Dir = DirPath;
86cb5bb285SZixu Wang for (auto NI = path::begin(File), NE = path::end(File),
87cb5bb285SZixu Wang DI = path::begin(Dir), DE = path::end(Dir);
88cb5bb285SZixu Wang /*termination condition in loop*/; ++NI, ++DI) {
89cb5bb285SZixu Wang // '.' components in File are ignored.
90cb5bb285SZixu Wang while (NI != NE && *NI == ".")
91cb5bb285SZixu Wang ++NI;
92cb5bb285SZixu Wang if (NI == NE)
93cb5bb285SZixu Wang break;
94cb5bb285SZixu Wang
95cb5bb285SZixu Wang // '.' components in Dir are ignored.
96cb5bb285SZixu Wang while (DI != DE && *DI == ".")
97cb5bb285SZixu Wang ++DI;
98cb5bb285SZixu Wang
99cb5bb285SZixu Wang // Dir is a prefix of File, up to '.' components and choice of path
100cb5bb285SZixu Wang // separators.
101cb5bb285SZixu Wang if (DI == DE)
102cb5bb285SZixu Wang return NI - path::begin(File);
103cb5bb285SZixu Wang
104cb5bb285SZixu Wang // Consider all path separators equal.
105cb5bb285SZixu Wang if (NI->size() == 1 && DI->size() == 1 &&
106cb5bb285SZixu Wang path::is_separator(NI->front()) && path::is_separator(DI->front()))
107cb5bb285SZixu Wang continue;
108cb5bb285SZixu Wang
109cb5bb285SZixu Wang // Special case Apple .sdk folders since the search path is typically a
110cb5bb285SZixu Wang // symlink like `iPhoneSimulator14.5.sdk` while the file is instead
111cb5bb285SZixu Wang // located in `iPhoneSimulator.sdk` (the real folder).
112cb5bb285SZixu Wang if (NI->endswith(".sdk") && DI->endswith(".sdk")) {
113cb5bb285SZixu Wang StringRef NBasename = path::stem(*NI);
114cb5bb285SZixu Wang StringRef DBasename = path::stem(*DI);
115cb5bb285SZixu Wang if (DBasename.startswith(NBasename))
116cb5bb285SZixu Wang continue;
117cb5bb285SZixu Wang }
118cb5bb285SZixu Wang
119cb5bb285SZixu Wang if (*NI != *DI)
120cb5bb285SZixu Wang break;
121cb5bb285SZixu Wang }
122cb5bb285SZixu Wang return 0;
123cb5bb285SZixu Wang };
124cb5bb285SZixu Wang
125cb5bb285SZixu Wang unsigned PrefixLength = 0;
126cb5bb285SZixu Wang
127cb5bb285SZixu Wang // Go through the search paths and find the first one that is a prefix of
128cb5bb285SZixu Wang // the header.
129cb5bb285SZixu Wang for (const auto &Entry : CI.getHeaderSearchOpts().UserEntries) {
130cb5bb285SZixu Wang // Note whether the match is found in a quoted entry.
131cb5bb285SZixu Wang if (IsQuoted)
132cb5bb285SZixu Wang *IsQuoted = Entry.Group == frontend::Quoted;
133cb5bb285SZixu Wang
134cb5bb285SZixu Wang if (auto EntryFile = CI.getFileManager().getOptionalFileRef(Entry.Path)) {
135cb5bb285SZixu Wang if (auto HMap = HeaderMap::Create(*EntryFile, CI.getFileManager())) {
136cb5bb285SZixu Wang // If this is a headermap entry, try to reverse lookup the full path
137cb5bb285SZixu Wang // for a spelled name before mapping.
138cb5bb285SZixu Wang StringRef SpelledFilename = HMap->reverseLookupFilename(File);
139cb5bb285SZixu Wang if (!SpelledFilename.empty())
140cb5bb285SZixu Wang return SpelledFilename.str();
141cb5bb285SZixu Wang
142cb5bb285SZixu Wang // No matching mapping in this headermap, try next search entry.
143cb5bb285SZixu Wang continue;
144cb5bb285SZixu Wang }
145cb5bb285SZixu Wang }
146cb5bb285SZixu Wang
147cb5bb285SZixu Wang // Entry is a directory search entry, try to check if it's a prefix of File.
148cb5bb285SZixu Wang PrefixLength = CheckDir(Entry.Path);
149cb5bb285SZixu Wang if (PrefixLength > 0) {
150cb5bb285SZixu Wang // The header is found in a framework path, construct the framework-style
151cb5bb285SZixu Wang // include name `<Framework/Header.h>`
152cb5bb285SZixu Wang if (Entry.IsFramework) {
153cb5bb285SZixu Wang SmallVector<StringRef, 4> Matches;
154cb5bb285SZixu Wang Rule.match(File, &Matches);
155cb5bb285SZixu Wang // Returned matches are always in stable order.
156cb5bb285SZixu Wang if (Matches.size() != 4)
157cb5bb285SZixu Wang return None;
158cb5bb285SZixu Wang
159cb5bb285SZixu Wang return path::convert_to_slash(
160cb5bb285SZixu Wang (Matches[1].drop_front(Matches[1].rfind('/') + 1) + "/" +
161cb5bb285SZixu Wang Matches[3])
162cb5bb285SZixu Wang .str());
163cb5bb285SZixu Wang }
164cb5bb285SZixu Wang
165cb5bb285SZixu Wang // The header is found in a normal search path, strip the search path
166cb5bb285SZixu Wang // prefix to get an include name.
167cb5bb285SZixu Wang return path::convert_to_slash(File.drop_front(PrefixLength));
168cb5bb285SZixu Wang }
169cb5bb285SZixu Wang }
170cb5bb285SZixu Wang
171cb5bb285SZixu Wang // Couldn't determine a include name, use full path instead.
172cb5bb285SZixu Wang return None;
173cb5bb285SZixu Wang }
174cb5bb285SZixu Wang
175aebe5fc6SDaniel Grumberg struct LocationFileChecker {
isLocationInKnownFile__anon4a2c2dcf0111::LocationFileChecker176aebe5fc6SDaniel Grumberg bool isLocationInKnownFile(SourceLocation Loc) {
177aebe5fc6SDaniel Grumberg // If the loc refers to a macro expansion we need to first get the file
178aebe5fc6SDaniel Grumberg // location of the expansion.
179cb5bb285SZixu Wang auto &SM = CI.getSourceManager();
180aebe5fc6SDaniel Grumberg auto FileLoc = SM.getFileLoc(Loc);
181aebe5fc6SDaniel Grumberg FileID FID = SM.getFileID(FileLoc);
182aebe5fc6SDaniel Grumberg if (FID.isInvalid())
183aebe5fc6SDaniel Grumberg return false;
184aebe5fc6SDaniel Grumberg
185aebe5fc6SDaniel Grumberg const auto *File = SM.getFileEntryForID(FID);
186aebe5fc6SDaniel Grumberg if (!File)
187aebe5fc6SDaniel Grumberg return false;
188aebe5fc6SDaniel Grumberg
189aebe5fc6SDaniel Grumberg if (KnownFileEntries.count(File))
190aebe5fc6SDaniel Grumberg return true;
191aebe5fc6SDaniel Grumberg
192cb5bb285SZixu Wang if (ExternalFileEntries.count(File))
193cb5bb285SZixu Wang return false;
194cb5bb285SZixu Wang
195cb5bb285SZixu Wang StringRef FileName = File->tryGetRealPathName().empty()
196cb5bb285SZixu Wang ? File->getName()
197cb5bb285SZixu Wang : File->tryGetRealPathName();
198cb5bb285SZixu Wang
199cb5bb285SZixu Wang // Try to reduce the include name the same way we tried to include it.
200cb5bb285SZixu Wang bool IsQuoted = false;
201cb5bb285SZixu Wang if (auto IncludeName = getRelativeIncludeName(CI, FileName, &IsQuoted))
202f13019f8SKazu Hirata if (llvm::any_of(KnownFiles,
203cb5bb285SZixu Wang [&IsQuoted, &IncludeName](const auto &KnownFile) {
204cb5bb285SZixu Wang return KnownFile.first.equals(*IncludeName) &&
205cb5bb285SZixu Wang KnownFile.second == IsQuoted;
206f13019f8SKazu Hirata })) {
207cb5bb285SZixu Wang KnownFileEntries.insert(File);
208cb5bb285SZixu Wang return true;
209cb5bb285SZixu Wang }
210cb5bb285SZixu Wang
211cb5bb285SZixu Wang // Record that the file was not found to avoid future reverse lookup for
212cb5bb285SZixu Wang // the same file.
213cb5bb285SZixu Wang ExternalFileEntries.insert(File);
214aebe5fc6SDaniel Grumberg return false;
215aebe5fc6SDaniel Grumberg }
216aebe5fc6SDaniel Grumberg
LocationFileChecker__anon4a2c2dcf0111::LocationFileChecker217cb5bb285SZixu Wang LocationFileChecker(const CompilerInstance &CI,
218cb5bb285SZixu Wang SmallVector<std::pair<SmallString<32>, bool>> &KnownFiles)
219cb5bb285SZixu Wang : CI(CI), KnownFiles(KnownFiles), ExternalFileEntries() {
220cb5bb285SZixu Wang for (const auto &KnownFile : KnownFiles)
221cb5bb285SZixu Wang if (auto FileEntry = CI.getFileManager().getFile(KnownFile.first))
222aebe5fc6SDaniel Grumberg KnownFileEntries.insert(*FileEntry);
223aebe5fc6SDaniel Grumberg }
224aebe5fc6SDaniel Grumberg
225aebe5fc6SDaniel Grumberg private:
226cb5bb285SZixu Wang const CompilerInstance &CI;
227cb5bb285SZixu Wang SmallVector<std::pair<SmallString<32>, bool>> &KnownFiles;
228aebe5fc6SDaniel Grumberg llvm::DenseSet<const FileEntry *> KnownFileEntries;
229cb5bb285SZixu Wang llvm::DenseSet<const FileEntry *> ExternalFileEntries;
230aebe5fc6SDaniel Grumberg };
231aebe5fc6SDaniel Grumberg
23289f6b26fSZixu Wang /// The RecursiveASTVisitor to traverse symbol declarations and collect API
23389f6b26fSZixu Wang /// information.
23489f6b26fSZixu Wang class ExtractAPIVisitor : public RecursiveASTVisitor<ExtractAPIVisitor> {
23589f6b26fSZixu Wang public:
ExtractAPIVisitor(ASTContext & Context,LocationFileChecker & LCF,APISet & API)236aebe5fc6SDaniel Grumberg ExtractAPIVisitor(ASTContext &Context, LocationFileChecker &LCF, APISet &API)
237aebe5fc6SDaniel Grumberg : Context(Context), API(API), LCF(LCF) {}
23889f6b26fSZixu Wang
getAPI() const23989f6b26fSZixu Wang const APISet &getAPI() const { return API; }
24089f6b26fSZixu Wang
VisitVarDecl(const VarDecl * Decl)24189f6b26fSZixu Wang bool VisitVarDecl(const VarDecl *Decl) {
24289f6b26fSZixu Wang // Skip function parameters.
24389f6b26fSZixu Wang if (isa<ParmVarDecl>(Decl))
24489f6b26fSZixu Wang return true;
24589f6b26fSZixu Wang
24689f6b26fSZixu Wang // Skip non-global variables in records (struct/union/class).
24789f6b26fSZixu Wang if (Decl->getDeclContext()->isRecord())
24889f6b26fSZixu Wang return true;
24989f6b26fSZixu Wang
25089f6b26fSZixu Wang // Skip local variables inside function or method.
25189f6b26fSZixu Wang if (!Decl->isDefinedOutsideFunctionOrMethod())
25289f6b26fSZixu Wang return true;
25389f6b26fSZixu Wang
25489f6b26fSZixu Wang // If this is a template but not specialization or instantiation, skip.
25589f6b26fSZixu Wang if (Decl->getASTContext().getTemplateOrSpecializationInfo(Decl) &&
25689f6b26fSZixu Wang Decl->getTemplateSpecializationKind() == TSK_Undeclared)
25789f6b26fSZixu Wang return true;
25889f6b26fSZixu Wang
259aebe5fc6SDaniel Grumberg if (!LCF.isLocationInKnownFile(Decl->getLocation()))
260aebe5fc6SDaniel Grumberg return true;
261aebe5fc6SDaniel Grumberg
26289f6b26fSZixu Wang // Collect symbol information.
26389f6b26fSZixu Wang StringRef Name = Decl->getName();
26489f6b26fSZixu Wang StringRef USR = API.recordUSR(Decl);
26589f6b26fSZixu Wang PresumedLoc Loc =
26689f6b26fSZixu Wang Context.getSourceManager().getPresumedLoc(Decl->getLocation());
26789f6b26fSZixu Wang AvailabilityInfo Availability = getAvailability(Decl);
26889f6b26fSZixu Wang LinkageInfo Linkage = Decl->getLinkageAndVisibility();
26989f6b26fSZixu Wang DocComment Comment;
27089f6b26fSZixu Wang if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
27189f6b26fSZixu Wang Comment = RawComment->getFormattedLines(Context.getSourceManager(),
27289f6b26fSZixu Wang Context.getDiagnostics());
27389f6b26fSZixu Wang
27489f6b26fSZixu Wang // Build declaration fragments and sub-heading for the variable.
27589f6b26fSZixu Wang DeclarationFragments Declaration =
27689f6b26fSZixu Wang DeclarationFragmentsBuilder::getFragmentsForVar(Decl);
27789f6b26fSZixu Wang DeclarationFragments SubHeading =
27889f6b26fSZixu Wang DeclarationFragmentsBuilder::getSubHeading(Decl);
27989f6b26fSZixu Wang
28089f6b26fSZixu Wang // Add the global variable record to the API set.
28189f6b26fSZixu Wang API.addGlobalVar(Name, USR, Loc, Availability, Linkage, Comment,
28289f6b26fSZixu Wang Declaration, SubHeading);
28389f6b26fSZixu Wang return true;
28489f6b26fSZixu Wang }
28589f6b26fSZixu Wang
VisitFunctionDecl(const FunctionDecl * Decl)28689f6b26fSZixu Wang bool VisitFunctionDecl(const FunctionDecl *Decl) {
28789f6b26fSZixu Wang if (const auto *Method = dyn_cast<CXXMethodDecl>(Decl)) {
28889f6b26fSZixu Wang // Skip member function in class templates.
28989f6b26fSZixu Wang if (Method->getParent()->getDescribedClassTemplate() != nullptr)
29089f6b26fSZixu Wang return true;
29189f6b26fSZixu Wang
29289f6b26fSZixu Wang // Skip methods in records.
29389f6b26fSZixu Wang for (auto P : Context.getParents(*Method)) {
29489f6b26fSZixu Wang if (P.get<CXXRecordDecl>())
29589f6b26fSZixu Wang return true;
29689f6b26fSZixu Wang }
29789f6b26fSZixu Wang
29889f6b26fSZixu Wang // Skip ConstructorDecl and DestructorDecl.
29989f6b26fSZixu Wang if (isa<CXXConstructorDecl>(Method) || isa<CXXDestructorDecl>(Method))
30089f6b26fSZixu Wang return true;
30189f6b26fSZixu Wang }
30289f6b26fSZixu Wang
30389f6b26fSZixu Wang // Skip templated functions.
30489f6b26fSZixu Wang switch (Decl->getTemplatedKind()) {
30589f6b26fSZixu Wang case FunctionDecl::TK_NonTemplate:
306*3ff86f96SErich Keane case FunctionDecl::TK_DependentNonTemplate:
30789f6b26fSZixu Wang break;
30889f6b26fSZixu Wang case FunctionDecl::TK_MemberSpecialization:
30989f6b26fSZixu Wang case FunctionDecl::TK_FunctionTemplateSpecialization:
31089f6b26fSZixu Wang if (auto *TemplateInfo = Decl->getTemplateSpecializationInfo()) {
31189f6b26fSZixu Wang if (!TemplateInfo->isExplicitInstantiationOrSpecialization())
31289f6b26fSZixu Wang return true;
31389f6b26fSZixu Wang }
31489f6b26fSZixu Wang break;
31589f6b26fSZixu Wang case FunctionDecl::TK_FunctionTemplate:
31689f6b26fSZixu Wang case FunctionDecl::TK_DependentFunctionTemplateSpecialization:
31789f6b26fSZixu Wang return true;
31889f6b26fSZixu Wang }
31989f6b26fSZixu Wang
320aebe5fc6SDaniel Grumberg if (!LCF.isLocationInKnownFile(Decl->getLocation()))
321aebe5fc6SDaniel Grumberg return true;
322aebe5fc6SDaniel Grumberg
32389f6b26fSZixu Wang // Collect symbol information.
32489f6b26fSZixu Wang StringRef Name = Decl->getName();
32589f6b26fSZixu Wang StringRef USR = API.recordUSR(Decl);
32689f6b26fSZixu Wang PresumedLoc Loc =
32789f6b26fSZixu Wang Context.getSourceManager().getPresumedLoc(Decl->getLocation());
32889f6b26fSZixu Wang AvailabilityInfo Availability = getAvailability(Decl);
32989f6b26fSZixu Wang LinkageInfo Linkage = Decl->getLinkageAndVisibility();
33089f6b26fSZixu Wang DocComment Comment;
33189f6b26fSZixu Wang if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
33289f6b26fSZixu Wang Comment = RawComment->getFormattedLines(Context.getSourceManager(),
33389f6b26fSZixu Wang Context.getDiagnostics());
33489f6b26fSZixu Wang
33589f6b26fSZixu Wang // Build declaration fragments, sub-heading, and signature of the function.
33689f6b26fSZixu Wang DeclarationFragments Declaration =
33789f6b26fSZixu Wang DeclarationFragmentsBuilder::getFragmentsForFunction(Decl);
33889f6b26fSZixu Wang DeclarationFragments SubHeading =
33989f6b26fSZixu Wang DeclarationFragmentsBuilder::getSubHeading(Decl);
34089f6b26fSZixu Wang FunctionSignature Signature =
34189f6b26fSZixu Wang DeclarationFragmentsBuilder::getFunctionSignature(Decl);
34289f6b26fSZixu Wang
34389f6b26fSZixu Wang // Add the function record to the API set.
344236b6a0eSDaniel Grumberg API.addGlobalFunction(Name, USR, Loc, Availability, Linkage, Comment,
345236b6a0eSDaniel Grumberg Declaration, SubHeading, Signature);
34689f6b26fSZixu Wang return true;
34789f6b26fSZixu Wang }
34889f6b26fSZixu Wang
VisitEnumDecl(const EnumDecl * Decl)34971b4c226SZixu Wang bool VisitEnumDecl(const EnumDecl *Decl) {
35071b4c226SZixu Wang if (!Decl->isComplete())
35171b4c226SZixu Wang return true;
35271b4c226SZixu Wang
35371b4c226SZixu Wang // Skip forward declaration.
35471b4c226SZixu Wang if (!Decl->isThisDeclarationADefinition())
35571b4c226SZixu Wang return true;
35671b4c226SZixu Wang
357aebe5fc6SDaniel Grumberg if (!LCF.isLocationInKnownFile(Decl->getLocation()))
358aebe5fc6SDaniel Grumberg return true;
359aebe5fc6SDaniel Grumberg
36071b4c226SZixu Wang // Collect symbol information.
3617443a504SDaniel Grumberg std::string NameString = Decl->getQualifiedNameAsString();
3627443a504SDaniel Grumberg StringRef Name(NameString);
3639fc45ca0SDaniel Grumberg if (Name.empty())
3649fc45ca0SDaniel Grumberg Name = getTypedefName(Decl);
3657443a504SDaniel Grumberg
36671b4c226SZixu Wang StringRef USR = API.recordUSR(Decl);
36771b4c226SZixu Wang PresumedLoc Loc =
36871b4c226SZixu Wang Context.getSourceManager().getPresumedLoc(Decl->getLocation());
36971b4c226SZixu Wang AvailabilityInfo Availability = getAvailability(Decl);
37071b4c226SZixu Wang DocComment Comment;
37171b4c226SZixu Wang if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
37271b4c226SZixu Wang Comment = RawComment->getFormattedLines(Context.getSourceManager(),
37371b4c226SZixu Wang Context.getDiagnostics());
37471b4c226SZixu Wang
37571b4c226SZixu Wang // Build declaration fragments and sub-heading for the enum.
37671b4c226SZixu Wang DeclarationFragments Declaration =
37771b4c226SZixu Wang DeclarationFragmentsBuilder::getFragmentsForEnum(Decl);
37871b4c226SZixu Wang DeclarationFragments SubHeading =
37971b4c226SZixu Wang DeclarationFragmentsBuilder::getSubHeading(Decl);
38071b4c226SZixu Wang
3817443a504SDaniel Grumberg EnumRecord *EnumRecord =
3827443a504SDaniel Grumberg API.addEnum(API.copyString(Name), USR, Loc, Availability, Comment,
38371b4c226SZixu Wang Declaration, SubHeading);
38471b4c226SZixu Wang
38571b4c226SZixu Wang // Now collect information about the enumerators in this enum.
38671b4c226SZixu Wang recordEnumConstants(EnumRecord, Decl->enumerators());
38771b4c226SZixu Wang
38871b4c226SZixu Wang return true;
38971b4c226SZixu Wang }
39071b4c226SZixu Wang
VisitRecordDecl(const RecordDecl * Decl)3915bb5704cSZixu Wang bool VisitRecordDecl(const RecordDecl *Decl) {
3925bb5704cSZixu Wang if (!Decl->isCompleteDefinition())
3935bb5704cSZixu Wang return true;
3945bb5704cSZixu Wang
3955bb5704cSZixu Wang // Skip C++ structs/classes/unions
3965bb5704cSZixu Wang // TODO: support C++ records
3975bb5704cSZixu Wang if (isa<CXXRecordDecl>(Decl))
3985bb5704cSZixu Wang return true;
3995bb5704cSZixu Wang
400aebe5fc6SDaniel Grumberg if (!LCF.isLocationInKnownFile(Decl->getLocation()))
401aebe5fc6SDaniel Grumberg return true;
402aebe5fc6SDaniel Grumberg
4035bb5704cSZixu Wang // Collect symbol information.
4045bb5704cSZixu Wang StringRef Name = Decl->getName();
4059fc45ca0SDaniel Grumberg if (Name.empty())
4069fc45ca0SDaniel Grumberg Name = getTypedefName(Decl);
4075bb5704cSZixu Wang StringRef USR = API.recordUSR(Decl);
4085bb5704cSZixu Wang PresumedLoc Loc =
4095bb5704cSZixu Wang Context.getSourceManager().getPresumedLoc(Decl->getLocation());
4105bb5704cSZixu Wang AvailabilityInfo Availability = getAvailability(Decl);
4115bb5704cSZixu Wang DocComment Comment;
4125bb5704cSZixu Wang if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
4135bb5704cSZixu Wang Comment = RawComment->getFormattedLines(Context.getSourceManager(),
4145bb5704cSZixu Wang Context.getDiagnostics());
4155bb5704cSZixu Wang
4165bb5704cSZixu Wang // Build declaration fragments and sub-heading for the struct.
4175bb5704cSZixu Wang DeclarationFragments Declaration =
4185bb5704cSZixu Wang DeclarationFragmentsBuilder::getFragmentsForStruct(Decl);
4195bb5704cSZixu Wang DeclarationFragments SubHeading =
4205bb5704cSZixu Wang DeclarationFragmentsBuilder::getSubHeading(Decl);
4215bb5704cSZixu Wang
4225bb5704cSZixu Wang StructRecord *StructRecord = API.addStruct(
4235bb5704cSZixu Wang Name, USR, Loc, Availability, Comment, Declaration, SubHeading);
4245bb5704cSZixu Wang
4255bb5704cSZixu Wang // Now collect information about the fields in this struct.
4265bb5704cSZixu Wang recordStructFields(StructRecord, Decl->fields());
4275bb5704cSZixu Wang
4285bb5704cSZixu Wang return true;
4295bb5704cSZixu Wang }
4305bb5704cSZixu Wang
VisitObjCInterfaceDecl(const ObjCInterfaceDecl * Decl)4319b36e126SZixu Wang bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *Decl) {
4329b36e126SZixu Wang // Skip forward declaration for classes (@class)
4339b36e126SZixu Wang if (!Decl->isThisDeclarationADefinition())
4349b36e126SZixu Wang return true;
4359b36e126SZixu Wang
436aebe5fc6SDaniel Grumberg if (!LCF.isLocationInKnownFile(Decl->getLocation()))
437aebe5fc6SDaniel Grumberg return true;
438aebe5fc6SDaniel Grumberg
4399b36e126SZixu Wang // Collect symbol information.
4409b36e126SZixu Wang StringRef Name = Decl->getName();
4419b36e126SZixu Wang StringRef USR = API.recordUSR(Decl);
4429b36e126SZixu Wang PresumedLoc Loc =
4439b36e126SZixu Wang Context.getSourceManager().getPresumedLoc(Decl->getLocation());
4449b36e126SZixu Wang AvailabilityInfo Availability = getAvailability(Decl);
4459b36e126SZixu Wang LinkageInfo Linkage = Decl->getLinkageAndVisibility();
4469b36e126SZixu Wang DocComment Comment;
4479b36e126SZixu Wang if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
4489b36e126SZixu Wang Comment = RawComment->getFormattedLines(Context.getSourceManager(),
4499b36e126SZixu Wang Context.getDiagnostics());
4509b36e126SZixu Wang
4519b36e126SZixu Wang // Build declaration fragments and sub-heading for the interface.
4529b36e126SZixu Wang DeclarationFragments Declaration =
4539b36e126SZixu Wang DeclarationFragmentsBuilder::getFragmentsForObjCInterface(Decl);
4549b36e126SZixu Wang DeclarationFragments SubHeading =
4559b36e126SZixu Wang DeclarationFragmentsBuilder::getSubHeading(Decl);
4569b36e126SZixu Wang
4579b36e126SZixu Wang // Collect super class information.
4589b36e126SZixu Wang SymbolReference SuperClass;
4599b36e126SZixu Wang if (const auto *SuperClassDecl = Decl->getSuperClass()) {
4609b36e126SZixu Wang SuperClass.Name = SuperClassDecl->getObjCRuntimeNameAsString();
4619b36e126SZixu Wang SuperClass.USR = API.recordUSR(SuperClassDecl);
4629b36e126SZixu Wang }
4639b36e126SZixu Wang
4649b36e126SZixu Wang ObjCInterfaceRecord *ObjCInterfaceRecord =
4659b36e126SZixu Wang API.addObjCInterface(Name, USR, Loc, Availability, Linkage, Comment,
4669b36e126SZixu Wang Declaration, SubHeading, SuperClass);
4679b36e126SZixu Wang
4689b36e126SZixu Wang // Record all methods (selectors). This doesn't include automatically
4699b36e126SZixu Wang // synthesized property methods.
4709b36e126SZixu Wang recordObjCMethods(ObjCInterfaceRecord, Decl->methods());
4719b36e126SZixu Wang recordObjCProperties(ObjCInterfaceRecord, Decl->properties());
4729b36e126SZixu Wang recordObjCInstanceVariables(ObjCInterfaceRecord, Decl->ivars());
4739b36e126SZixu Wang recordObjCProtocols(ObjCInterfaceRecord, Decl->protocols());
4749b36e126SZixu Wang
4759b36e126SZixu Wang return true;
4769b36e126SZixu Wang }
4779b36e126SZixu Wang
VisitObjCProtocolDecl(const ObjCProtocolDecl * Decl)478d1d34bafSZixu Wang bool VisitObjCProtocolDecl(const ObjCProtocolDecl *Decl) {
479d1d34bafSZixu Wang // Skip forward declaration for protocols (@protocol).
480d1d34bafSZixu Wang if (!Decl->isThisDeclarationADefinition())
481d1d34bafSZixu Wang return true;
482d1d34bafSZixu Wang
483aebe5fc6SDaniel Grumberg if (!LCF.isLocationInKnownFile(Decl->getLocation()))
484aebe5fc6SDaniel Grumberg return true;
485aebe5fc6SDaniel Grumberg
486d1d34bafSZixu Wang // Collect symbol information.
487d1d34bafSZixu Wang StringRef Name = Decl->getName();
488d1d34bafSZixu Wang StringRef USR = API.recordUSR(Decl);
489d1d34bafSZixu Wang PresumedLoc Loc =
490d1d34bafSZixu Wang Context.getSourceManager().getPresumedLoc(Decl->getLocation());
491d1d34bafSZixu Wang AvailabilityInfo Availability = getAvailability(Decl);
492d1d34bafSZixu Wang DocComment Comment;
493d1d34bafSZixu Wang if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
494d1d34bafSZixu Wang Comment = RawComment->getFormattedLines(Context.getSourceManager(),
495d1d34bafSZixu Wang Context.getDiagnostics());
496d1d34bafSZixu Wang
497d1d34bafSZixu Wang // Build declaration fragments and sub-heading for the protocol.
498d1d34bafSZixu Wang DeclarationFragments Declaration =
499d1d34bafSZixu Wang DeclarationFragmentsBuilder::getFragmentsForObjCProtocol(Decl);
500d1d34bafSZixu Wang DeclarationFragments SubHeading =
501d1d34bafSZixu Wang DeclarationFragmentsBuilder::getSubHeading(Decl);
502d1d34bafSZixu Wang
503d1d34bafSZixu Wang ObjCProtocolRecord *ObjCProtocolRecord = API.addObjCProtocol(
504d1d34bafSZixu Wang Name, USR, Loc, Availability, Comment, Declaration, SubHeading);
505d1d34bafSZixu Wang
506d1d34bafSZixu Wang recordObjCMethods(ObjCProtocolRecord, Decl->methods());
507d1d34bafSZixu Wang recordObjCProperties(ObjCProtocolRecord, Decl->properties());
508d1d34bafSZixu Wang recordObjCProtocols(ObjCProtocolRecord, Decl->protocols());
509d1d34bafSZixu Wang
510d1d34bafSZixu Wang return true;
511d1d34bafSZixu Wang }
512d1d34bafSZixu Wang
VisitTypedefNameDecl(const TypedefNameDecl * Decl)5139fc45ca0SDaniel Grumberg bool VisitTypedefNameDecl(const TypedefNameDecl *Decl) {
5149fc45ca0SDaniel Grumberg // Skip ObjC Type Parameter for now.
5159fc45ca0SDaniel Grumberg if (isa<ObjCTypeParamDecl>(Decl))
5169fc45ca0SDaniel Grumberg return true;
5179fc45ca0SDaniel Grumberg
5189fc45ca0SDaniel Grumberg if (!Decl->isDefinedOutsideFunctionOrMethod())
5199fc45ca0SDaniel Grumberg return true;
5209fc45ca0SDaniel Grumberg
521aebe5fc6SDaniel Grumberg if (!LCF.isLocationInKnownFile(Decl->getLocation()))
522aebe5fc6SDaniel Grumberg return true;
523aebe5fc6SDaniel Grumberg
5249fc45ca0SDaniel Grumberg PresumedLoc Loc =
5259fc45ca0SDaniel Grumberg Context.getSourceManager().getPresumedLoc(Decl->getLocation());
5269fc45ca0SDaniel Grumberg StringRef Name = Decl->getName();
5279fc45ca0SDaniel Grumberg AvailabilityInfo Availability = getAvailability(Decl);
5289fc45ca0SDaniel Grumberg StringRef USR = API.recordUSR(Decl);
5299fc45ca0SDaniel Grumberg DocComment Comment;
5309fc45ca0SDaniel Grumberg if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
5319fc45ca0SDaniel Grumberg Comment = RawComment->getFormattedLines(Context.getSourceManager(),
5329fc45ca0SDaniel Grumberg Context.getDiagnostics());
5339fc45ca0SDaniel Grumberg
5349fc45ca0SDaniel Grumberg QualType Type = Decl->getUnderlyingType();
5359fc45ca0SDaniel Grumberg SymbolReference SymRef =
5369fc45ca0SDaniel Grumberg TypedefUnderlyingTypeResolver(Context).getSymbolReferenceForType(Type,
5379fc45ca0SDaniel Grumberg API);
5389fc45ca0SDaniel Grumberg
5399fc45ca0SDaniel Grumberg API.addTypedef(Name, USR, Loc, Availability, Comment,
5409fc45ca0SDaniel Grumberg DeclarationFragmentsBuilder::getFragmentsForTypedef(Decl),
5419fc45ca0SDaniel Grumberg DeclarationFragmentsBuilder::getSubHeading(Decl), SymRef);
5429fc45ca0SDaniel Grumberg
5439fc45ca0SDaniel Grumberg return true;
5449fc45ca0SDaniel Grumberg }
5459fc45ca0SDaniel Grumberg
VisitObjCCategoryDecl(const ObjCCategoryDecl * Decl)546178aad9bSZixu Wang bool VisitObjCCategoryDecl(const ObjCCategoryDecl *Decl) {
547178aad9bSZixu Wang // Collect symbol information.
548178aad9bSZixu Wang StringRef Name = Decl->getName();
549178aad9bSZixu Wang StringRef USR = API.recordUSR(Decl);
550178aad9bSZixu Wang PresumedLoc Loc =
551178aad9bSZixu Wang Context.getSourceManager().getPresumedLoc(Decl->getLocation());
552178aad9bSZixu Wang AvailabilityInfo Availability = getAvailability(Decl);
553178aad9bSZixu Wang DocComment Comment;
554178aad9bSZixu Wang if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
555178aad9bSZixu Wang Comment = RawComment->getFormattedLines(Context.getSourceManager(),
556178aad9bSZixu Wang Context.getDiagnostics());
557178aad9bSZixu Wang // Build declaration fragments and sub-heading for the category.
558178aad9bSZixu Wang DeclarationFragments Declaration =
559178aad9bSZixu Wang DeclarationFragmentsBuilder::getFragmentsForObjCCategory(Decl);
560178aad9bSZixu Wang DeclarationFragments SubHeading =
561178aad9bSZixu Wang DeclarationFragmentsBuilder::getSubHeading(Decl);
562178aad9bSZixu Wang
563178aad9bSZixu Wang const ObjCInterfaceDecl *InterfaceDecl = Decl->getClassInterface();
564178aad9bSZixu Wang SymbolReference Interface(InterfaceDecl->getName(),
565178aad9bSZixu Wang API.recordUSR(InterfaceDecl));
566178aad9bSZixu Wang
567178aad9bSZixu Wang ObjCCategoryRecord *ObjCCategoryRecord =
568178aad9bSZixu Wang API.addObjCCategory(Name, USR, Loc, Availability, Comment, Declaration,
569178aad9bSZixu Wang SubHeading, Interface);
570178aad9bSZixu Wang
571178aad9bSZixu Wang recordObjCMethods(ObjCCategoryRecord, Decl->methods());
572178aad9bSZixu Wang recordObjCProperties(ObjCCategoryRecord, Decl->properties());
573178aad9bSZixu Wang recordObjCInstanceVariables(ObjCCategoryRecord, Decl->ivars());
574178aad9bSZixu Wang recordObjCProtocols(ObjCCategoryRecord, Decl->protocols());
575178aad9bSZixu Wang
576178aad9bSZixu Wang return true;
577178aad9bSZixu Wang }
578178aad9bSZixu Wang
57989f6b26fSZixu Wang private:
58089f6b26fSZixu Wang /// Get availability information of the declaration \p D.
getAvailability(const Decl * D) const58189f6b26fSZixu Wang AvailabilityInfo getAvailability(const Decl *D) const {
58289f6b26fSZixu Wang StringRef PlatformName = Context.getTargetInfo().getPlatformName();
58389f6b26fSZixu Wang
58489f6b26fSZixu Wang AvailabilityInfo Availability;
58589f6b26fSZixu Wang // Collect availability attributes from all redeclarations.
58689f6b26fSZixu Wang for (const auto *RD : D->redecls()) {
58789f6b26fSZixu Wang for (const auto *A : RD->specific_attrs<AvailabilityAttr>()) {
58889f6b26fSZixu Wang if (A->getPlatform()->getName() != PlatformName)
58989f6b26fSZixu Wang continue;
59089f6b26fSZixu Wang Availability = AvailabilityInfo(A->getIntroduced(), A->getDeprecated(),
59189f6b26fSZixu Wang A->getObsoleted(), A->getUnavailable(),
59289f6b26fSZixu Wang /* UnconditionallyDeprecated */ false,
59389f6b26fSZixu Wang /* UnconditionallyUnavailable */ false);
59489f6b26fSZixu Wang break;
59589f6b26fSZixu Wang }
59689f6b26fSZixu Wang
59789f6b26fSZixu Wang if (const auto *A = RD->getAttr<UnavailableAttr>())
59889f6b26fSZixu Wang if (!A->isImplicit()) {
59989f6b26fSZixu Wang Availability.Unavailable = true;
60089f6b26fSZixu Wang Availability.UnconditionallyUnavailable = true;
60189f6b26fSZixu Wang }
60289f6b26fSZixu Wang
60389f6b26fSZixu Wang if (const auto *A = RD->getAttr<DeprecatedAttr>())
60489f6b26fSZixu Wang if (!A->isImplicit())
60589f6b26fSZixu Wang Availability.UnconditionallyDeprecated = true;
60689f6b26fSZixu Wang }
60789f6b26fSZixu Wang
60889f6b26fSZixu Wang return Availability;
60989f6b26fSZixu Wang }
61089f6b26fSZixu Wang
61171b4c226SZixu Wang /// Collect API information for the enum constants and associate with the
61271b4c226SZixu Wang /// parent enum.
recordEnumConstants(EnumRecord * EnumRecord,const EnumDecl::enumerator_range Constants)61371b4c226SZixu Wang void recordEnumConstants(EnumRecord *EnumRecord,
61471b4c226SZixu Wang const EnumDecl::enumerator_range Constants) {
61571b4c226SZixu Wang for (const auto *Constant : Constants) {
61671b4c226SZixu Wang // Collect symbol information.
61771b4c226SZixu Wang StringRef Name = Constant->getName();
61871b4c226SZixu Wang StringRef USR = API.recordUSR(Constant);
61971b4c226SZixu Wang PresumedLoc Loc =
62071b4c226SZixu Wang Context.getSourceManager().getPresumedLoc(Constant->getLocation());
62171b4c226SZixu Wang AvailabilityInfo Availability = getAvailability(Constant);
62271b4c226SZixu Wang DocComment Comment;
62371b4c226SZixu Wang if (auto *RawComment = Context.getRawCommentForDeclNoCache(Constant))
62471b4c226SZixu Wang Comment = RawComment->getFormattedLines(Context.getSourceManager(),
62571b4c226SZixu Wang Context.getDiagnostics());
62671b4c226SZixu Wang
62771b4c226SZixu Wang // Build declaration fragments and sub-heading for the enum constant.
62871b4c226SZixu Wang DeclarationFragments Declaration =
62971b4c226SZixu Wang DeclarationFragmentsBuilder::getFragmentsForEnumConstant(Constant);
63071b4c226SZixu Wang DeclarationFragments SubHeading =
63171b4c226SZixu Wang DeclarationFragmentsBuilder::getSubHeading(Constant);
63271b4c226SZixu Wang
63371b4c226SZixu Wang API.addEnumConstant(EnumRecord, Name, USR, Loc, Availability, Comment,
63471b4c226SZixu Wang Declaration, SubHeading);
63571b4c226SZixu Wang }
63671b4c226SZixu Wang }
63771b4c226SZixu Wang
6385bb5704cSZixu Wang /// Collect API information for the struct fields and associate with the
6395bb5704cSZixu Wang /// parent struct.
recordStructFields(StructRecord * StructRecord,const RecordDecl::field_range Fields)6405bb5704cSZixu Wang void recordStructFields(StructRecord *StructRecord,
6415bb5704cSZixu Wang const RecordDecl::field_range Fields) {
6425bb5704cSZixu Wang for (const auto *Field : Fields) {
6435bb5704cSZixu Wang // Collect symbol information.
6445bb5704cSZixu Wang StringRef Name = Field->getName();
6455bb5704cSZixu Wang StringRef USR = API.recordUSR(Field);
6465bb5704cSZixu Wang PresumedLoc Loc =
6475bb5704cSZixu Wang Context.getSourceManager().getPresumedLoc(Field->getLocation());
6485bb5704cSZixu Wang AvailabilityInfo Availability = getAvailability(Field);
6495bb5704cSZixu Wang DocComment Comment;
6505bb5704cSZixu Wang if (auto *RawComment = Context.getRawCommentForDeclNoCache(Field))
6515bb5704cSZixu Wang Comment = RawComment->getFormattedLines(Context.getSourceManager(),
6525bb5704cSZixu Wang Context.getDiagnostics());
6535bb5704cSZixu Wang
6545bb5704cSZixu Wang // Build declaration fragments and sub-heading for the struct field.
6555bb5704cSZixu Wang DeclarationFragments Declaration =
6565bb5704cSZixu Wang DeclarationFragmentsBuilder::getFragmentsForField(Field);
6575bb5704cSZixu Wang DeclarationFragments SubHeading =
6585bb5704cSZixu Wang DeclarationFragmentsBuilder::getSubHeading(Field);
6595bb5704cSZixu Wang
6605bb5704cSZixu Wang API.addStructField(StructRecord, Name, USR, Loc, Availability, Comment,
6615bb5704cSZixu Wang Declaration, SubHeading);
6625bb5704cSZixu Wang }
6635bb5704cSZixu Wang }
6645bb5704cSZixu Wang
6659b36e126SZixu Wang /// Collect API information for the Objective-C methods and associate with the
6669b36e126SZixu Wang /// parent container.
recordObjCMethods(ObjCContainerRecord * Container,const ObjCContainerDecl::method_range Methods)6679b36e126SZixu Wang void recordObjCMethods(ObjCContainerRecord *Container,
6689b36e126SZixu Wang const ObjCContainerDecl::method_range Methods) {
6699b36e126SZixu Wang for (const auto *Method : Methods) {
6709b36e126SZixu Wang // Don't record selectors for properties.
6719b36e126SZixu Wang if (Method->isPropertyAccessor())
6729b36e126SZixu Wang continue;
6739b36e126SZixu Wang
6749b36e126SZixu Wang StringRef Name = API.copyString(Method->getSelector().getAsString());
6759b36e126SZixu Wang StringRef USR = API.recordUSR(Method);
6769b36e126SZixu Wang PresumedLoc Loc =
6779b36e126SZixu Wang Context.getSourceManager().getPresumedLoc(Method->getLocation());
6789b36e126SZixu Wang AvailabilityInfo Availability = getAvailability(Method);
6799b36e126SZixu Wang DocComment Comment;
6809b36e126SZixu Wang if (auto *RawComment = Context.getRawCommentForDeclNoCache(Method))
6819b36e126SZixu Wang Comment = RawComment->getFormattedLines(Context.getSourceManager(),
6829b36e126SZixu Wang Context.getDiagnostics());
6839b36e126SZixu Wang
6849b36e126SZixu Wang // Build declaration fragments, sub-heading, and signature for the method.
6859b36e126SZixu Wang DeclarationFragments Declaration =
6869b36e126SZixu Wang DeclarationFragmentsBuilder::getFragmentsForObjCMethod(Method);
6879b36e126SZixu Wang DeclarationFragments SubHeading =
6889b36e126SZixu Wang DeclarationFragmentsBuilder::getSubHeading(Method);
6899b36e126SZixu Wang FunctionSignature Signature =
6909b36e126SZixu Wang DeclarationFragmentsBuilder::getFunctionSignature(Method);
6919b36e126SZixu Wang
6929b36e126SZixu Wang API.addObjCMethod(Container, Name, USR, Loc, Availability, Comment,
6939b36e126SZixu Wang Declaration, SubHeading, Signature,
6949b36e126SZixu Wang Method->isInstanceMethod());
6959b36e126SZixu Wang }
6969b36e126SZixu Wang }
6979b36e126SZixu Wang
recordObjCProperties(ObjCContainerRecord * Container,const ObjCContainerDecl::prop_range Properties)6989b36e126SZixu Wang void recordObjCProperties(ObjCContainerRecord *Container,
6999b36e126SZixu Wang const ObjCContainerDecl::prop_range Properties) {
7009b36e126SZixu Wang for (const auto *Property : Properties) {
7019b36e126SZixu Wang StringRef Name = Property->getName();
7029b36e126SZixu Wang StringRef USR = API.recordUSR(Property);
7039b36e126SZixu Wang PresumedLoc Loc =
7049b36e126SZixu Wang Context.getSourceManager().getPresumedLoc(Property->getLocation());
7059b36e126SZixu Wang AvailabilityInfo Availability = getAvailability(Property);
7069b36e126SZixu Wang DocComment Comment;
7079b36e126SZixu Wang if (auto *RawComment = Context.getRawCommentForDeclNoCache(Property))
7089b36e126SZixu Wang Comment = RawComment->getFormattedLines(Context.getSourceManager(),
7099b36e126SZixu Wang Context.getDiagnostics());
7109b36e126SZixu Wang
7119b36e126SZixu Wang // Build declaration fragments and sub-heading for the property.
7129b36e126SZixu Wang DeclarationFragments Declaration =
7139b36e126SZixu Wang DeclarationFragmentsBuilder::getFragmentsForObjCProperty(Property);
7149b36e126SZixu Wang DeclarationFragments SubHeading =
7159b36e126SZixu Wang DeclarationFragmentsBuilder::getSubHeading(Property);
7169b36e126SZixu Wang
7179b36e126SZixu Wang StringRef GetterName =
7189b36e126SZixu Wang API.copyString(Property->getGetterName().getAsString());
7199b36e126SZixu Wang StringRef SetterName =
7209b36e126SZixu Wang API.copyString(Property->getSetterName().getAsString());
7219b36e126SZixu Wang
7229b36e126SZixu Wang // Get the attributes for property.
7239b36e126SZixu Wang unsigned Attributes = ObjCPropertyRecord::NoAttr;
7249b36e126SZixu Wang if (Property->getPropertyAttributes() &
7259b36e126SZixu Wang ObjCPropertyAttribute::kind_readonly)
7269b36e126SZixu Wang Attributes |= ObjCPropertyRecord::ReadOnly;
7279b36e126SZixu Wang if (Property->getPropertyAttributes() & ObjCPropertyAttribute::kind_class)
7289b36e126SZixu Wang Attributes |= ObjCPropertyRecord::Class;
7299b36e126SZixu Wang
7309b36e126SZixu Wang API.addObjCProperty(
7319b36e126SZixu Wang Container, Name, USR, Loc, Availability, Comment, Declaration,
7329b36e126SZixu Wang SubHeading,
7339b36e126SZixu Wang static_cast<ObjCPropertyRecord::AttributeKind>(Attributes),
7349b36e126SZixu Wang GetterName, SetterName, Property->isOptional());
7359b36e126SZixu Wang }
7369b36e126SZixu Wang }
7379b36e126SZixu Wang
recordObjCInstanceVariables(ObjCContainerRecord * Container,const llvm::iterator_range<DeclContext::specific_decl_iterator<ObjCIvarDecl>> Ivars)7389b36e126SZixu Wang void recordObjCInstanceVariables(
7399b36e126SZixu Wang ObjCContainerRecord *Container,
7409b36e126SZixu Wang const llvm::iterator_range<
7419b36e126SZixu Wang DeclContext::specific_decl_iterator<ObjCIvarDecl>>
7429b36e126SZixu Wang Ivars) {
7439b36e126SZixu Wang for (const auto *Ivar : Ivars) {
7449b36e126SZixu Wang StringRef Name = Ivar->getName();
7459b36e126SZixu Wang StringRef USR = API.recordUSR(Ivar);
7469b36e126SZixu Wang PresumedLoc Loc =
7479b36e126SZixu Wang Context.getSourceManager().getPresumedLoc(Ivar->getLocation());
7489b36e126SZixu Wang AvailabilityInfo Availability = getAvailability(Ivar);
7499b36e126SZixu Wang DocComment Comment;
7509b36e126SZixu Wang if (auto *RawComment = Context.getRawCommentForDeclNoCache(Ivar))
7519b36e126SZixu Wang Comment = RawComment->getFormattedLines(Context.getSourceManager(),
7529b36e126SZixu Wang Context.getDiagnostics());
7539b36e126SZixu Wang
7549b36e126SZixu Wang // Build declaration fragments and sub-heading for the instance variable.
7559b36e126SZixu Wang DeclarationFragments Declaration =
7569b36e126SZixu Wang DeclarationFragmentsBuilder::getFragmentsForField(Ivar);
7579b36e126SZixu Wang DeclarationFragments SubHeading =
7589b36e126SZixu Wang DeclarationFragmentsBuilder::getSubHeading(Ivar);
7599b36e126SZixu Wang
7609b36e126SZixu Wang ObjCInstanceVariableRecord::AccessControl Access =
7619b36e126SZixu Wang Ivar->getCanonicalAccessControl();
7629b36e126SZixu Wang
7639b36e126SZixu Wang API.addObjCInstanceVariable(Container, Name, USR, Loc, Availability,
7649b36e126SZixu Wang Comment, Declaration, SubHeading, Access);
7659b36e126SZixu Wang }
7669b36e126SZixu Wang }
7679b36e126SZixu Wang
recordObjCProtocols(ObjCContainerRecord * Container,ObjCInterfaceDecl::protocol_range Protocols)7689b36e126SZixu Wang void recordObjCProtocols(ObjCContainerRecord *Container,
7699b36e126SZixu Wang ObjCInterfaceDecl::protocol_range Protocols) {
7709b36e126SZixu Wang for (const auto *Protocol : Protocols)
7719b36e126SZixu Wang Container->Protocols.emplace_back(Protocol->getName(),
7729b36e126SZixu Wang API.recordUSR(Protocol));
7739b36e126SZixu Wang }
7749b36e126SZixu Wang
77589f6b26fSZixu Wang ASTContext &Context;
776a9909d23SDaniel Grumberg APISet &API;
777aebe5fc6SDaniel Grumberg LocationFileChecker &LCF;
77889f6b26fSZixu Wang };
77989f6b26fSZixu Wang
78089f6b26fSZixu Wang class ExtractAPIConsumer : public ASTConsumer {
78189f6b26fSZixu Wang public:
ExtractAPIConsumer(ASTContext & Context,std::unique_ptr<LocationFileChecker> LCF,APISet & API)782aebe5fc6SDaniel Grumberg ExtractAPIConsumer(ASTContext &Context,
783aebe5fc6SDaniel Grumberg std::unique_ptr<LocationFileChecker> LCF, APISet &API)
784aebe5fc6SDaniel Grumberg : Visitor(Context, *LCF, API), LCF(std::move(LCF)) {}
78589f6b26fSZixu Wang
HandleTranslationUnit(ASTContext & Context)78689f6b26fSZixu Wang void HandleTranslationUnit(ASTContext &Context) override {
78789f6b26fSZixu Wang // Use ExtractAPIVisitor to traverse symbol declarations in the context.
78889f6b26fSZixu Wang Visitor.TraverseDecl(Context.getTranslationUnitDecl());
78989f6b26fSZixu Wang }
79089f6b26fSZixu Wang
79189f6b26fSZixu Wang private:
79289f6b26fSZixu Wang ExtractAPIVisitor Visitor;
793aebe5fc6SDaniel Grumberg std::unique_ptr<LocationFileChecker> LCF;
79489f6b26fSZixu Wang };
79589f6b26fSZixu Wang
796529a0570SDaniel Grumberg class MacroCallback : public PPCallbacks {
797529a0570SDaniel Grumberg public:
MacroCallback(const SourceManager & SM,LocationFileChecker & LCF,APISet & API,Preprocessor & PP)79810155922SDaniel Grumberg MacroCallback(const SourceManager &SM, LocationFileChecker &LCF, APISet &API,
79910155922SDaniel Grumberg Preprocessor &PP)
80010155922SDaniel Grumberg : SM(SM), LCF(LCF), API(API), PP(PP) {}
801529a0570SDaniel Grumberg
MacroDefined(const Token & MacroNameToken,const MacroDirective * MD)802529a0570SDaniel Grumberg void MacroDefined(const Token &MacroNameToken,
803529a0570SDaniel Grumberg const MacroDirective *MD) override {
804529a0570SDaniel Grumberg auto *MacroInfo = MD->getMacroInfo();
805529a0570SDaniel Grumberg
806529a0570SDaniel Grumberg if (MacroInfo->isBuiltinMacro())
807529a0570SDaniel Grumberg return;
808529a0570SDaniel Grumberg
809529a0570SDaniel Grumberg auto SourceLoc = MacroNameToken.getLocation();
810529a0570SDaniel Grumberg if (SM.isWrittenInBuiltinFile(SourceLoc) ||
811529a0570SDaniel Grumberg SM.isWrittenInCommandLineFile(SourceLoc))
812529a0570SDaniel Grumberg return;
813529a0570SDaniel Grumberg
814529a0570SDaniel Grumberg PendingMacros.emplace_back(MacroNameToken, MD);
815529a0570SDaniel Grumberg }
816529a0570SDaniel Grumberg
817529a0570SDaniel Grumberg // If a macro gets undefined at some point during preprocessing of the inputs
818529a0570SDaniel Grumberg // it means that it isn't an exposed API and we should therefore not add a
819529a0570SDaniel Grumberg // macro definition for it.
MacroUndefined(const Token & MacroNameToken,const MacroDefinition & MD,const MacroDirective * Undef)820529a0570SDaniel Grumberg void MacroUndefined(const Token &MacroNameToken, const MacroDefinition &MD,
821529a0570SDaniel Grumberg const MacroDirective *Undef) override {
8228b63622bSDaniel Grumberg // If this macro wasn't previously defined we don't need to do anything
8238b63622bSDaniel Grumberg // here.
8248b63622bSDaniel Grumberg if (!Undef)
8258b63622bSDaniel Grumberg return;
8268b63622bSDaniel Grumberg
82710155922SDaniel Grumberg llvm::erase_if(PendingMacros, [&MD, this](const PendingMacro &PM) {
82810155922SDaniel Grumberg return MD.getMacroInfo()->isIdenticalTo(*PM.MD->getMacroInfo(), PP,
82910155922SDaniel Grumberg /*Syntactically*/ false);
830529a0570SDaniel Grumberg });
831529a0570SDaniel Grumberg }
832529a0570SDaniel Grumberg
EndOfMainFile()833529a0570SDaniel Grumberg void EndOfMainFile() override {
834529a0570SDaniel Grumberg for (auto &PM : PendingMacros) {
835529a0570SDaniel Grumberg // `isUsedForHeaderGuard` is only set when the preprocessor leaves the
836529a0570SDaniel Grumberg // file so check for it here.
837529a0570SDaniel Grumberg if (PM.MD->getMacroInfo()->isUsedForHeaderGuard())
838529a0570SDaniel Grumberg continue;
839529a0570SDaniel Grumberg
840aebe5fc6SDaniel Grumberg if (!LCF.isLocationInKnownFile(PM.MacroNameToken.getLocation()))
841aebe5fc6SDaniel Grumberg continue;
842aebe5fc6SDaniel Grumberg
843529a0570SDaniel Grumberg StringRef Name = PM.MacroNameToken.getIdentifierInfo()->getName();
844529a0570SDaniel Grumberg PresumedLoc Loc = SM.getPresumedLoc(PM.MacroNameToken.getLocation());
845529a0570SDaniel Grumberg StringRef USR =
846529a0570SDaniel Grumberg API.recordUSRForMacro(Name, PM.MacroNameToken.getLocation(), SM);
847529a0570SDaniel Grumberg
848529a0570SDaniel Grumberg API.addMacroDefinition(
849529a0570SDaniel Grumberg Name, USR, Loc,
850529a0570SDaniel Grumberg DeclarationFragmentsBuilder::getFragmentsForMacro(Name, PM.MD),
851529a0570SDaniel Grumberg DeclarationFragmentsBuilder::getSubHeadingForMacro(Name));
852529a0570SDaniel Grumberg }
853529a0570SDaniel Grumberg
854529a0570SDaniel Grumberg PendingMacros.clear();
855529a0570SDaniel Grumberg }
856529a0570SDaniel Grumberg
857529a0570SDaniel Grumberg private:
858529a0570SDaniel Grumberg struct PendingMacro {
859529a0570SDaniel Grumberg Token MacroNameToken;
860529a0570SDaniel Grumberg const MacroDirective *MD;
861529a0570SDaniel Grumberg
PendingMacro__anon4a2c2dcf0111::MacroCallback::PendingMacro862529a0570SDaniel Grumberg PendingMacro(const Token &MacroNameToken, const MacroDirective *MD)
863529a0570SDaniel Grumberg : MacroNameToken(MacroNameToken), MD(MD) {}
864529a0570SDaniel Grumberg };
865529a0570SDaniel Grumberg
866529a0570SDaniel Grumberg const SourceManager &SM;
867aebe5fc6SDaniel Grumberg LocationFileChecker &LCF;
868529a0570SDaniel Grumberg APISet &API;
86910155922SDaniel Grumberg Preprocessor &PP;
870529a0570SDaniel Grumberg llvm::SmallVector<PendingMacro> PendingMacros;
871529a0570SDaniel Grumberg };
872529a0570SDaniel Grumberg
87389f6b26fSZixu Wang } // namespace
87489f6b26fSZixu Wang
87589f6b26fSZixu Wang std::unique_ptr<ASTConsumer>
CreateASTConsumer(CompilerInstance & CI,StringRef InFile)87689f6b26fSZixu Wang ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
877a9909d23SDaniel Grumberg OS = CreateOutputFile(CI, InFile);
87889f6b26fSZixu Wang if (!OS)
87989f6b26fSZixu Wang return nullptr;
880a9909d23SDaniel Grumberg
881a9909d23SDaniel Grumberg ProductName = CI.getFrontendOpts().ProductName;
882a9909d23SDaniel Grumberg
883a9909d23SDaniel Grumberg // Now that we have enough information about the language options and the
884a9909d23SDaniel Grumberg // target triple, let's create the APISet before anyone uses it.
885a9909d23SDaniel Grumberg API = std::make_unique<APISet>(
886a9909d23SDaniel Grumberg CI.getTarget().getTriple(),
887a9909d23SDaniel Grumberg CI.getFrontendOpts().Inputs.back().getKind().getLanguage());
888a9909d23SDaniel Grumberg
889cb5bb285SZixu Wang auto LCF = std::make_unique<LocationFileChecker>(CI, KnownInputFiles);
890aebe5fc6SDaniel Grumberg
89110155922SDaniel Grumberg CI.getPreprocessor().addPPCallbacks(std::make_unique<MacroCallback>(
89210155922SDaniel Grumberg CI.getSourceManager(), *LCF, *API, CI.getPreprocessor()));
893529a0570SDaniel Grumberg
894aebe5fc6SDaniel Grumberg return std::make_unique<ExtractAPIConsumer>(CI.getASTContext(),
895aebe5fc6SDaniel Grumberg std::move(LCF), *API);
89689f6b26fSZixu Wang }
89789f6b26fSZixu Wang
PrepareToExecuteAction(CompilerInstance & CI)898f833aab0SDaniel Grumberg bool ExtractAPIAction::PrepareToExecuteAction(CompilerInstance &CI) {
899f833aab0SDaniel Grumberg auto &Inputs = CI.getFrontendOpts().Inputs;
900f833aab0SDaniel Grumberg if (Inputs.empty())
901f833aab0SDaniel Grumberg return true;
902f833aab0SDaniel Grumberg
903cb5bb285SZixu Wang if (!CI.hasFileManager())
904cb5bb285SZixu Wang if (!CI.createFileManager())
905cb5bb285SZixu Wang return false;
906cb5bb285SZixu Wang
907f833aab0SDaniel Grumberg auto Kind = Inputs[0].getKind();
908f833aab0SDaniel Grumberg
909f833aab0SDaniel Grumberg // Convert the header file inputs into a single input buffer.
910f833aab0SDaniel Grumberg SmallString<256> HeaderContents;
911cb5bb285SZixu Wang bool IsQuoted = false;
912f833aab0SDaniel Grumberg for (const FrontendInputFile &FIF : Inputs) {
913f833aab0SDaniel Grumberg if (Kind.isObjectiveC())
914f833aab0SDaniel Grumberg HeaderContents += "#import";
915f833aab0SDaniel Grumberg else
916f833aab0SDaniel Grumberg HeaderContents += "#include";
9174c262feeSZixu Wang
918cb5bb285SZixu Wang StringRef FilePath = FIF.getFile();
919cb5bb285SZixu Wang if (auto RelativeName = getRelativeIncludeName(CI, FilePath, &IsQuoted)) {
920cb5bb285SZixu Wang if (IsQuoted)
921cb5bb285SZixu Wang HeaderContents += " \"";
922cb5bb285SZixu Wang else
923cb5bb285SZixu Wang HeaderContents += " <";
924cb5bb285SZixu Wang
925cb5bb285SZixu Wang HeaderContents += *RelativeName;
926cb5bb285SZixu Wang
927cb5bb285SZixu Wang if (IsQuoted)
928cb5bb285SZixu Wang HeaderContents += "\"\n";
929cb5bb285SZixu Wang else
930cb5bb285SZixu Wang HeaderContents += ">\n";
931cc344d26SAdrian Kuegel KnownInputFiles.emplace_back(static_cast<SmallString<32>>(*RelativeName),
932cc344d26SAdrian Kuegel IsQuoted);
933cb5bb285SZixu Wang } else {
934cb5bb285SZixu Wang HeaderContents += " \"";
935cb5bb285SZixu Wang HeaderContents += FilePath;
936cb5bb285SZixu Wang HeaderContents += "\"\n";
937cb5bb285SZixu Wang KnownInputFiles.emplace_back(FilePath, true);
9382966f0faSZixu Wang }
939cb5bb285SZixu Wang }
940cb5bb285SZixu Wang
941cb5bb285SZixu Wang if (CI.getHeaderSearchOpts().Verbose)
942cb5bb285SZixu Wang CI.getVerboseOutputStream() << getInputBufferName() << ":\n"
943cb5bb285SZixu Wang << HeaderContents << "\n";
944f833aab0SDaniel Grumberg
945985eaa1aSDaniel Grumberg Buffer = llvm::MemoryBuffer::getMemBufferCopy(HeaderContents,
946f833aab0SDaniel Grumberg getInputBufferName());
947f833aab0SDaniel Grumberg
948f833aab0SDaniel Grumberg // Set that buffer up as our "real" input in the CompilerInstance.
949f833aab0SDaniel Grumberg Inputs.clear();
950f833aab0SDaniel Grumberg Inputs.emplace_back(Buffer->getMemBufferRef(), Kind, /*IsSystem*/ false);
951f833aab0SDaniel Grumberg
952f833aab0SDaniel Grumberg return true;
953f833aab0SDaniel Grumberg }
954f833aab0SDaniel Grumberg
EndSourceFileAction()955a9909d23SDaniel Grumberg void ExtractAPIAction::EndSourceFileAction() {
956a9909d23SDaniel Grumberg if (!OS)
957a9909d23SDaniel Grumberg return;
958a9909d23SDaniel Grumberg
959a9909d23SDaniel Grumberg // Setup a SymbolGraphSerializer to write out collected API information in
960a9909d23SDaniel Grumberg // the Symbol Graph format.
961a9909d23SDaniel Grumberg // FIXME: Make the kind of APISerializer configurable.
962a9909d23SDaniel Grumberg SymbolGraphSerializer SGSerializer(*API, ProductName);
963a9909d23SDaniel Grumberg SGSerializer.serialize(*OS);
9642d133867SDuncan P. N. Exon Smith OS.reset();
965a9909d23SDaniel Grumberg }
966a9909d23SDaniel Grumberg
96789f6b26fSZixu Wang std::unique_ptr<raw_pwrite_stream>
CreateOutputFile(CompilerInstance & CI,StringRef InFile)96889f6b26fSZixu Wang ExtractAPIAction::CreateOutputFile(CompilerInstance &CI, StringRef InFile) {
96989f6b26fSZixu Wang std::unique_ptr<raw_pwrite_stream> OS =
97089f6b26fSZixu Wang CI.createDefaultOutputFile(/*Binary=*/false, InFile, /*Extension=*/"json",
97189f6b26fSZixu Wang /*RemoveFileOnSignal=*/false);
97289f6b26fSZixu Wang if (!OS)
97389f6b26fSZixu Wang return nullptr;
97489f6b26fSZixu Wang return OS;
97589f6b26fSZixu Wang }
976