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