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