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 107a851921SDaniel Grumberg /// This file implements the ExtractAPIAction, and ASTConsumer to collect API 117a851921SDaniel Grumberg /// information. 1289f6b26fSZixu Wang /// 1389f6b26fSZixu Wang //===----------------------------------------------------------------------===// 1489f6b26fSZixu Wang 15142c3d9dSDaniel Grumberg #include "clang/AST/ASTConcept.h" 1689f6b26fSZixu Wang #include "clang/AST/ASTConsumer.h" 1789f6b26fSZixu Wang #include "clang/AST/ASTContext.h" 18142c3d9dSDaniel Grumberg #include "clang/AST/DeclObjC.h" 19791fe26dSDaniel Grumberg #include "clang/Basic/DiagnosticFrontend.h" 2014e99174SDaniel Grumberg #include "clang/Basic/FileEntry.h" 21aebe5fc6SDaniel Grumberg #include "clang/Basic/SourceLocation.h" 22aebe5fc6SDaniel Grumberg #include "clang/Basic/SourceManager.h" 2389f6b26fSZixu Wang #include "clang/Basic/TargetInfo.h" 2489f6b26fSZixu Wang #include "clang/ExtractAPI/API.h" 25791fe26dSDaniel Grumberg #include "clang/ExtractAPI/APIIgnoresList.h" 267a851921SDaniel Grumberg #include "clang/ExtractAPI/ExtractAPIVisitor.h" 2789f6b26fSZixu Wang #include "clang/ExtractAPI/FrontendActions.h" 2889f6b26fSZixu Wang #include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h" 2989f6b26fSZixu Wang #include "clang/Frontend/ASTConsumers.h" 3089f6b26fSZixu Wang #include "clang/Frontend/CompilerInstance.h" 31f833aab0SDaniel Grumberg #include "clang/Frontend/FrontendOptions.h" 328e9145e4SAnkur #include "clang/Frontend/MultiplexConsumer.h" 33e05c1b46SDaniel Grumberg #include "clang/Index/USRGeneration.h" 344c6043deSCyndy Ishida #include "clang/InstallAPI/HeaderFile.h" 35529a0570SDaniel Grumberg #include "clang/Lex/MacroInfo.h" 36529a0570SDaniel Grumberg #include "clang/Lex/PPCallbacks.h" 37aebe5fc6SDaniel Grumberg #include "clang/Lex/Preprocessor.h" 38529a0570SDaniel Grumberg #include "clang/Lex/PreprocessorOptions.h" 39aebe5fc6SDaniel Grumberg #include "llvm/ADT/DenseSet.h" 40529a0570SDaniel Grumberg #include "llvm/ADT/STLExtras.h" 418e9145e4SAnkur #include "llvm/ADT/SmallString.h" 42f833aab0SDaniel Grumberg #include "llvm/ADT/SmallVector.h" 43e05c1b46SDaniel Grumberg #include "llvm/ADT/StringRef.h" 44142c3d9dSDaniel Grumberg #include "llvm/Support/Casting.h" 45791fe26dSDaniel Grumberg #include "llvm/Support/Error.h" 46cb5bb285SZixu Wang #include "llvm/Support/FileSystem.h" 47f833aab0SDaniel Grumberg #include "llvm/Support/MemoryBuffer.h" 48cb5bb285SZixu Wang #include "llvm/Support/Path.h" 49cb5bb285SZixu Wang #include "llvm/Support/Regex.h" 5089f6b26fSZixu Wang #include "llvm/Support/raw_ostream.h" 51aebe5fc6SDaniel Grumberg #include <memory> 52f655cb0fSKazu Hirata #include <optional> 53aebe5fc6SDaniel Grumberg #include <utility> 5489f6b26fSZixu Wang 5589f6b26fSZixu Wang using namespace clang; 5689f6b26fSZixu Wang using namespace extractapi; 5789f6b26fSZixu Wang 5889f6b26fSZixu Wang namespace { 5989f6b26fSZixu Wang 606ad0788cSKazu Hirata std::optional<std::string> getRelativeIncludeName(const CompilerInstance &CI, 61cb5bb285SZixu Wang StringRef File, 62cb5bb285SZixu Wang bool *IsQuoted = nullptr) { 63cb5bb285SZixu Wang assert(CI.hasFileManager() && 64cb5bb285SZixu Wang "CompilerInstance does not have a FileNamager!"); 65cb5bb285SZixu Wang 66cb5bb285SZixu Wang using namespace llvm::sys; 67cb5bb285SZixu Wang const auto &FS = CI.getVirtualFileSystem(); 68cb5bb285SZixu Wang 69cb5bb285SZixu Wang SmallString<128> FilePath(File.begin(), File.end()); 70cb5bb285SZixu Wang FS.makeAbsolute(FilePath); 71cb5bb285SZixu Wang path::remove_dots(FilePath, true); 72cb5bb285SZixu Wang FilePath = path::convert_to_slash(FilePath); 73cb5bb285SZixu Wang File = FilePath; 74cb5bb285SZixu Wang 75cb5bb285SZixu Wang // Checks whether `Dir` is a strict path prefix of `File`. If so returns 76cb5bb285SZixu Wang // the prefix length. Otherwise return 0. 77cb5bb285SZixu Wang auto CheckDir = [&](llvm::StringRef Dir) -> unsigned { 78cb5bb285SZixu Wang llvm::SmallString<32> DirPath(Dir.begin(), Dir.end()); 79cb5bb285SZixu Wang FS.makeAbsolute(DirPath); 80cb5bb285SZixu Wang path::remove_dots(DirPath, true); 81cb5bb285SZixu Wang Dir = DirPath; 82cb5bb285SZixu Wang for (auto NI = path::begin(File), NE = path::end(File), 83cb5bb285SZixu Wang DI = path::begin(Dir), DE = path::end(Dir); 84cb5bb285SZixu Wang /*termination condition in loop*/; ++NI, ++DI) { 85cb5bb285SZixu Wang // '.' components in File are ignored. 86cb5bb285SZixu Wang while (NI != NE && *NI == ".") 87cb5bb285SZixu Wang ++NI; 88cb5bb285SZixu Wang if (NI == NE) 89cb5bb285SZixu Wang break; 90cb5bb285SZixu Wang 91cb5bb285SZixu Wang // '.' components in Dir are ignored. 92cb5bb285SZixu Wang while (DI != DE && *DI == ".") 93cb5bb285SZixu Wang ++DI; 94cb5bb285SZixu Wang 95cb5bb285SZixu Wang // Dir is a prefix of File, up to '.' components and choice of path 96cb5bb285SZixu Wang // separators. 97cb5bb285SZixu Wang if (DI == DE) 98cb5bb285SZixu Wang return NI - path::begin(File); 99cb5bb285SZixu Wang 100cb5bb285SZixu Wang // Consider all path separators equal. 101cb5bb285SZixu Wang if (NI->size() == 1 && DI->size() == 1 && 102cb5bb285SZixu Wang path::is_separator(NI->front()) && path::is_separator(DI->front())) 103cb5bb285SZixu Wang continue; 104cb5bb285SZixu Wang 105cb5bb285SZixu Wang // Special case Apple .sdk folders since the search path is typically a 106cb5bb285SZixu Wang // symlink like `iPhoneSimulator14.5.sdk` while the file is instead 107cb5bb285SZixu Wang // located in `iPhoneSimulator.sdk` (the real folder). 108f3dcc235SKazu Hirata if (NI->ends_with(".sdk") && DI->ends_with(".sdk")) { 109cb5bb285SZixu Wang StringRef NBasename = path::stem(*NI); 110cb5bb285SZixu Wang StringRef DBasename = path::stem(*DI); 111f3dcc235SKazu Hirata if (DBasename.starts_with(NBasename)) 112cb5bb285SZixu Wang continue; 113cb5bb285SZixu Wang } 114cb5bb285SZixu Wang 115cb5bb285SZixu Wang if (*NI != *DI) 116cb5bb285SZixu Wang break; 117cb5bb285SZixu Wang } 118cb5bb285SZixu Wang return 0; 119cb5bb285SZixu Wang }; 120cb5bb285SZixu Wang 121cb5bb285SZixu Wang unsigned PrefixLength = 0; 122cb5bb285SZixu Wang 123cb5bb285SZixu Wang // Go through the search paths and find the first one that is a prefix of 124cb5bb285SZixu Wang // the header. 125cb5bb285SZixu Wang for (const auto &Entry : CI.getHeaderSearchOpts().UserEntries) { 126cb5bb285SZixu Wang // Note whether the match is found in a quoted entry. 127cb5bb285SZixu Wang if (IsQuoted) 128cb5bb285SZixu Wang *IsQuoted = Entry.Group == frontend::Quoted; 129cb5bb285SZixu Wang 130cb5bb285SZixu Wang if (auto EntryFile = CI.getFileManager().getOptionalFileRef(Entry.Path)) { 131cb5bb285SZixu Wang if (auto HMap = HeaderMap::Create(*EntryFile, CI.getFileManager())) { 132cb5bb285SZixu Wang // If this is a headermap entry, try to reverse lookup the full path 133cb5bb285SZixu Wang // for a spelled name before mapping. 134cb5bb285SZixu Wang StringRef SpelledFilename = HMap->reverseLookupFilename(File); 135cb5bb285SZixu Wang if (!SpelledFilename.empty()) 136cb5bb285SZixu Wang return SpelledFilename.str(); 137cb5bb285SZixu Wang 138cb5bb285SZixu Wang // No matching mapping in this headermap, try next search entry. 139cb5bb285SZixu Wang continue; 140cb5bb285SZixu Wang } 141cb5bb285SZixu Wang } 142cb5bb285SZixu Wang 143cb5bb285SZixu Wang // Entry is a directory search entry, try to check if it's a prefix of File. 144cb5bb285SZixu Wang PrefixLength = CheckDir(Entry.Path); 145cb5bb285SZixu Wang if (PrefixLength > 0) { 146cb5bb285SZixu Wang // The header is found in a framework path, construct the framework-style 147cb5bb285SZixu Wang // include name `<Framework/Header.h>` 148cb5bb285SZixu Wang if (Entry.IsFramework) { 149cb5bb285SZixu Wang SmallVector<StringRef, 4> Matches; 1504c6043deSCyndy Ishida clang::installapi::HeaderFile::getFrameworkIncludeRule().match( 1514c6043deSCyndy Ishida File, &Matches); 152cb5bb285SZixu Wang // Returned matches are always in stable order. 153cb5bb285SZixu Wang if (Matches.size() != 4) 1545891420eSKazu Hirata return std::nullopt; 155cb5bb285SZixu Wang 156cb5bb285SZixu Wang return path::convert_to_slash( 157cb5bb285SZixu Wang (Matches[1].drop_front(Matches[1].rfind('/') + 1) + "/" + 158cb5bb285SZixu Wang Matches[3]) 159cb5bb285SZixu Wang .str()); 160cb5bb285SZixu Wang } 161cb5bb285SZixu Wang 162cb5bb285SZixu Wang // The header is found in a normal search path, strip the search path 163cb5bb285SZixu Wang // prefix to get an include name. 164cb5bb285SZixu Wang return path::convert_to_slash(File.drop_front(PrefixLength)); 165cb5bb285SZixu Wang } 166cb5bb285SZixu Wang } 167cb5bb285SZixu Wang 168cb5bb285SZixu Wang // Couldn't determine a include name, use full path instead. 1695891420eSKazu Hirata return std::nullopt; 170cb5bb285SZixu Wang } 171cb5bb285SZixu Wang 17214e99174SDaniel Grumberg std::optional<std::string> getRelativeIncludeName(const CompilerInstance &CI, 17314e99174SDaniel Grumberg FileEntryRef FE, 17414e99174SDaniel Grumberg bool *IsQuoted = nullptr) { 17514e99174SDaniel Grumberg return getRelativeIncludeName(CI, FE.getNameAsRequested(), IsQuoted); 17614e99174SDaniel Grumberg } 17714e99174SDaniel Grumberg 178aebe5fc6SDaniel Grumberg struct LocationFileChecker { 1797a851921SDaniel Grumberg bool operator()(SourceLocation Loc) { 180d0dd151eSDaniel Grumberg // If the loc refers to a macro expansion we need to first get the file 181aebe5fc6SDaniel Grumberg // location of the expansion. 182cb5bb285SZixu 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 1885a3130e3SJan Svoboda OptionalFileEntryRef File = SM.getFileEntryRefForID(FID); 189aebe5fc6SDaniel Grumberg if (!File) 190aebe5fc6SDaniel Grumberg return false; 191aebe5fc6SDaniel Grumberg 1925a3130e3SJan Svoboda if (KnownFileEntries.count(*File)) 193aebe5fc6SDaniel Grumberg return true; 194aebe5fc6SDaniel Grumberg 1955a3130e3SJan Svoboda if (ExternalFileEntries.count(*File)) 196cb5bb285SZixu Wang return false; 197cb5bb285SZixu Wang 198cb5bb285SZixu Wang // Try to reduce the include name the same way we tried to include it. 199cb5bb285SZixu Wang bool IsQuoted = false; 20014e99174SDaniel Grumberg if (auto IncludeName = getRelativeIncludeName(CI, *File, &IsQuoted)) 201f13019f8SKazu Hirata if (llvm::any_of(KnownFiles, 202cb5bb285SZixu Wang [&IsQuoted, &IncludeName](const auto &KnownFile) { 203cb5bb285SZixu Wang return KnownFile.first.equals(*IncludeName) && 204cb5bb285SZixu Wang KnownFile.second == IsQuoted; 205f13019f8SKazu Hirata })) { 2065a3130e3SJan Svoboda KnownFileEntries.insert(*File); 207cb5bb285SZixu Wang return true; 208cb5bb285SZixu Wang } 209cb5bb285SZixu Wang 210cb5bb285SZixu Wang // Record that the file was not found to avoid future reverse lookup for 211cb5bb285SZixu Wang // the same file. 2125a3130e3SJan Svoboda ExternalFileEntries.insert(*File); 213aebe5fc6SDaniel Grumberg return false; 214aebe5fc6SDaniel Grumberg } 215aebe5fc6SDaniel Grumberg 216cb5bb285SZixu Wang LocationFileChecker(const CompilerInstance &CI, 217cb5bb285SZixu Wang SmallVector<std::pair<SmallString<32>, bool>> &KnownFiles) 218cb5bb285SZixu Wang : CI(CI), KnownFiles(KnownFiles), ExternalFileEntries() { 219cb5bb285SZixu Wang for (const auto &KnownFile : KnownFiles) 220*b1aea98cSJan Svoboda if (auto FE = CI.getFileManager().getOptionalFileRef(KnownFile.first)) 221*b1aea98cSJan Svoboda KnownFileEntries.insert(*FE); 222aebe5fc6SDaniel Grumberg } 223aebe5fc6SDaniel Grumberg 224aebe5fc6SDaniel Grumberg private: 225cb5bb285SZixu Wang const CompilerInstance &CI; 226cb5bb285SZixu Wang SmallVector<std::pair<SmallString<32>, bool>> &KnownFiles; 227aebe5fc6SDaniel Grumberg llvm::DenseSet<const FileEntry *> KnownFileEntries; 228cb5bb285SZixu Wang llvm::DenseSet<const FileEntry *> ExternalFileEntries; 229aebe5fc6SDaniel Grumberg }; 230aebe5fc6SDaniel Grumberg 231142c3d9dSDaniel Grumberg struct BatchExtractAPIVisitor : ExtractAPIVisitor<BatchExtractAPIVisitor> { 232142c3d9dSDaniel Grumberg bool shouldDeclBeIncluded(const Decl *D) const { 233142c3d9dSDaniel Grumberg bool ShouldBeIncluded = true; 234142c3d9dSDaniel Grumberg // Check that we have the definition for redeclarable types. 235142c3d9dSDaniel Grumberg if (auto *TD = llvm::dyn_cast<TagDecl>(D)) 236142c3d9dSDaniel Grumberg ShouldBeIncluded = TD->isThisDeclarationADefinition(); 237142c3d9dSDaniel Grumberg else if (auto *Interface = llvm::dyn_cast<ObjCInterfaceDecl>(D)) 238142c3d9dSDaniel Grumberg ShouldBeIncluded = Interface->isThisDeclarationADefinition(); 239142c3d9dSDaniel Grumberg else if (auto *Protocol = llvm::dyn_cast<ObjCProtocolDecl>(D)) 240142c3d9dSDaniel Grumberg ShouldBeIncluded = Protocol->isThisDeclarationADefinition(); 241142c3d9dSDaniel Grumberg 242142c3d9dSDaniel Grumberg ShouldBeIncluded = ShouldBeIncluded && LCF(D->getLocation()); 243142c3d9dSDaniel Grumberg return ShouldBeIncluded; 244142c3d9dSDaniel Grumberg } 245142c3d9dSDaniel Grumberg 246142c3d9dSDaniel Grumberg BatchExtractAPIVisitor(LocationFileChecker &LCF, ASTContext &Context, 247142c3d9dSDaniel Grumberg APISet &API) 248142c3d9dSDaniel Grumberg : ExtractAPIVisitor<BatchExtractAPIVisitor>(Context, API), LCF(LCF) {} 249142c3d9dSDaniel Grumberg 250142c3d9dSDaniel Grumberg private: 251142c3d9dSDaniel Grumberg LocationFileChecker &LCF; 252142c3d9dSDaniel Grumberg }; 253142c3d9dSDaniel Grumberg 2548e9145e4SAnkur class WrappingExtractAPIConsumer : public ASTConsumer { 2558e9145e4SAnkur public: 2568e9145e4SAnkur WrappingExtractAPIConsumer(ASTContext &Context, APISet &API) 2578e9145e4SAnkur : Visitor(Context, API) {} 2588e9145e4SAnkur 2598e9145e4SAnkur void HandleTranslationUnit(ASTContext &Context) override { 2608e9145e4SAnkur // Use ExtractAPIVisitor to traverse symbol declarations in the context. 2618e9145e4SAnkur Visitor.TraverseDecl(Context.getTranslationUnitDecl()); 2628e9145e4SAnkur } 2638e9145e4SAnkur 2648e9145e4SAnkur private: 2658e9145e4SAnkur ExtractAPIVisitor<> Visitor; 2668e9145e4SAnkur }; 2678e9145e4SAnkur 26889f6b26fSZixu Wang class ExtractAPIConsumer : public ASTConsumer { 26989f6b26fSZixu Wang public: 270aebe5fc6SDaniel Grumberg ExtractAPIConsumer(ASTContext &Context, 271aebe5fc6SDaniel Grumberg std::unique_ptr<LocationFileChecker> LCF, APISet &API) 272142c3d9dSDaniel Grumberg : Visitor(*LCF, Context, API), LCF(std::move(LCF)) {} 27389f6b26fSZixu Wang 27489f6b26fSZixu Wang void HandleTranslationUnit(ASTContext &Context) override { 27589f6b26fSZixu Wang // Use ExtractAPIVisitor to traverse symbol declarations in the context. 27689f6b26fSZixu Wang Visitor.TraverseDecl(Context.getTranslationUnitDecl()); 27789f6b26fSZixu Wang } 27889f6b26fSZixu Wang 27989f6b26fSZixu Wang private: 280142c3d9dSDaniel Grumberg BatchExtractAPIVisitor Visitor; 281aebe5fc6SDaniel Grumberg std::unique_ptr<LocationFileChecker> LCF; 28289f6b26fSZixu Wang }; 28389f6b26fSZixu Wang 284529a0570SDaniel Grumberg class MacroCallback : public PPCallbacks { 285529a0570SDaniel Grumberg public: 2868e9145e4SAnkur MacroCallback(const SourceManager &SM, APISet &API, Preprocessor &PP) 2878e9145e4SAnkur : SM(SM), API(API), PP(PP) {} 288529a0570SDaniel Grumberg 289529a0570SDaniel Grumberg void EndOfMainFile() override { 290b1b24d75SDaniel Grumberg for (const auto &M : PP.macros()) { 291b1b24d75SDaniel Grumberg auto *II = M.getFirst(); 292b1b24d75SDaniel Grumberg auto MD = PP.getMacroDefinition(II); 293b1b24d75SDaniel Grumberg auto *MI = MD.getMacroInfo(); 294b1b24d75SDaniel Grumberg 295b1b24d75SDaniel Grumberg if (!MI) 296529a0570SDaniel Grumberg continue; 297529a0570SDaniel Grumberg 298b1b24d75SDaniel Grumberg // Ignore header guard macros 299b1b24d75SDaniel Grumberg if (MI->isUsedForHeaderGuard()) 300aebe5fc6SDaniel Grumberg continue; 301aebe5fc6SDaniel Grumberg 302b1b24d75SDaniel Grumberg // Ignore builtin macros and ones defined via the command line. 303b1b24d75SDaniel Grumberg if (MI->isBuiltinMacro()) 304b1b24d75SDaniel Grumberg continue; 305b1b24d75SDaniel Grumberg 306b1b24d75SDaniel Grumberg auto DefLoc = MI->getDefinitionLoc(); 307b1b24d75SDaniel Grumberg 308b1b24d75SDaniel Grumberg if (SM.isWrittenInBuiltinFile(DefLoc) || 309b1b24d75SDaniel Grumberg SM.isWrittenInCommandLineFile(DefLoc)) 310b1b24d75SDaniel Grumberg continue; 311b1b24d75SDaniel Grumberg 312b1b24d75SDaniel Grumberg auto AssociatedModuleMacros = MD.getModuleMacros(); 313b1b24d75SDaniel Grumberg StringRef OwningModuleName; 314b1b24d75SDaniel Grumberg if (!AssociatedModuleMacros.empty()) 315b1b24d75SDaniel Grumberg OwningModuleName = AssociatedModuleMacros.back() 316b1b24d75SDaniel Grumberg ->getOwningModule() 317b1b24d75SDaniel Grumberg ->getTopLevelModuleName(); 318b1b24d75SDaniel Grumberg 319b1b24d75SDaniel Grumberg if (!shouldMacroBeIncluded(DefLoc, OwningModuleName)) 320b1b24d75SDaniel Grumberg continue; 321b1b24d75SDaniel Grumberg 322b1b24d75SDaniel Grumberg StringRef Name = II->getName(); 323b1b24d75SDaniel Grumberg PresumedLoc Loc = SM.getPresumedLoc(DefLoc); 324e05c1b46SDaniel Grumberg SmallString<128> USR; 325b1b24d75SDaniel Grumberg index::generateUSRForMacro(Name, DefLoc, SM, USR); 326e05c1b46SDaniel Grumberg API.createRecord<extractapi::MacroDefinitionRecord>( 327e05c1b46SDaniel Grumberg USR, Name, SymbolReference(), Loc, 328b1b24d75SDaniel Grumberg DeclarationFragmentsBuilder::getFragmentsForMacro(Name, MI), 3297a851921SDaniel Grumberg DeclarationFragmentsBuilder::getSubHeadingForMacro(Name), 330b1b24d75SDaniel Grumberg SM.isInSystemHeader(DefLoc)); 331b1b24d75SDaniel Grumberg } 332529a0570SDaniel Grumberg } 333529a0570SDaniel Grumberg 334b1b24d75SDaniel Grumberg virtual bool shouldMacroBeIncluded(const SourceLocation &MacroLoc, 335b1b24d75SDaniel Grumberg StringRef ModuleName) { 336b1b24d75SDaniel Grumberg return true; 337529a0570SDaniel Grumberg } 338529a0570SDaniel Grumberg 339529a0570SDaniel Grumberg const SourceManager &SM; 340529a0570SDaniel Grumberg APISet &API; 34110155922SDaniel Grumberg Preprocessor &PP; 342529a0570SDaniel Grumberg }; 343529a0570SDaniel Grumberg 3448e9145e4SAnkur class APIMacroCallback : public MacroCallback { 3458e9145e4SAnkur public: 3468e9145e4SAnkur APIMacroCallback(const SourceManager &SM, APISet &API, Preprocessor &PP, 3478e9145e4SAnkur LocationFileChecker &LCF) 3488e9145e4SAnkur : MacroCallback(SM, API, PP), LCF(LCF) {} 3498e9145e4SAnkur 350b1b24d75SDaniel Grumberg bool shouldMacroBeIncluded(const SourceLocation &MacroLoc, 351b1b24d75SDaniel Grumberg StringRef ModuleName) override { 3528e9145e4SAnkur // Do not include macros from external files 35386835d2dSDaniel Grumberg return LCF(MacroLoc); 3548e9145e4SAnkur } 3558e9145e4SAnkur 3568e9145e4SAnkur private: 3578e9145e4SAnkur LocationFileChecker &LCF; 3588e9145e4SAnkur }; 3598e9145e4SAnkur 360e05c1b46SDaniel Grumberg std::unique_ptr<llvm::raw_pwrite_stream> 361e05c1b46SDaniel Grumberg createAdditionalSymbolGraphFile(CompilerInstance &CI, Twine BaseName) { 362e05c1b46SDaniel Grumberg auto OutputDirectory = CI.getFrontendOpts().SymbolGraphOutputDir; 363b31414bfSDaniel Grumberg 364e05c1b46SDaniel Grumberg SmallString<256> FileName; 365e05c1b46SDaniel Grumberg llvm::sys::path::append(FileName, OutputDirectory, 366e05c1b46SDaniel Grumberg BaseName + ".symbols.json"); 367e05c1b46SDaniel Grumberg return CI.createOutputFile( 368e05c1b46SDaniel Grumberg FileName, /*Binary*/ false, /*RemoveFileOnSignal*/ false, 369e05c1b46SDaniel Grumberg /*UseTemporary*/ true, /*CreateMissingDirectories*/ true); 370b31414bfSDaniel Grumberg } 371b31414bfSDaniel Grumberg 372e05c1b46SDaniel Grumberg } // namespace 373e05c1b46SDaniel Grumberg 374e05c1b46SDaniel Grumberg void ExtractAPIActionBase::ImplEndSourceFileAction(CompilerInstance &CI) { 375e05c1b46SDaniel Grumberg SymbolGraphSerializerOption SerializationOptions; 376e05c1b46SDaniel Grumberg SerializationOptions.Compact = !CI.getFrontendOpts().EmitPrettySymbolGraphs; 377e05c1b46SDaniel Grumberg SerializationOptions.EmitSymbolLabelsForTesting = 378e05c1b46SDaniel Grumberg CI.getFrontendOpts().EmitSymbolGraphSymbolLabelsForTesting; 379e05c1b46SDaniel Grumberg 380e05c1b46SDaniel Grumberg if (CI.getFrontendOpts().EmitExtensionSymbolGraphs) { 381e05c1b46SDaniel Grumberg auto ConstructOutputFile = [&CI](Twine BaseName) { 382e05c1b46SDaniel Grumberg return createAdditionalSymbolGraphFile(CI, BaseName); 383e05c1b46SDaniel Grumberg }; 384e05c1b46SDaniel Grumberg 385e05c1b46SDaniel Grumberg SymbolGraphSerializer::serializeWithExtensionGraphs( 386e05c1b46SDaniel Grumberg *OS, *API, IgnoresList, ConstructOutputFile, SerializationOptions); 387e05c1b46SDaniel Grumberg } else { 388e05c1b46SDaniel Grumberg SymbolGraphSerializer::serializeMainSymbolGraph(*OS, *API, IgnoresList, 389e05c1b46SDaniel Grumberg SerializationOptions); 390e05c1b46SDaniel Grumberg } 391e05c1b46SDaniel Grumberg 392e05c1b46SDaniel Grumberg // Flush the stream and close the main output stream. 393e05c1b46SDaniel Grumberg OS.reset(); 3948e9145e4SAnkur } 3958e9145e4SAnkur 39689f6b26fSZixu Wang std::unique_ptr<ASTConsumer> 39789f6b26fSZixu Wang ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { 398e05c1b46SDaniel Grumberg auto ProductName = CI.getFrontendOpts().ProductName; 399e05c1b46SDaniel Grumberg 400e05c1b46SDaniel Grumberg if (CI.getFrontendOpts().SymbolGraphOutputDir.empty()) 401e05c1b46SDaniel Grumberg OS = CI.createDefaultOutputFile(/*Binary*/ false, InFile, 402e05c1b46SDaniel Grumberg /*Extension*/ "symbols.json", 403e05c1b46SDaniel Grumberg /*RemoveFileOnSignal*/ false, 404e05c1b46SDaniel Grumberg /*CreateMissingDirectories*/ true); 405e05c1b46SDaniel Grumberg else 406e05c1b46SDaniel Grumberg OS = createAdditionalSymbolGraphFile(CI, ProductName); 4078e9145e4SAnkur 40889f6b26fSZixu Wang if (!OS) 40989f6b26fSZixu Wang return nullptr; 410a9909d23SDaniel Grumberg 411a9909d23SDaniel Grumberg // Now that we have enough information about the language options and the 412a9909d23SDaniel Grumberg // target triple, let's create the APISet before anyone uses it. 413a9909d23SDaniel Grumberg API = std::make_unique<APISet>( 414a9909d23SDaniel Grumberg CI.getTarget().getTriple(), 4157a851921SDaniel Grumberg CI.getFrontendOpts().Inputs.back().getKind().getLanguage(), ProductName); 416a9909d23SDaniel Grumberg 417cb5bb285SZixu Wang auto LCF = std::make_unique<LocationFileChecker>(CI, KnownInputFiles); 418aebe5fc6SDaniel Grumberg 4198e9145e4SAnkur CI.getPreprocessor().addPPCallbacks(std::make_unique<APIMacroCallback>( 4208e9145e4SAnkur CI.getSourceManager(), *API, CI.getPreprocessor(), *LCF)); 421529a0570SDaniel Grumberg 4225301826fSZixu Wang // Do not include location in anonymous decls. 4235301826fSZixu Wang PrintingPolicy Policy = CI.getASTContext().getPrintingPolicy(); 4245301826fSZixu Wang Policy.AnonymousTagLocations = false; 4255301826fSZixu Wang CI.getASTContext().setPrintingPolicy(Policy); 4265301826fSZixu Wang 42758825d2cSAnkur if (!CI.getFrontendOpts().ExtractAPIIgnoresFileList.empty()) { 428791fe26dSDaniel Grumberg llvm::handleAllErrors( 42958825d2cSAnkur APIIgnoresList::create(CI.getFrontendOpts().ExtractAPIIgnoresFileList, 430791fe26dSDaniel Grumberg CI.getFileManager()) 431791fe26dSDaniel Grumberg .moveInto(IgnoresList), 432791fe26dSDaniel Grumberg [&CI](const IgnoresFileNotFound &Err) { 433791fe26dSDaniel Grumberg CI.getDiagnostics().Report( 434791fe26dSDaniel Grumberg diag::err_extract_api_ignores_file_not_found) 435791fe26dSDaniel Grumberg << Err.Path; 436791fe26dSDaniel Grumberg }); 437791fe26dSDaniel Grumberg } 438791fe26dSDaniel Grumberg 439aebe5fc6SDaniel Grumberg return std::make_unique<ExtractAPIConsumer>(CI.getASTContext(), 440aebe5fc6SDaniel Grumberg std::move(LCF), *API); 44189f6b26fSZixu Wang } 44289f6b26fSZixu Wang 443f833aab0SDaniel Grumberg bool ExtractAPIAction::PrepareToExecuteAction(CompilerInstance &CI) { 444f833aab0SDaniel Grumberg auto &Inputs = CI.getFrontendOpts().Inputs; 445f833aab0SDaniel Grumberg if (Inputs.empty()) 446f833aab0SDaniel Grumberg return true; 447f833aab0SDaniel Grumberg 448cb5bb285SZixu Wang if (!CI.hasFileManager()) 449cb5bb285SZixu Wang if (!CI.createFileManager()) 450cb5bb285SZixu Wang return false; 451cb5bb285SZixu Wang 452f833aab0SDaniel Grumberg auto Kind = Inputs[0].getKind(); 453f833aab0SDaniel Grumberg 454f833aab0SDaniel Grumberg // Convert the header file inputs into a single input buffer. 455f833aab0SDaniel Grumberg SmallString<256> HeaderContents; 456cb5bb285SZixu Wang bool IsQuoted = false; 457f833aab0SDaniel Grumberg for (const FrontendInputFile &FIF : Inputs) { 458f833aab0SDaniel Grumberg if (Kind.isObjectiveC()) 459f833aab0SDaniel Grumberg HeaderContents += "#import"; 460f833aab0SDaniel Grumberg else 461f833aab0SDaniel Grumberg HeaderContents += "#include"; 4624c262feeSZixu Wang 463cb5bb285SZixu Wang StringRef FilePath = FIF.getFile(); 464cb5bb285SZixu Wang if (auto RelativeName = getRelativeIncludeName(CI, FilePath, &IsQuoted)) { 465cb5bb285SZixu Wang if (IsQuoted) 466cb5bb285SZixu Wang HeaderContents += " \""; 467cb5bb285SZixu Wang else 468cb5bb285SZixu Wang HeaderContents += " <"; 469cb5bb285SZixu Wang 470cb5bb285SZixu Wang HeaderContents += *RelativeName; 471cb5bb285SZixu Wang 472cb5bb285SZixu Wang if (IsQuoted) 473cb5bb285SZixu Wang HeaderContents += "\"\n"; 474cb5bb285SZixu Wang else 475cb5bb285SZixu Wang HeaderContents += ">\n"; 476cc344d26SAdrian Kuegel KnownInputFiles.emplace_back(static_cast<SmallString<32>>(*RelativeName), 477cc344d26SAdrian Kuegel IsQuoted); 478cb5bb285SZixu Wang } else { 479cb5bb285SZixu Wang HeaderContents += " \""; 480cb5bb285SZixu Wang HeaderContents += FilePath; 481cb5bb285SZixu Wang HeaderContents += "\"\n"; 482cb5bb285SZixu Wang KnownInputFiles.emplace_back(FilePath, true); 4832966f0faSZixu Wang } 484cb5bb285SZixu Wang } 485cb5bb285SZixu Wang 486cb5bb285SZixu Wang if (CI.getHeaderSearchOpts().Verbose) 487cb5bb285SZixu Wang CI.getVerboseOutputStream() << getInputBufferName() << ":\n" 488cb5bb285SZixu Wang << HeaderContents << "\n"; 489f833aab0SDaniel Grumberg 490985eaa1aSDaniel Grumberg Buffer = llvm::MemoryBuffer::getMemBufferCopy(HeaderContents, 491f833aab0SDaniel Grumberg getInputBufferName()); 492f833aab0SDaniel Grumberg 493f833aab0SDaniel Grumberg // Set that buffer up as our "real" input in the CompilerInstance. 494f833aab0SDaniel Grumberg Inputs.clear(); 495f833aab0SDaniel Grumberg Inputs.emplace_back(Buffer->getMemBufferRef(), Kind, /*IsSystem*/ false); 496f833aab0SDaniel Grumberg 497f833aab0SDaniel Grumberg return true; 498f833aab0SDaniel Grumberg } 499f833aab0SDaniel Grumberg 500e05c1b46SDaniel Grumberg void ExtractAPIAction::EndSourceFileAction() { 501e05c1b46SDaniel Grumberg ImplEndSourceFileAction(getCompilerInstance()); 502e05c1b46SDaniel Grumberg } 503a9909d23SDaniel Grumberg 5048e9145e4SAnkur std::unique_ptr<ASTConsumer> 5058e9145e4SAnkur WrappingExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, 5068e9145e4SAnkur StringRef InFile) { 5078e9145e4SAnkur auto OtherConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile); 5088e9145e4SAnkur if (!OtherConsumer) 5098e9145e4SAnkur return nullptr; 5108e9145e4SAnkur 5118e9145e4SAnkur CreatedASTConsumer = true; 5128e9145e4SAnkur 513e05c1b46SDaniel Grumberg ProductName = CI.getFrontendOpts().ProductName; 514e05c1b46SDaniel Grumberg auto InputFilename = llvm::sys::path::filename(InFile); 515e05c1b46SDaniel Grumberg OS = createAdditionalSymbolGraphFile(CI, InputFilename); 5168e9145e4SAnkur 5178e9145e4SAnkur // Now that we have enough information about the language options and the 5188e9145e4SAnkur // target triple, let's create the APISet before anyone uses it. 5198e9145e4SAnkur API = std::make_unique<APISet>( 5208e9145e4SAnkur CI.getTarget().getTriple(), 5218e9145e4SAnkur CI.getFrontendOpts().Inputs.back().getKind().getLanguage(), ProductName); 5228e9145e4SAnkur 5238e9145e4SAnkur CI.getPreprocessor().addPPCallbacks(std::make_unique<MacroCallback>( 5248e9145e4SAnkur CI.getSourceManager(), *API, CI.getPreprocessor())); 5258e9145e4SAnkur 5268e9145e4SAnkur // Do not include location in anonymous decls. 5278e9145e4SAnkur PrintingPolicy Policy = CI.getASTContext().getPrintingPolicy(); 5288e9145e4SAnkur Policy.AnonymousTagLocations = false; 5298e9145e4SAnkur CI.getASTContext().setPrintingPolicy(Policy); 5308e9145e4SAnkur 5318e9145e4SAnkur if (!CI.getFrontendOpts().ExtractAPIIgnoresFileList.empty()) { 5328e9145e4SAnkur llvm::handleAllErrors( 5338e9145e4SAnkur APIIgnoresList::create(CI.getFrontendOpts().ExtractAPIIgnoresFileList, 5348e9145e4SAnkur CI.getFileManager()) 5358e9145e4SAnkur .moveInto(IgnoresList), 5368e9145e4SAnkur [&CI](const IgnoresFileNotFound &Err) { 5378e9145e4SAnkur CI.getDiagnostics().Report( 5388e9145e4SAnkur diag::err_extract_api_ignores_file_not_found) 5398e9145e4SAnkur << Err.Path; 5408e9145e4SAnkur }); 5418e9145e4SAnkur } 5428e9145e4SAnkur 5438e9145e4SAnkur auto WrappingConsumer = 5448e9145e4SAnkur std::make_unique<WrappingExtractAPIConsumer>(CI.getASTContext(), *API); 5458e9145e4SAnkur std::vector<std::unique_ptr<ASTConsumer>> Consumers; 5468e9145e4SAnkur Consumers.push_back(std::move(OtherConsumer)); 5478e9145e4SAnkur Consumers.push_back(std::move(WrappingConsumer)); 5488e9145e4SAnkur 5498e9145e4SAnkur return std::make_unique<MultiplexConsumer>(std::move(Consumers)); 5508e9145e4SAnkur } 5518e9145e4SAnkur 5528e9145e4SAnkur void WrappingExtractAPIAction::EndSourceFileAction() { 5538e9145e4SAnkur // Invoke wrapped action's method. 5548e9145e4SAnkur WrapperFrontendAction::EndSourceFileAction(); 5558e9145e4SAnkur 5568e9145e4SAnkur if (CreatedASTConsumer) { 557e05c1b46SDaniel Grumberg ImplEndSourceFileAction(getCompilerInstance()); 5588e9145e4SAnkur } 559a9909d23SDaniel Grumberg } 560