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*cb5bb285SZixu Wang #include "llvm/Support/FileSystem.h"
42f833aab0SDaniel Grumberg #include "llvm/Support/MemoryBuffer.h"
43*cb5bb285SZixu Wang #include "llvm/Support/Path.h"
44*cb5bb285SZixu 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*cb5bb285SZixu Wang Optional<std::string> getRelativeIncludeName(const CompilerInstance &CI,
62*cb5bb285SZixu Wang                                              StringRef File,
63*cb5bb285SZixu Wang                                              bool *IsQuoted = nullptr) {
64*cb5bb285SZixu Wang   assert(CI.hasFileManager() &&
65*cb5bb285SZixu Wang          "CompilerInstance does not have a FileNamager!");
66*cb5bb285SZixu Wang 
67*cb5bb285SZixu Wang   using namespace llvm::sys;
68*cb5bb285SZixu Wang   // Matches framework include patterns
69*cb5bb285SZixu Wang   const llvm::Regex Rule("/(.+)\\.framework/(.+)?Headers/(.+)");
70*cb5bb285SZixu Wang 
71*cb5bb285SZixu Wang   const auto &FS = CI.getVirtualFileSystem();
72*cb5bb285SZixu Wang 
73*cb5bb285SZixu Wang   SmallString<128> FilePath(File.begin(), File.end());
74*cb5bb285SZixu Wang   FS.makeAbsolute(FilePath);
75*cb5bb285SZixu Wang   path::remove_dots(FilePath, true);
76*cb5bb285SZixu Wang   FilePath = path::convert_to_slash(FilePath);
77*cb5bb285SZixu Wang   File = FilePath;
78*cb5bb285SZixu Wang 
79*cb5bb285SZixu Wang   // Checks whether `Dir` is a strict path prefix of `File`. If so returns
80*cb5bb285SZixu Wang   // the prefix length. Otherwise return 0.
81*cb5bb285SZixu Wang   auto CheckDir = [&](llvm::StringRef Dir) -> unsigned {
82*cb5bb285SZixu Wang     llvm::SmallString<32> DirPath(Dir.begin(), Dir.end());
83*cb5bb285SZixu Wang     FS.makeAbsolute(DirPath);
84*cb5bb285SZixu Wang     path::remove_dots(DirPath, true);
85*cb5bb285SZixu Wang     Dir = DirPath;
86*cb5bb285SZixu Wang     for (auto NI = path::begin(File), NE = path::end(File),
87*cb5bb285SZixu Wang               DI = path::begin(Dir), DE = path::end(Dir);
88*cb5bb285SZixu Wang          /*termination condition in loop*/; ++NI, ++DI) {
89*cb5bb285SZixu Wang       // '.' components in File are ignored.
90*cb5bb285SZixu Wang       while (NI != NE && *NI == ".")
91*cb5bb285SZixu Wang         ++NI;
92*cb5bb285SZixu Wang       if (NI == NE)
93*cb5bb285SZixu Wang         break;
94*cb5bb285SZixu Wang 
95*cb5bb285SZixu Wang       // '.' components in Dir are ignored.
96*cb5bb285SZixu Wang       while (DI != DE && *DI == ".")
97*cb5bb285SZixu Wang         ++DI;
98*cb5bb285SZixu Wang 
99*cb5bb285SZixu Wang       // Dir is a prefix of File, up to '.' components and choice of path
100*cb5bb285SZixu Wang       // separators.
101*cb5bb285SZixu Wang       if (DI == DE)
102*cb5bb285SZixu Wang         return NI - path::begin(File);
103*cb5bb285SZixu Wang 
104*cb5bb285SZixu Wang       // Consider all path separators equal.
105*cb5bb285SZixu Wang       if (NI->size() == 1 && DI->size() == 1 &&
106*cb5bb285SZixu Wang           path::is_separator(NI->front()) && path::is_separator(DI->front()))
107*cb5bb285SZixu Wang         continue;
108*cb5bb285SZixu Wang 
109*cb5bb285SZixu Wang       // Special case Apple .sdk folders since the search path is typically a
110*cb5bb285SZixu Wang       // symlink like `iPhoneSimulator14.5.sdk` while the file is instead
111*cb5bb285SZixu Wang       // located in `iPhoneSimulator.sdk` (the real folder).
112*cb5bb285SZixu Wang       if (NI->endswith(".sdk") && DI->endswith(".sdk")) {
113*cb5bb285SZixu Wang         StringRef NBasename = path::stem(*NI);
114*cb5bb285SZixu Wang         StringRef DBasename = path::stem(*DI);
115*cb5bb285SZixu Wang         if (DBasename.startswith(NBasename))
116*cb5bb285SZixu Wang           continue;
117*cb5bb285SZixu Wang       }
118*cb5bb285SZixu Wang 
119*cb5bb285SZixu Wang       if (*NI != *DI)
120*cb5bb285SZixu Wang         break;
121*cb5bb285SZixu Wang     }
122*cb5bb285SZixu Wang     return 0;
123*cb5bb285SZixu Wang   };
124*cb5bb285SZixu Wang 
125*cb5bb285SZixu Wang   unsigned PrefixLength = 0;
126*cb5bb285SZixu Wang 
127*cb5bb285SZixu Wang   // Go through the search paths and find the first one that is a prefix of
128*cb5bb285SZixu Wang   // the header.
129*cb5bb285SZixu Wang   for (const auto &Entry : CI.getHeaderSearchOpts().UserEntries) {
130*cb5bb285SZixu Wang     // Note whether the match is found in a quoted entry.
131*cb5bb285SZixu Wang     if (IsQuoted)
132*cb5bb285SZixu Wang       *IsQuoted = Entry.Group == frontend::Quoted;
133*cb5bb285SZixu Wang 
134*cb5bb285SZixu Wang     if (auto EntryFile = CI.getFileManager().getOptionalFileRef(Entry.Path)) {
135*cb5bb285SZixu Wang       if (auto HMap = HeaderMap::Create(*EntryFile, CI.getFileManager())) {
136*cb5bb285SZixu Wang         // If this is a headermap entry, try to reverse lookup the full path
137*cb5bb285SZixu Wang         // for a spelled name before mapping.
138*cb5bb285SZixu Wang         StringRef SpelledFilename = HMap->reverseLookupFilename(File);
139*cb5bb285SZixu Wang         if (!SpelledFilename.empty())
140*cb5bb285SZixu Wang           return SpelledFilename.str();
141*cb5bb285SZixu Wang 
142*cb5bb285SZixu Wang         // No matching mapping in this headermap, try next search entry.
143*cb5bb285SZixu Wang         continue;
144*cb5bb285SZixu Wang       }
145*cb5bb285SZixu Wang     }
146*cb5bb285SZixu Wang 
147*cb5bb285SZixu Wang     // Entry is a directory search entry, try to check if it's a prefix of File.
148*cb5bb285SZixu Wang     PrefixLength = CheckDir(Entry.Path);
149*cb5bb285SZixu Wang     if (PrefixLength > 0) {
150*cb5bb285SZixu Wang       // The header is found in a framework path, construct the framework-style
151*cb5bb285SZixu Wang       // include name `<Framework/Header.h>`
152*cb5bb285SZixu Wang       if (Entry.IsFramework) {
153*cb5bb285SZixu Wang         SmallVector<StringRef, 4> Matches;
154*cb5bb285SZixu Wang         Rule.match(File, &Matches);
155*cb5bb285SZixu Wang         // Returned matches are always in stable order.
156*cb5bb285SZixu Wang         if (Matches.size() != 4)
157*cb5bb285SZixu Wang           return None;
158*cb5bb285SZixu Wang 
159*cb5bb285SZixu Wang         return path::convert_to_slash(
160*cb5bb285SZixu Wang             (Matches[1].drop_front(Matches[1].rfind('/') + 1) + "/" +
161*cb5bb285SZixu Wang              Matches[3])
162*cb5bb285SZixu Wang                 .str());
163*cb5bb285SZixu Wang       }
164*cb5bb285SZixu Wang 
165*cb5bb285SZixu Wang       // The header is found in a normal search path, strip the search path
166*cb5bb285SZixu Wang       // prefix to get an include name.
167*cb5bb285SZixu Wang       return path::convert_to_slash(File.drop_front(PrefixLength));
168*cb5bb285SZixu Wang     }
169*cb5bb285SZixu Wang   }
170*cb5bb285SZixu Wang 
171*cb5bb285SZixu Wang   // Couldn't determine a include name, use full path instead.
172*cb5bb285SZixu Wang   return None;
173*cb5bb285SZixu Wang }
174*cb5bb285SZixu Wang 
175aebe5fc6SDaniel Grumberg struct LocationFileChecker {
176aebe5fc6SDaniel 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.
179*cb5bb285SZixu 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 
192*cb5bb285SZixu Wang     if (ExternalFileEntries.count(File))
193*cb5bb285SZixu Wang       return false;
194*cb5bb285SZixu Wang 
195*cb5bb285SZixu Wang     StringRef FileName = File->tryGetRealPathName().empty()
196*cb5bb285SZixu Wang                              ? File->getName()
197*cb5bb285SZixu Wang                              : File->tryGetRealPathName();
198*cb5bb285SZixu Wang 
199*cb5bb285SZixu Wang     // Try to reduce the include name the same way we tried to include it.
200*cb5bb285SZixu Wang     bool IsQuoted = false;
201*cb5bb285SZixu Wang     if (auto IncludeName = getRelativeIncludeName(CI, FileName, &IsQuoted))
202*cb5bb285SZixu Wang       if (llvm::find_if(KnownFiles,
203*cb5bb285SZixu Wang                         [&IsQuoted, &IncludeName](const auto &KnownFile) {
204*cb5bb285SZixu Wang                           return KnownFile.first.equals(*IncludeName) &&
205*cb5bb285SZixu Wang                                  KnownFile.second == IsQuoted;
206*cb5bb285SZixu Wang                         }) != KnownFiles.end()) {
207*cb5bb285SZixu Wang         KnownFileEntries.insert(File);
208*cb5bb285SZixu Wang         return true;
209*cb5bb285SZixu Wang       }
210*cb5bb285SZixu Wang 
211*cb5bb285SZixu Wang     // Record that the file was not found to avoid future reverse lookup for
212*cb5bb285SZixu Wang     // the same file.
213*cb5bb285SZixu Wang     ExternalFileEntries.insert(File);
214aebe5fc6SDaniel Grumberg     return false;
215aebe5fc6SDaniel Grumberg   }
216aebe5fc6SDaniel Grumberg 
217*cb5bb285SZixu Wang   LocationFileChecker(const CompilerInstance &CI,
218*cb5bb285SZixu Wang                       SmallVector<std::pair<SmallString<32>, bool>> &KnownFiles)
219*cb5bb285SZixu Wang       : CI(CI), KnownFiles(KnownFiles), ExternalFileEntries() {
220*cb5bb285SZixu Wang     for (const auto &KnownFile : KnownFiles)
221*cb5bb285SZixu Wang       if (auto FileEntry = CI.getFileManager().getFile(KnownFile.first))
222aebe5fc6SDaniel Grumberg         KnownFileEntries.insert(*FileEntry);
223aebe5fc6SDaniel Grumberg   }
224aebe5fc6SDaniel Grumberg 
225aebe5fc6SDaniel Grumberg private:
226*cb5bb285SZixu Wang   const CompilerInstance &CI;
227*cb5bb285SZixu Wang   SmallVector<std::pair<SmallString<32>, bool>> &KnownFiles;
228aebe5fc6SDaniel Grumberg   llvm::DenseSet<const FileEntry *> KnownFileEntries;
229*cb5bb285SZixu 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:
236aebe5fc6SDaniel Grumberg   ExtractAPIVisitor(ASTContext &Context, LocationFileChecker &LCF, APISet &API)
237aebe5fc6SDaniel Grumberg       : Context(Context), API(API), LCF(LCF) {}
23889f6b26fSZixu Wang 
23989f6b26fSZixu Wang   const APISet &getAPI() const { return API; }
24089f6b26fSZixu Wang 
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 
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:
30689f6b26fSZixu Wang       break;
30789f6b26fSZixu Wang     case FunctionDecl::TK_MemberSpecialization:
30889f6b26fSZixu Wang     case FunctionDecl::TK_FunctionTemplateSpecialization:
30989f6b26fSZixu Wang       if (auto *TemplateInfo = Decl->getTemplateSpecializationInfo()) {
31089f6b26fSZixu Wang         if (!TemplateInfo->isExplicitInstantiationOrSpecialization())
31189f6b26fSZixu Wang           return true;
31289f6b26fSZixu Wang       }
31389f6b26fSZixu Wang       break;
31489f6b26fSZixu Wang     case FunctionDecl::TK_FunctionTemplate:
31589f6b26fSZixu Wang     case FunctionDecl::TK_DependentFunctionTemplateSpecialization:
31689f6b26fSZixu Wang       return true;
31789f6b26fSZixu Wang     }
31889f6b26fSZixu Wang 
319aebe5fc6SDaniel Grumberg     if (!LCF.isLocationInKnownFile(Decl->getLocation()))
320aebe5fc6SDaniel Grumberg       return true;
321aebe5fc6SDaniel Grumberg 
32289f6b26fSZixu Wang     // Collect symbol information.
32389f6b26fSZixu Wang     StringRef Name = Decl->getName();
32489f6b26fSZixu Wang     StringRef USR = API.recordUSR(Decl);
32589f6b26fSZixu Wang     PresumedLoc Loc =
32689f6b26fSZixu Wang         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
32789f6b26fSZixu Wang     AvailabilityInfo Availability = getAvailability(Decl);
32889f6b26fSZixu Wang     LinkageInfo Linkage = Decl->getLinkageAndVisibility();
32989f6b26fSZixu Wang     DocComment Comment;
33089f6b26fSZixu Wang     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
33189f6b26fSZixu Wang       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
33289f6b26fSZixu Wang                                               Context.getDiagnostics());
33389f6b26fSZixu Wang 
33489f6b26fSZixu Wang     // Build declaration fragments, sub-heading, and signature of the function.
33589f6b26fSZixu Wang     DeclarationFragments Declaration =
33689f6b26fSZixu Wang         DeclarationFragmentsBuilder::getFragmentsForFunction(Decl);
33789f6b26fSZixu Wang     DeclarationFragments SubHeading =
33889f6b26fSZixu Wang         DeclarationFragmentsBuilder::getSubHeading(Decl);
33989f6b26fSZixu Wang     FunctionSignature Signature =
34089f6b26fSZixu Wang         DeclarationFragmentsBuilder::getFunctionSignature(Decl);
34189f6b26fSZixu Wang 
34289f6b26fSZixu Wang     // Add the function record to the API set.
343236b6a0eSDaniel Grumberg     API.addGlobalFunction(Name, USR, Loc, Availability, Linkage, Comment,
344236b6a0eSDaniel Grumberg                           Declaration, SubHeading, Signature);
34589f6b26fSZixu Wang     return true;
34689f6b26fSZixu Wang   }
34789f6b26fSZixu Wang 
34871b4c226SZixu Wang   bool VisitEnumDecl(const EnumDecl *Decl) {
34971b4c226SZixu Wang     if (!Decl->isComplete())
35071b4c226SZixu Wang       return true;
35171b4c226SZixu Wang 
35271b4c226SZixu Wang     // Skip forward declaration.
35371b4c226SZixu Wang     if (!Decl->isThisDeclarationADefinition())
35471b4c226SZixu Wang       return true;
35571b4c226SZixu Wang 
356aebe5fc6SDaniel Grumberg     if (!LCF.isLocationInKnownFile(Decl->getLocation()))
357aebe5fc6SDaniel Grumberg       return true;
358aebe5fc6SDaniel Grumberg 
35971b4c226SZixu Wang     // Collect symbol information.
3607443a504SDaniel Grumberg     std::string NameString = Decl->getQualifiedNameAsString();
3617443a504SDaniel Grumberg     StringRef Name(NameString);
3629fc45ca0SDaniel Grumberg     if (Name.empty())
3639fc45ca0SDaniel Grumberg       Name = getTypedefName(Decl);
3647443a504SDaniel Grumberg 
36571b4c226SZixu Wang     StringRef USR = API.recordUSR(Decl);
36671b4c226SZixu Wang     PresumedLoc Loc =
36771b4c226SZixu Wang         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
36871b4c226SZixu Wang     AvailabilityInfo Availability = getAvailability(Decl);
36971b4c226SZixu Wang     DocComment Comment;
37071b4c226SZixu Wang     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
37171b4c226SZixu Wang       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
37271b4c226SZixu Wang                                               Context.getDiagnostics());
37371b4c226SZixu Wang 
37471b4c226SZixu Wang     // Build declaration fragments and sub-heading for the enum.
37571b4c226SZixu Wang     DeclarationFragments Declaration =
37671b4c226SZixu Wang         DeclarationFragmentsBuilder::getFragmentsForEnum(Decl);
37771b4c226SZixu Wang     DeclarationFragments SubHeading =
37871b4c226SZixu Wang         DeclarationFragmentsBuilder::getSubHeading(Decl);
37971b4c226SZixu Wang 
3807443a504SDaniel Grumberg     EnumRecord *EnumRecord =
3817443a504SDaniel Grumberg         API.addEnum(API.copyString(Name), USR, Loc, Availability, Comment,
38271b4c226SZixu Wang                     Declaration, SubHeading);
38371b4c226SZixu Wang 
38471b4c226SZixu Wang     // Now collect information about the enumerators in this enum.
38571b4c226SZixu Wang     recordEnumConstants(EnumRecord, Decl->enumerators());
38671b4c226SZixu Wang 
38771b4c226SZixu Wang     return true;
38871b4c226SZixu Wang   }
38971b4c226SZixu Wang 
3905bb5704cSZixu Wang   bool VisitRecordDecl(const RecordDecl *Decl) {
3915bb5704cSZixu Wang     if (!Decl->isCompleteDefinition())
3925bb5704cSZixu Wang       return true;
3935bb5704cSZixu Wang 
3945bb5704cSZixu Wang     // Skip C++ structs/classes/unions
3955bb5704cSZixu Wang     // TODO: support C++ records
3965bb5704cSZixu Wang     if (isa<CXXRecordDecl>(Decl))
3975bb5704cSZixu Wang       return true;
3985bb5704cSZixu Wang 
399aebe5fc6SDaniel Grumberg     if (!LCF.isLocationInKnownFile(Decl->getLocation()))
400aebe5fc6SDaniel Grumberg       return true;
401aebe5fc6SDaniel Grumberg 
4025bb5704cSZixu Wang     // Collect symbol information.
4035bb5704cSZixu Wang     StringRef Name = Decl->getName();
4049fc45ca0SDaniel Grumberg     if (Name.empty())
4059fc45ca0SDaniel Grumberg       Name = getTypedefName(Decl);
4065bb5704cSZixu Wang     StringRef USR = API.recordUSR(Decl);
4075bb5704cSZixu Wang     PresumedLoc Loc =
4085bb5704cSZixu Wang         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
4095bb5704cSZixu Wang     AvailabilityInfo Availability = getAvailability(Decl);
4105bb5704cSZixu Wang     DocComment Comment;
4115bb5704cSZixu Wang     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
4125bb5704cSZixu Wang       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
4135bb5704cSZixu Wang                                               Context.getDiagnostics());
4145bb5704cSZixu Wang 
4155bb5704cSZixu Wang     // Build declaration fragments and sub-heading for the struct.
4165bb5704cSZixu Wang     DeclarationFragments Declaration =
4175bb5704cSZixu Wang         DeclarationFragmentsBuilder::getFragmentsForStruct(Decl);
4185bb5704cSZixu Wang     DeclarationFragments SubHeading =
4195bb5704cSZixu Wang         DeclarationFragmentsBuilder::getSubHeading(Decl);
4205bb5704cSZixu Wang 
4215bb5704cSZixu Wang     StructRecord *StructRecord = API.addStruct(
4225bb5704cSZixu Wang         Name, USR, Loc, Availability, Comment, Declaration, SubHeading);
4235bb5704cSZixu Wang 
4245bb5704cSZixu Wang     // Now collect information about the fields in this struct.
4255bb5704cSZixu Wang     recordStructFields(StructRecord, Decl->fields());
4265bb5704cSZixu Wang 
4275bb5704cSZixu Wang     return true;
4285bb5704cSZixu Wang   }
4295bb5704cSZixu Wang 
4309b36e126SZixu Wang   bool VisitObjCInterfaceDecl(const ObjCInterfaceDecl *Decl) {
4319b36e126SZixu Wang     // Skip forward declaration for classes (@class)
4329b36e126SZixu Wang     if (!Decl->isThisDeclarationADefinition())
4339b36e126SZixu Wang       return true;
4349b36e126SZixu Wang 
435aebe5fc6SDaniel Grumberg     if (!LCF.isLocationInKnownFile(Decl->getLocation()))
436aebe5fc6SDaniel Grumberg       return true;
437aebe5fc6SDaniel Grumberg 
4389b36e126SZixu Wang     // Collect symbol information.
4399b36e126SZixu Wang     StringRef Name = Decl->getName();
4409b36e126SZixu Wang     StringRef USR = API.recordUSR(Decl);
4419b36e126SZixu Wang     PresumedLoc Loc =
4429b36e126SZixu Wang         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
4439b36e126SZixu Wang     AvailabilityInfo Availability = getAvailability(Decl);
4449b36e126SZixu Wang     LinkageInfo Linkage = Decl->getLinkageAndVisibility();
4459b36e126SZixu Wang     DocComment Comment;
4469b36e126SZixu Wang     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
4479b36e126SZixu Wang       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
4489b36e126SZixu Wang                                               Context.getDiagnostics());
4499b36e126SZixu Wang 
4509b36e126SZixu Wang     // Build declaration fragments and sub-heading for the interface.
4519b36e126SZixu Wang     DeclarationFragments Declaration =
4529b36e126SZixu Wang         DeclarationFragmentsBuilder::getFragmentsForObjCInterface(Decl);
4539b36e126SZixu Wang     DeclarationFragments SubHeading =
4549b36e126SZixu Wang         DeclarationFragmentsBuilder::getSubHeading(Decl);
4559b36e126SZixu Wang 
4569b36e126SZixu Wang     // Collect super class information.
4579b36e126SZixu Wang     SymbolReference SuperClass;
4589b36e126SZixu Wang     if (const auto *SuperClassDecl = Decl->getSuperClass()) {
4599b36e126SZixu Wang       SuperClass.Name = SuperClassDecl->getObjCRuntimeNameAsString();
4609b36e126SZixu Wang       SuperClass.USR = API.recordUSR(SuperClassDecl);
4619b36e126SZixu Wang     }
4629b36e126SZixu Wang 
4639b36e126SZixu Wang     ObjCInterfaceRecord *ObjCInterfaceRecord =
4649b36e126SZixu Wang         API.addObjCInterface(Name, USR, Loc, Availability, Linkage, Comment,
4659b36e126SZixu Wang                              Declaration, SubHeading, SuperClass);
4669b36e126SZixu Wang 
4679b36e126SZixu Wang     // Record all methods (selectors). This doesn't include automatically
4689b36e126SZixu Wang     // synthesized property methods.
4699b36e126SZixu Wang     recordObjCMethods(ObjCInterfaceRecord, Decl->methods());
4709b36e126SZixu Wang     recordObjCProperties(ObjCInterfaceRecord, Decl->properties());
4719b36e126SZixu Wang     recordObjCInstanceVariables(ObjCInterfaceRecord, Decl->ivars());
4729b36e126SZixu Wang     recordObjCProtocols(ObjCInterfaceRecord, Decl->protocols());
4739b36e126SZixu Wang 
4749b36e126SZixu Wang     return true;
4759b36e126SZixu Wang   }
4769b36e126SZixu Wang 
477d1d34bafSZixu Wang   bool VisitObjCProtocolDecl(const ObjCProtocolDecl *Decl) {
478d1d34bafSZixu Wang     // Skip forward declaration for protocols (@protocol).
479d1d34bafSZixu Wang     if (!Decl->isThisDeclarationADefinition())
480d1d34bafSZixu Wang       return true;
481d1d34bafSZixu Wang 
482aebe5fc6SDaniel Grumberg     if (!LCF.isLocationInKnownFile(Decl->getLocation()))
483aebe5fc6SDaniel Grumberg       return true;
484aebe5fc6SDaniel Grumberg 
485d1d34bafSZixu Wang     // Collect symbol information.
486d1d34bafSZixu Wang     StringRef Name = Decl->getName();
487d1d34bafSZixu Wang     StringRef USR = API.recordUSR(Decl);
488d1d34bafSZixu Wang     PresumedLoc Loc =
489d1d34bafSZixu Wang         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
490d1d34bafSZixu Wang     AvailabilityInfo Availability = getAvailability(Decl);
491d1d34bafSZixu Wang     DocComment Comment;
492d1d34bafSZixu Wang     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
493d1d34bafSZixu Wang       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
494d1d34bafSZixu Wang                                               Context.getDiagnostics());
495d1d34bafSZixu Wang 
496d1d34bafSZixu Wang     // Build declaration fragments and sub-heading for the protocol.
497d1d34bafSZixu Wang     DeclarationFragments Declaration =
498d1d34bafSZixu Wang         DeclarationFragmentsBuilder::getFragmentsForObjCProtocol(Decl);
499d1d34bafSZixu Wang     DeclarationFragments SubHeading =
500d1d34bafSZixu Wang         DeclarationFragmentsBuilder::getSubHeading(Decl);
501d1d34bafSZixu Wang 
502d1d34bafSZixu Wang     ObjCProtocolRecord *ObjCProtocolRecord = API.addObjCProtocol(
503d1d34bafSZixu Wang         Name, USR, Loc, Availability, Comment, Declaration, SubHeading);
504d1d34bafSZixu Wang 
505d1d34bafSZixu Wang     recordObjCMethods(ObjCProtocolRecord, Decl->methods());
506d1d34bafSZixu Wang     recordObjCProperties(ObjCProtocolRecord, Decl->properties());
507d1d34bafSZixu Wang     recordObjCProtocols(ObjCProtocolRecord, Decl->protocols());
508d1d34bafSZixu Wang 
509d1d34bafSZixu Wang     return true;
510d1d34bafSZixu Wang   }
511d1d34bafSZixu Wang 
5129fc45ca0SDaniel Grumberg   bool VisitTypedefNameDecl(const TypedefNameDecl *Decl) {
5139fc45ca0SDaniel Grumberg     // Skip ObjC Type Parameter for now.
5149fc45ca0SDaniel Grumberg     if (isa<ObjCTypeParamDecl>(Decl))
5159fc45ca0SDaniel Grumberg       return true;
5169fc45ca0SDaniel Grumberg 
5179fc45ca0SDaniel Grumberg     if (!Decl->isDefinedOutsideFunctionOrMethod())
5189fc45ca0SDaniel Grumberg       return true;
5199fc45ca0SDaniel Grumberg 
520aebe5fc6SDaniel Grumberg     if (!LCF.isLocationInKnownFile(Decl->getLocation()))
521aebe5fc6SDaniel Grumberg       return true;
522aebe5fc6SDaniel Grumberg 
5239fc45ca0SDaniel Grumberg     PresumedLoc Loc =
5249fc45ca0SDaniel Grumberg         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
5259fc45ca0SDaniel Grumberg     StringRef Name = Decl->getName();
5269fc45ca0SDaniel Grumberg     AvailabilityInfo Availability = getAvailability(Decl);
5279fc45ca0SDaniel Grumberg     StringRef USR = API.recordUSR(Decl);
5289fc45ca0SDaniel Grumberg     DocComment Comment;
5299fc45ca0SDaniel Grumberg     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
5309fc45ca0SDaniel Grumberg       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
5319fc45ca0SDaniel Grumberg                                               Context.getDiagnostics());
5329fc45ca0SDaniel Grumberg 
5339fc45ca0SDaniel Grumberg     QualType Type = Decl->getUnderlyingType();
5349fc45ca0SDaniel Grumberg     SymbolReference SymRef =
5359fc45ca0SDaniel Grumberg         TypedefUnderlyingTypeResolver(Context).getSymbolReferenceForType(Type,
5369fc45ca0SDaniel Grumberg                                                                          API);
5379fc45ca0SDaniel Grumberg 
5389fc45ca0SDaniel Grumberg     API.addTypedef(Name, USR, Loc, Availability, Comment,
5399fc45ca0SDaniel Grumberg                    DeclarationFragmentsBuilder::getFragmentsForTypedef(Decl),
5409fc45ca0SDaniel Grumberg                    DeclarationFragmentsBuilder::getSubHeading(Decl), SymRef);
5419fc45ca0SDaniel Grumberg 
5429fc45ca0SDaniel Grumberg     return true;
5439fc45ca0SDaniel Grumberg   }
5449fc45ca0SDaniel Grumberg 
545178aad9bSZixu Wang   bool VisitObjCCategoryDecl(const ObjCCategoryDecl *Decl) {
546178aad9bSZixu Wang     // Collect symbol information.
547178aad9bSZixu Wang     StringRef Name = Decl->getName();
548178aad9bSZixu Wang     StringRef USR = API.recordUSR(Decl);
549178aad9bSZixu Wang     PresumedLoc Loc =
550178aad9bSZixu Wang         Context.getSourceManager().getPresumedLoc(Decl->getLocation());
551178aad9bSZixu Wang     AvailabilityInfo Availability = getAvailability(Decl);
552178aad9bSZixu Wang     DocComment Comment;
553178aad9bSZixu Wang     if (auto *RawComment = Context.getRawCommentForDeclNoCache(Decl))
554178aad9bSZixu Wang       Comment = RawComment->getFormattedLines(Context.getSourceManager(),
555178aad9bSZixu Wang                                               Context.getDiagnostics());
556178aad9bSZixu Wang     // Build declaration fragments and sub-heading for the category.
557178aad9bSZixu Wang     DeclarationFragments Declaration =
558178aad9bSZixu Wang         DeclarationFragmentsBuilder::getFragmentsForObjCCategory(Decl);
559178aad9bSZixu Wang     DeclarationFragments SubHeading =
560178aad9bSZixu Wang         DeclarationFragmentsBuilder::getSubHeading(Decl);
561178aad9bSZixu Wang 
562178aad9bSZixu Wang     const ObjCInterfaceDecl *InterfaceDecl = Decl->getClassInterface();
563178aad9bSZixu Wang     SymbolReference Interface(InterfaceDecl->getName(),
564178aad9bSZixu Wang                               API.recordUSR(InterfaceDecl));
565178aad9bSZixu Wang 
566178aad9bSZixu Wang     ObjCCategoryRecord *ObjCCategoryRecord =
567178aad9bSZixu Wang         API.addObjCCategory(Name, USR, Loc, Availability, Comment, Declaration,
568178aad9bSZixu Wang                             SubHeading, Interface);
569178aad9bSZixu Wang 
570178aad9bSZixu Wang     recordObjCMethods(ObjCCategoryRecord, Decl->methods());
571178aad9bSZixu Wang     recordObjCProperties(ObjCCategoryRecord, Decl->properties());
572178aad9bSZixu Wang     recordObjCInstanceVariables(ObjCCategoryRecord, Decl->ivars());
573178aad9bSZixu Wang     recordObjCProtocols(ObjCCategoryRecord, Decl->protocols());
574178aad9bSZixu Wang 
575178aad9bSZixu Wang     return true;
576178aad9bSZixu Wang   }
577178aad9bSZixu Wang 
57889f6b26fSZixu Wang private:
57989f6b26fSZixu Wang   /// Get availability information of the declaration \p D.
58089f6b26fSZixu Wang   AvailabilityInfo getAvailability(const Decl *D) const {
58189f6b26fSZixu Wang     StringRef PlatformName = Context.getTargetInfo().getPlatformName();
58289f6b26fSZixu Wang 
58389f6b26fSZixu Wang     AvailabilityInfo Availability;
58489f6b26fSZixu Wang     // Collect availability attributes from all redeclarations.
58589f6b26fSZixu Wang     for (const auto *RD : D->redecls()) {
58689f6b26fSZixu Wang       for (const auto *A : RD->specific_attrs<AvailabilityAttr>()) {
58789f6b26fSZixu Wang         if (A->getPlatform()->getName() != PlatformName)
58889f6b26fSZixu Wang           continue;
58989f6b26fSZixu Wang         Availability = AvailabilityInfo(A->getIntroduced(), A->getDeprecated(),
59089f6b26fSZixu Wang                                         A->getObsoleted(), A->getUnavailable(),
59189f6b26fSZixu Wang                                         /* UnconditionallyDeprecated */ false,
59289f6b26fSZixu Wang                                         /* UnconditionallyUnavailable */ false);
59389f6b26fSZixu Wang         break;
59489f6b26fSZixu Wang       }
59589f6b26fSZixu Wang 
59689f6b26fSZixu Wang       if (const auto *A = RD->getAttr<UnavailableAttr>())
59789f6b26fSZixu Wang         if (!A->isImplicit()) {
59889f6b26fSZixu Wang           Availability.Unavailable = true;
59989f6b26fSZixu Wang           Availability.UnconditionallyUnavailable = true;
60089f6b26fSZixu Wang         }
60189f6b26fSZixu Wang 
60289f6b26fSZixu Wang       if (const auto *A = RD->getAttr<DeprecatedAttr>())
60389f6b26fSZixu Wang         if (!A->isImplicit())
60489f6b26fSZixu Wang           Availability.UnconditionallyDeprecated = true;
60589f6b26fSZixu Wang     }
60689f6b26fSZixu Wang 
60789f6b26fSZixu Wang     return Availability;
60889f6b26fSZixu Wang   }
60989f6b26fSZixu Wang 
61071b4c226SZixu Wang   /// Collect API information for the enum constants and associate with the
61171b4c226SZixu Wang   /// parent enum.
61271b4c226SZixu Wang   void recordEnumConstants(EnumRecord *EnumRecord,
61371b4c226SZixu Wang                            const EnumDecl::enumerator_range Constants) {
61471b4c226SZixu Wang     for (const auto *Constant : Constants) {
61571b4c226SZixu Wang       // Collect symbol information.
61671b4c226SZixu Wang       StringRef Name = Constant->getName();
61771b4c226SZixu Wang       StringRef USR = API.recordUSR(Constant);
61871b4c226SZixu Wang       PresumedLoc Loc =
61971b4c226SZixu Wang           Context.getSourceManager().getPresumedLoc(Constant->getLocation());
62071b4c226SZixu Wang       AvailabilityInfo Availability = getAvailability(Constant);
62171b4c226SZixu Wang       DocComment Comment;
62271b4c226SZixu Wang       if (auto *RawComment = Context.getRawCommentForDeclNoCache(Constant))
62371b4c226SZixu Wang         Comment = RawComment->getFormattedLines(Context.getSourceManager(),
62471b4c226SZixu Wang                                                 Context.getDiagnostics());
62571b4c226SZixu Wang 
62671b4c226SZixu Wang       // Build declaration fragments and sub-heading for the enum constant.
62771b4c226SZixu Wang       DeclarationFragments Declaration =
62871b4c226SZixu Wang           DeclarationFragmentsBuilder::getFragmentsForEnumConstant(Constant);
62971b4c226SZixu Wang       DeclarationFragments SubHeading =
63071b4c226SZixu Wang           DeclarationFragmentsBuilder::getSubHeading(Constant);
63171b4c226SZixu Wang 
63271b4c226SZixu Wang       API.addEnumConstant(EnumRecord, Name, USR, Loc, Availability, Comment,
63371b4c226SZixu Wang                           Declaration, SubHeading);
63471b4c226SZixu Wang     }
63571b4c226SZixu Wang   }
63671b4c226SZixu Wang 
6375bb5704cSZixu Wang   /// Collect API information for the struct fields and associate with the
6385bb5704cSZixu Wang   /// parent struct.
6395bb5704cSZixu Wang   void recordStructFields(StructRecord *StructRecord,
6405bb5704cSZixu Wang                           const RecordDecl::field_range Fields) {
6415bb5704cSZixu Wang     for (const auto *Field : Fields) {
6425bb5704cSZixu Wang       // Collect symbol information.
6435bb5704cSZixu Wang       StringRef Name = Field->getName();
6445bb5704cSZixu Wang       StringRef USR = API.recordUSR(Field);
6455bb5704cSZixu Wang       PresumedLoc Loc =
6465bb5704cSZixu Wang           Context.getSourceManager().getPresumedLoc(Field->getLocation());
6475bb5704cSZixu Wang       AvailabilityInfo Availability = getAvailability(Field);
6485bb5704cSZixu Wang       DocComment Comment;
6495bb5704cSZixu Wang       if (auto *RawComment = Context.getRawCommentForDeclNoCache(Field))
6505bb5704cSZixu Wang         Comment = RawComment->getFormattedLines(Context.getSourceManager(),
6515bb5704cSZixu Wang                                                 Context.getDiagnostics());
6525bb5704cSZixu Wang 
6535bb5704cSZixu Wang       // Build declaration fragments and sub-heading for the struct field.
6545bb5704cSZixu Wang       DeclarationFragments Declaration =
6555bb5704cSZixu Wang           DeclarationFragmentsBuilder::getFragmentsForField(Field);
6565bb5704cSZixu Wang       DeclarationFragments SubHeading =
6575bb5704cSZixu Wang           DeclarationFragmentsBuilder::getSubHeading(Field);
6585bb5704cSZixu Wang 
6595bb5704cSZixu Wang       API.addStructField(StructRecord, Name, USR, Loc, Availability, Comment,
6605bb5704cSZixu Wang                          Declaration, SubHeading);
6615bb5704cSZixu Wang     }
6625bb5704cSZixu Wang   }
6635bb5704cSZixu Wang 
6649b36e126SZixu Wang   /// Collect API information for the Objective-C methods and associate with the
6659b36e126SZixu Wang   /// parent container.
6669b36e126SZixu Wang   void recordObjCMethods(ObjCContainerRecord *Container,
6679b36e126SZixu Wang                          const ObjCContainerDecl::method_range Methods) {
6689b36e126SZixu Wang     for (const auto *Method : Methods) {
6699b36e126SZixu Wang       // Don't record selectors for properties.
6709b36e126SZixu Wang       if (Method->isPropertyAccessor())
6719b36e126SZixu Wang         continue;
6729b36e126SZixu Wang 
6739b36e126SZixu Wang       StringRef Name = API.copyString(Method->getSelector().getAsString());
6749b36e126SZixu Wang       StringRef USR = API.recordUSR(Method);
6759b36e126SZixu Wang       PresumedLoc Loc =
6769b36e126SZixu Wang           Context.getSourceManager().getPresumedLoc(Method->getLocation());
6779b36e126SZixu Wang       AvailabilityInfo Availability = getAvailability(Method);
6789b36e126SZixu Wang       DocComment Comment;
6799b36e126SZixu Wang       if (auto *RawComment = Context.getRawCommentForDeclNoCache(Method))
6809b36e126SZixu Wang         Comment = RawComment->getFormattedLines(Context.getSourceManager(),
6819b36e126SZixu Wang                                                 Context.getDiagnostics());
6829b36e126SZixu Wang 
6839b36e126SZixu Wang       // Build declaration fragments, sub-heading, and signature for the method.
6849b36e126SZixu Wang       DeclarationFragments Declaration =
6859b36e126SZixu Wang           DeclarationFragmentsBuilder::getFragmentsForObjCMethod(Method);
6869b36e126SZixu Wang       DeclarationFragments SubHeading =
6879b36e126SZixu Wang           DeclarationFragmentsBuilder::getSubHeading(Method);
6889b36e126SZixu Wang       FunctionSignature Signature =
6899b36e126SZixu Wang           DeclarationFragmentsBuilder::getFunctionSignature(Method);
6909b36e126SZixu Wang 
6919b36e126SZixu Wang       API.addObjCMethod(Container, Name, USR, Loc, Availability, Comment,
6929b36e126SZixu Wang                         Declaration, SubHeading, Signature,
6939b36e126SZixu Wang                         Method->isInstanceMethod());
6949b36e126SZixu Wang     }
6959b36e126SZixu Wang   }
6969b36e126SZixu Wang 
6979b36e126SZixu Wang   void recordObjCProperties(ObjCContainerRecord *Container,
6989b36e126SZixu Wang                             const ObjCContainerDecl::prop_range Properties) {
6999b36e126SZixu Wang     for (const auto *Property : Properties) {
7009b36e126SZixu Wang       StringRef Name = Property->getName();
7019b36e126SZixu Wang       StringRef USR = API.recordUSR(Property);
7029b36e126SZixu Wang       PresumedLoc Loc =
7039b36e126SZixu Wang           Context.getSourceManager().getPresumedLoc(Property->getLocation());
7049b36e126SZixu Wang       AvailabilityInfo Availability = getAvailability(Property);
7059b36e126SZixu Wang       DocComment Comment;
7069b36e126SZixu Wang       if (auto *RawComment = Context.getRawCommentForDeclNoCache(Property))
7079b36e126SZixu Wang         Comment = RawComment->getFormattedLines(Context.getSourceManager(),
7089b36e126SZixu Wang                                                 Context.getDiagnostics());
7099b36e126SZixu Wang 
7109b36e126SZixu Wang       // Build declaration fragments and sub-heading for the property.
7119b36e126SZixu Wang       DeclarationFragments Declaration =
7129b36e126SZixu Wang           DeclarationFragmentsBuilder::getFragmentsForObjCProperty(Property);
7139b36e126SZixu Wang       DeclarationFragments SubHeading =
7149b36e126SZixu Wang           DeclarationFragmentsBuilder::getSubHeading(Property);
7159b36e126SZixu Wang 
7169b36e126SZixu Wang       StringRef GetterName =
7179b36e126SZixu Wang           API.copyString(Property->getGetterName().getAsString());
7189b36e126SZixu Wang       StringRef SetterName =
7199b36e126SZixu Wang           API.copyString(Property->getSetterName().getAsString());
7209b36e126SZixu Wang 
7219b36e126SZixu Wang       // Get the attributes for property.
7229b36e126SZixu Wang       unsigned Attributes = ObjCPropertyRecord::NoAttr;
7239b36e126SZixu Wang       if (Property->getPropertyAttributes() &
7249b36e126SZixu Wang           ObjCPropertyAttribute::kind_readonly)
7259b36e126SZixu Wang         Attributes |= ObjCPropertyRecord::ReadOnly;
7269b36e126SZixu Wang       if (Property->getPropertyAttributes() & ObjCPropertyAttribute::kind_class)
7279b36e126SZixu Wang         Attributes |= ObjCPropertyRecord::Class;
7289b36e126SZixu Wang 
7299b36e126SZixu Wang       API.addObjCProperty(
7309b36e126SZixu Wang           Container, Name, USR, Loc, Availability, Comment, Declaration,
7319b36e126SZixu Wang           SubHeading,
7329b36e126SZixu Wang           static_cast<ObjCPropertyRecord::AttributeKind>(Attributes),
7339b36e126SZixu Wang           GetterName, SetterName, Property->isOptional());
7349b36e126SZixu Wang     }
7359b36e126SZixu Wang   }
7369b36e126SZixu Wang 
7379b36e126SZixu Wang   void recordObjCInstanceVariables(
7389b36e126SZixu Wang       ObjCContainerRecord *Container,
7399b36e126SZixu Wang       const llvm::iterator_range<
7409b36e126SZixu Wang           DeclContext::specific_decl_iterator<ObjCIvarDecl>>
7419b36e126SZixu Wang           Ivars) {
7429b36e126SZixu Wang     for (const auto *Ivar : Ivars) {
7439b36e126SZixu Wang       StringRef Name = Ivar->getName();
7449b36e126SZixu Wang       StringRef USR = API.recordUSR(Ivar);
7459b36e126SZixu Wang       PresumedLoc Loc =
7469b36e126SZixu Wang           Context.getSourceManager().getPresumedLoc(Ivar->getLocation());
7479b36e126SZixu Wang       AvailabilityInfo Availability = getAvailability(Ivar);
7489b36e126SZixu Wang       DocComment Comment;
7499b36e126SZixu Wang       if (auto *RawComment = Context.getRawCommentForDeclNoCache(Ivar))
7509b36e126SZixu Wang         Comment = RawComment->getFormattedLines(Context.getSourceManager(),
7519b36e126SZixu Wang                                                 Context.getDiagnostics());
7529b36e126SZixu Wang 
7539b36e126SZixu Wang       // Build declaration fragments and sub-heading for the instance variable.
7549b36e126SZixu Wang       DeclarationFragments Declaration =
7559b36e126SZixu Wang           DeclarationFragmentsBuilder::getFragmentsForField(Ivar);
7569b36e126SZixu Wang       DeclarationFragments SubHeading =
7579b36e126SZixu Wang           DeclarationFragmentsBuilder::getSubHeading(Ivar);
7589b36e126SZixu Wang 
7599b36e126SZixu Wang       ObjCInstanceVariableRecord::AccessControl Access =
7609b36e126SZixu Wang           Ivar->getCanonicalAccessControl();
7619b36e126SZixu Wang 
7629b36e126SZixu Wang       API.addObjCInstanceVariable(Container, Name, USR, Loc, Availability,
7639b36e126SZixu Wang                                   Comment, Declaration, SubHeading, Access);
7649b36e126SZixu Wang     }
7659b36e126SZixu Wang   }
7669b36e126SZixu Wang 
7679b36e126SZixu Wang   void recordObjCProtocols(ObjCContainerRecord *Container,
7689b36e126SZixu Wang                            ObjCInterfaceDecl::protocol_range Protocols) {
7699b36e126SZixu Wang     for (const auto *Protocol : Protocols)
7709b36e126SZixu Wang       Container->Protocols.emplace_back(Protocol->getName(),
7719b36e126SZixu Wang                                         API.recordUSR(Protocol));
7729b36e126SZixu Wang   }
7739b36e126SZixu Wang 
77489f6b26fSZixu Wang   ASTContext &Context;
775a9909d23SDaniel Grumberg   APISet &API;
776aebe5fc6SDaniel Grumberg   LocationFileChecker &LCF;
77789f6b26fSZixu Wang };
77889f6b26fSZixu Wang 
77989f6b26fSZixu Wang class ExtractAPIConsumer : public ASTConsumer {
78089f6b26fSZixu Wang public:
781aebe5fc6SDaniel Grumberg   ExtractAPIConsumer(ASTContext &Context,
782aebe5fc6SDaniel Grumberg                      std::unique_ptr<LocationFileChecker> LCF, APISet &API)
783aebe5fc6SDaniel Grumberg       : Visitor(Context, *LCF, API), LCF(std::move(LCF)) {}
78489f6b26fSZixu Wang 
78589f6b26fSZixu Wang   void HandleTranslationUnit(ASTContext &Context) override {
78689f6b26fSZixu Wang     // Use ExtractAPIVisitor to traverse symbol declarations in the context.
78789f6b26fSZixu Wang     Visitor.TraverseDecl(Context.getTranslationUnitDecl());
78889f6b26fSZixu Wang   }
78989f6b26fSZixu Wang 
79089f6b26fSZixu Wang private:
79189f6b26fSZixu Wang   ExtractAPIVisitor Visitor;
792aebe5fc6SDaniel Grumberg   std::unique_ptr<LocationFileChecker> LCF;
79389f6b26fSZixu Wang };
79489f6b26fSZixu Wang 
795529a0570SDaniel Grumberg class MacroCallback : public PPCallbacks {
796529a0570SDaniel Grumberg public:
79710155922SDaniel Grumberg   MacroCallback(const SourceManager &SM, LocationFileChecker &LCF, APISet &API,
79810155922SDaniel Grumberg                 Preprocessor &PP)
79910155922SDaniel Grumberg       : SM(SM), LCF(LCF), API(API), PP(PP) {}
800529a0570SDaniel Grumberg 
801529a0570SDaniel Grumberg   void MacroDefined(const Token &MacroNameToken,
802529a0570SDaniel Grumberg                     const MacroDirective *MD) override {
803529a0570SDaniel Grumberg     auto *MacroInfo = MD->getMacroInfo();
804529a0570SDaniel Grumberg 
805529a0570SDaniel Grumberg     if (MacroInfo->isBuiltinMacro())
806529a0570SDaniel Grumberg       return;
807529a0570SDaniel Grumberg 
808529a0570SDaniel Grumberg     auto SourceLoc = MacroNameToken.getLocation();
809529a0570SDaniel Grumberg     if (SM.isWrittenInBuiltinFile(SourceLoc) ||
810529a0570SDaniel Grumberg         SM.isWrittenInCommandLineFile(SourceLoc))
811529a0570SDaniel Grumberg       return;
812529a0570SDaniel Grumberg 
813529a0570SDaniel Grumberg     PendingMacros.emplace_back(MacroNameToken, MD);
814529a0570SDaniel Grumberg   }
815529a0570SDaniel Grumberg 
816529a0570SDaniel Grumberg   // If a macro gets undefined at some point during preprocessing of the inputs
817529a0570SDaniel Grumberg   // it means that it isn't an exposed API and we should therefore not add a
818529a0570SDaniel Grumberg   // macro definition for it.
819529a0570SDaniel Grumberg   void MacroUndefined(const Token &MacroNameToken, const MacroDefinition &MD,
820529a0570SDaniel Grumberg                       const MacroDirective *Undef) override {
8218b63622bSDaniel Grumberg     // If this macro wasn't previously defined we don't need to do anything
8228b63622bSDaniel Grumberg     // here.
8238b63622bSDaniel Grumberg     if (!Undef)
8248b63622bSDaniel Grumberg       return;
8258b63622bSDaniel Grumberg 
82610155922SDaniel Grumberg     llvm::erase_if(PendingMacros, [&MD, this](const PendingMacro &PM) {
82710155922SDaniel Grumberg       return MD.getMacroInfo()->isIdenticalTo(*PM.MD->getMacroInfo(), PP,
82810155922SDaniel Grumberg                                               /*Syntactically*/ false);
829529a0570SDaniel Grumberg     });
830529a0570SDaniel Grumberg   }
831529a0570SDaniel Grumberg 
832529a0570SDaniel Grumberg   void EndOfMainFile() override {
833529a0570SDaniel Grumberg     for (auto &PM : PendingMacros) {
834529a0570SDaniel Grumberg       // `isUsedForHeaderGuard` is only set when the preprocessor leaves the
835529a0570SDaniel Grumberg       // file so check for it here.
836529a0570SDaniel Grumberg       if (PM.MD->getMacroInfo()->isUsedForHeaderGuard())
837529a0570SDaniel Grumberg         continue;
838529a0570SDaniel Grumberg 
839aebe5fc6SDaniel Grumberg       if (!LCF.isLocationInKnownFile(PM.MacroNameToken.getLocation()))
840aebe5fc6SDaniel Grumberg         continue;
841aebe5fc6SDaniel Grumberg 
842529a0570SDaniel Grumberg       StringRef Name = PM.MacroNameToken.getIdentifierInfo()->getName();
843529a0570SDaniel Grumberg       PresumedLoc Loc = SM.getPresumedLoc(PM.MacroNameToken.getLocation());
844529a0570SDaniel Grumberg       StringRef USR =
845529a0570SDaniel Grumberg           API.recordUSRForMacro(Name, PM.MacroNameToken.getLocation(), SM);
846529a0570SDaniel Grumberg 
847529a0570SDaniel Grumberg       API.addMacroDefinition(
848529a0570SDaniel Grumberg           Name, USR, Loc,
849529a0570SDaniel Grumberg           DeclarationFragmentsBuilder::getFragmentsForMacro(Name, PM.MD),
850529a0570SDaniel Grumberg           DeclarationFragmentsBuilder::getSubHeadingForMacro(Name));
851529a0570SDaniel Grumberg     }
852529a0570SDaniel Grumberg 
853529a0570SDaniel Grumberg     PendingMacros.clear();
854529a0570SDaniel Grumberg   }
855529a0570SDaniel Grumberg 
856529a0570SDaniel Grumberg private:
857529a0570SDaniel Grumberg   struct PendingMacro {
858529a0570SDaniel Grumberg     Token MacroNameToken;
859529a0570SDaniel Grumberg     const MacroDirective *MD;
860529a0570SDaniel Grumberg 
861529a0570SDaniel Grumberg     PendingMacro(const Token &MacroNameToken, const MacroDirective *MD)
862529a0570SDaniel Grumberg         : MacroNameToken(MacroNameToken), MD(MD) {}
863529a0570SDaniel Grumberg   };
864529a0570SDaniel Grumberg 
865529a0570SDaniel Grumberg   const SourceManager &SM;
866aebe5fc6SDaniel Grumberg   LocationFileChecker &LCF;
867529a0570SDaniel Grumberg   APISet &API;
86810155922SDaniel Grumberg   Preprocessor &PP;
869529a0570SDaniel Grumberg   llvm::SmallVector<PendingMacro> PendingMacros;
870529a0570SDaniel Grumberg };
871529a0570SDaniel Grumberg 
87289f6b26fSZixu Wang } // namespace
87389f6b26fSZixu Wang 
87489f6b26fSZixu Wang std::unique_ptr<ASTConsumer>
87589f6b26fSZixu Wang ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
876a9909d23SDaniel Grumberg   OS = CreateOutputFile(CI, InFile);
87789f6b26fSZixu Wang   if (!OS)
87889f6b26fSZixu Wang     return nullptr;
879a9909d23SDaniel Grumberg 
880a9909d23SDaniel Grumberg   ProductName = CI.getFrontendOpts().ProductName;
881a9909d23SDaniel Grumberg 
882a9909d23SDaniel Grumberg   // Now that we have enough information about the language options and the
883a9909d23SDaniel Grumberg   // target triple, let's create the APISet before anyone uses it.
884a9909d23SDaniel Grumberg   API = std::make_unique<APISet>(
885a9909d23SDaniel Grumberg       CI.getTarget().getTriple(),
886a9909d23SDaniel Grumberg       CI.getFrontendOpts().Inputs.back().getKind().getLanguage());
887a9909d23SDaniel Grumberg 
888*cb5bb285SZixu Wang   auto LCF = std::make_unique<LocationFileChecker>(CI, KnownInputFiles);
889aebe5fc6SDaniel Grumberg 
89010155922SDaniel Grumberg   CI.getPreprocessor().addPPCallbacks(std::make_unique<MacroCallback>(
89110155922SDaniel Grumberg       CI.getSourceManager(), *LCF, *API, CI.getPreprocessor()));
892529a0570SDaniel Grumberg 
893aebe5fc6SDaniel Grumberg   return std::make_unique<ExtractAPIConsumer>(CI.getASTContext(),
894aebe5fc6SDaniel Grumberg                                               std::move(LCF), *API);
89589f6b26fSZixu Wang }
89689f6b26fSZixu Wang 
897f833aab0SDaniel Grumberg bool ExtractAPIAction::PrepareToExecuteAction(CompilerInstance &CI) {
898f833aab0SDaniel Grumberg   auto &Inputs = CI.getFrontendOpts().Inputs;
899f833aab0SDaniel Grumberg   if (Inputs.empty())
900f833aab0SDaniel Grumberg     return true;
901f833aab0SDaniel Grumberg 
902*cb5bb285SZixu Wang   if (!CI.hasFileManager())
903*cb5bb285SZixu Wang     if (!CI.createFileManager())
904*cb5bb285SZixu Wang       return false;
905*cb5bb285SZixu Wang 
906f833aab0SDaniel Grumberg   auto Kind = Inputs[0].getKind();
907f833aab0SDaniel Grumberg 
908f833aab0SDaniel Grumberg   // Convert the header file inputs into a single input buffer.
909f833aab0SDaniel Grumberg   SmallString<256> HeaderContents;
910*cb5bb285SZixu Wang   bool IsQuoted = false;
911f833aab0SDaniel Grumberg   for (const FrontendInputFile &FIF : Inputs) {
912f833aab0SDaniel Grumberg     if (Kind.isObjectiveC())
913f833aab0SDaniel Grumberg       HeaderContents += "#import";
914f833aab0SDaniel Grumberg     else
915f833aab0SDaniel Grumberg       HeaderContents += "#include";
9164c262feeSZixu Wang 
917*cb5bb285SZixu Wang     StringRef FilePath = FIF.getFile();
918*cb5bb285SZixu Wang     if (auto RelativeName = getRelativeIncludeName(CI, FilePath, &IsQuoted)) {
919*cb5bb285SZixu Wang       if (IsQuoted)
920*cb5bb285SZixu Wang         HeaderContents += " \"";
921*cb5bb285SZixu Wang       else
922*cb5bb285SZixu Wang         HeaderContents += " <";
923*cb5bb285SZixu Wang 
924*cb5bb285SZixu Wang       HeaderContents += *RelativeName;
925*cb5bb285SZixu Wang 
926*cb5bb285SZixu Wang       if (IsQuoted)
927*cb5bb285SZixu Wang         HeaderContents += "\"\n";
928*cb5bb285SZixu Wang       else
929*cb5bb285SZixu Wang         HeaderContents += ">\n";
930*cb5bb285SZixu Wang       KnownInputFiles.emplace_back(*RelativeName, IsQuoted);
931*cb5bb285SZixu Wang     } else {
932*cb5bb285SZixu Wang       HeaderContents += " \"";
933*cb5bb285SZixu Wang       HeaderContents += FilePath;
934*cb5bb285SZixu Wang       HeaderContents += "\"\n";
935*cb5bb285SZixu Wang       KnownInputFiles.emplace_back(FilePath, true);
9362966f0faSZixu Wang     }
937*cb5bb285SZixu Wang   }
938*cb5bb285SZixu Wang 
939*cb5bb285SZixu Wang   if (CI.getHeaderSearchOpts().Verbose)
940*cb5bb285SZixu Wang     CI.getVerboseOutputStream() << getInputBufferName() << ":\n"
941*cb5bb285SZixu Wang                                 << HeaderContents << "\n";
942f833aab0SDaniel Grumberg 
943985eaa1aSDaniel Grumberg   Buffer = llvm::MemoryBuffer::getMemBufferCopy(HeaderContents,
944f833aab0SDaniel Grumberg                                                 getInputBufferName());
945f833aab0SDaniel Grumberg 
946f833aab0SDaniel Grumberg   // Set that buffer up as our "real" input in the CompilerInstance.
947f833aab0SDaniel Grumberg   Inputs.clear();
948f833aab0SDaniel Grumberg   Inputs.emplace_back(Buffer->getMemBufferRef(), Kind, /*IsSystem*/ false);
949f833aab0SDaniel Grumberg 
950f833aab0SDaniel Grumberg   return true;
951f833aab0SDaniel Grumberg }
952f833aab0SDaniel Grumberg 
953a9909d23SDaniel Grumberg void ExtractAPIAction::EndSourceFileAction() {
954a9909d23SDaniel Grumberg   if (!OS)
955a9909d23SDaniel Grumberg     return;
956a9909d23SDaniel Grumberg 
957a9909d23SDaniel Grumberg   // Setup a SymbolGraphSerializer to write out collected API information in
958a9909d23SDaniel Grumberg   // the Symbol Graph format.
959a9909d23SDaniel Grumberg   // FIXME: Make the kind of APISerializer configurable.
960a9909d23SDaniel Grumberg   SymbolGraphSerializer SGSerializer(*API, ProductName);
961a9909d23SDaniel Grumberg   SGSerializer.serialize(*OS);
9622d133867SDuncan P. N. Exon Smith   OS.reset();
963a9909d23SDaniel Grumberg }
964a9909d23SDaniel Grumberg 
96589f6b26fSZixu Wang std::unique_ptr<raw_pwrite_stream>
96689f6b26fSZixu Wang ExtractAPIAction::CreateOutputFile(CompilerInstance &CI, StringRef InFile) {
96789f6b26fSZixu Wang   std::unique_ptr<raw_pwrite_stream> OS =
96889f6b26fSZixu Wang       CI.createDefaultOutputFile(/*Binary=*/false, InFile, /*Extension=*/"json",
96989f6b26fSZixu Wang                                  /*RemoveFileOnSignal=*/false);
97089f6b26fSZixu Wang   if (!OS)
97189f6b26fSZixu Wang     return nullptr;
97289f6b26fSZixu Wang   return OS;
97389f6b26fSZixu Wang }
974