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