181ad6265SDimitry Andric //===- ExtractAPI/ExtractAPIConsumer.cpp ------------------------*- C++ -*-===// 281ad6265SDimitry Andric // 381ad6265SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 481ad6265SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 581ad6265SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 681ad6265SDimitry Andric // 781ad6265SDimitry Andric //===----------------------------------------------------------------------===// 881ad6265SDimitry Andric /// 981ad6265SDimitry Andric /// \file 10bdd1243dSDimitry Andric /// This file implements the ExtractAPIAction, and ASTConsumer to collect API 11bdd1243dSDimitry Andric /// information. 1281ad6265SDimitry Andric /// 1381ad6265SDimitry Andric //===----------------------------------------------------------------------===// 1481ad6265SDimitry Andric 15*06c3fb27SDimitry Andric #include "clang/AST/ASTConcept.h" 1681ad6265SDimitry Andric #include "clang/AST/ASTConsumer.h" 1781ad6265SDimitry Andric #include "clang/AST/ASTContext.h" 18*06c3fb27SDimitry Andric #include "clang/AST/DeclObjC.h" 19bdd1243dSDimitry Andric #include "clang/Basic/DiagnosticFrontend.h" 2081ad6265SDimitry Andric #include "clang/Basic/SourceLocation.h" 2181ad6265SDimitry Andric #include "clang/Basic/SourceManager.h" 2281ad6265SDimitry Andric #include "clang/Basic/TargetInfo.h" 2381ad6265SDimitry Andric #include "clang/ExtractAPI/API.h" 24bdd1243dSDimitry Andric #include "clang/ExtractAPI/APIIgnoresList.h" 25bdd1243dSDimitry Andric #include "clang/ExtractAPI/ExtractAPIVisitor.h" 2681ad6265SDimitry Andric #include "clang/ExtractAPI/FrontendActions.h" 2781ad6265SDimitry Andric #include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h" 2881ad6265SDimitry Andric #include "clang/Frontend/ASTConsumers.h" 2981ad6265SDimitry Andric #include "clang/Frontend/CompilerInstance.h" 3081ad6265SDimitry Andric #include "clang/Frontend/FrontendOptions.h" 31*06c3fb27SDimitry Andric #include "clang/Frontend/MultiplexConsumer.h" 3281ad6265SDimitry Andric #include "clang/Lex/MacroInfo.h" 3381ad6265SDimitry Andric #include "clang/Lex/PPCallbacks.h" 3481ad6265SDimitry Andric #include "clang/Lex/Preprocessor.h" 3581ad6265SDimitry Andric #include "clang/Lex/PreprocessorOptions.h" 3681ad6265SDimitry Andric #include "llvm/ADT/DenseSet.h" 3781ad6265SDimitry Andric #include "llvm/ADT/STLExtras.h" 38*06c3fb27SDimitry Andric #include "llvm/ADT/SmallString.h" 3981ad6265SDimitry Andric #include "llvm/ADT/SmallVector.h" 40*06c3fb27SDimitry Andric #include "llvm/Support/Casting.h" 41bdd1243dSDimitry Andric #include "llvm/Support/Error.h" 4281ad6265SDimitry Andric #include "llvm/Support/FileSystem.h" 4381ad6265SDimitry Andric #include "llvm/Support/MemoryBuffer.h" 4481ad6265SDimitry Andric #include "llvm/Support/Path.h" 4581ad6265SDimitry Andric #include "llvm/Support/Regex.h" 4681ad6265SDimitry Andric #include "llvm/Support/raw_ostream.h" 4781ad6265SDimitry Andric #include <memory> 48bdd1243dSDimitry Andric #include <optional> 4981ad6265SDimitry Andric #include <utility> 5081ad6265SDimitry Andric 5181ad6265SDimitry Andric using namespace clang; 5281ad6265SDimitry Andric using namespace extractapi; 5381ad6265SDimitry Andric 5481ad6265SDimitry Andric namespace { 5581ad6265SDimitry Andric 56bdd1243dSDimitry Andric std::optional<std::string> getRelativeIncludeName(const CompilerInstance &CI, 5781ad6265SDimitry Andric StringRef File, 5881ad6265SDimitry Andric bool *IsQuoted = nullptr) { 5981ad6265SDimitry Andric assert(CI.hasFileManager() && 6081ad6265SDimitry Andric "CompilerInstance does not have a FileNamager!"); 6181ad6265SDimitry Andric 6281ad6265SDimitry Andric using namespace llvm::sys; 6381ad6265SDimitry Andric // Matches framework include patterns 6481ad6265SDimitry Andric const llvm::Regex Rule("/(.+)\\.framework/(.+)?Headers/(.+)"); 6581ad6265SDimitry Andric 6681ad6265SDimitry Andric const auto &FS = CI.getVirtualFileSystem(); 6781ad6265SDimitry Andric 6881ad6265SDimitry Andric SmallString<128> FilePath(File.begin(), File.end()); 6981ad6265SDimitry Andric FS.makeAbsolute(FilePath); 7081ad6265SDimitry Andric path::remove_dots(FilePath, true); 7181ad6265SDimitry Andric FilePath = path::convert_to_slash(FilePath); 7281ad6265SDimitry Andric File = FilePath; 7381ad6265SDimitry Andric 7481ad6265SDimitry Andric // Checks whether `Dir` is a strict path prefix of `File`. If so returns 7581ad6265SDimitry Andric // the prefix length. Otherwise return 0. 7681ad6265SDimitry Andric auto CheckDir = [&](llvm::StringRef Dir) -> unsigned { 7781ad6265SDimitry Andric llvm::SmallString<32> DirPath(Dir.begin(), Dir.end()); 7881ad6265SDimitry Andric FS.makeAbsolute(DirPath); 7981ad6265SDimitry Andric path::remove_dots(DirPath, true); 8081ad6265SDimitry Andric Dir = DirPath; 8181ad6265SDimitry Andric for (auto NI = path::begin(File), NE = path::end(File), 8281ad6265SDimitry Andric DI = path::begin(Dir), DE = path::end(Dir); 8381ad6265SDimitry Andric /*termination condition in loop*/; ++NI, ++DI) { 8481ad6265SDimitry Andric // '.' components in File are ignored. 8581ad6265SDimitry Andric while (NI != NE && *NI == ".") 8681ad6265SDimitry Andric ++NI; 8781ad6265SDimitry Andric if (NI == NE) 8881ad6265SDimitry Andric break; 8981ad6265SDimitry Andric 9081ad6265SDimitry Andric // '.' components in Dir are ignored. 9181ad6265SDimitry Andric while (DI != DE && *DI == ".") 9281ad6265SDimitry Andric ++DI; 9381ad6265SDimitry Andric 9481ad6265SDimitry Andric // Dir is a prefix of File, up to '.' components and choice of path 9581ad6265SDimitry Andric // separators. 9681ad6265SDimitry Andric if (DI == DE) 9781ad6265SDimitry Andric return NI - path::begin(File); 9881ad6265SDimitry Andric 9981ad6265SDimitry Andric // Consider all path separators equal. 10081ad6265SDimitry Andric if (NI->size() == 1 && DI->size() == 1 && 10181ad6265SDimitry Andric path::is_separator(NI->front()) && path::is_separator(DI->front())) 10281ad6265SDimitry Andric continue; 10381ad6265SDimitry Andric 10481ad6265SDimitry Andric // Special case Apple .sdk folders since the search path is typically a 10581ad6265SDimitry Andric // symlink like `iPhoneSimulator14.5.sdk` while the file is instead 10681ad6265SDimitry Andric // located in `iPhoneSimulator.sdk` (the real folder). 10781ad6265SDimitry Andric if (NI->endswith(".sdk") && DI->endswith(".sdk")) { 10881ad6265SDimitry Andric StringRef NBasename = path::stem(*NI); 10981ad6265SDimitry Andric StringRef DBasename = path::stem(*DI); 11081ad6265SDimitry Andric if (DBasename.startswith(NBasename)) 11181ad6265SDimitry Andric continue; 11281ad6265SDimitry Andric } 11381ad6265SDimitry Andric 11481ad6265SDimitry Andric if (*NI != *DI) 11581ad6265SDimitry Andric break; 11681ad6265SDimitry Andric } 11781ad6265SDimitry Andric return 0; 11881ad6265SDimitry Andric }; 11981ad6265SDimitry Andric 12081ad6265SDimitry Andric unsigned PrefixLength = 0; 12181ad6265SDimitry Andric 12281ad6265SDimitry Andric // Go through the search paths and find the first one that is a prefix of 12381ad6265SDimitry Andric // the header. 12481ad6265SDimitry Andric for (const auto &Entry : CI.getHeaderSearchOpts().UserEntries) { 12581ad6265SDimitry Andric // Note whether the match is found in a quoted entry. 12681ad6265SDimitry Andric if (IsQuoted) 12781ad6265SDimitry Andric *IsQuoted = Entry.Group == frontend::Quoted; 12881ad6265SDimitry Andric 12981ad6265SDimitry Andric if (auto EntryFile = CI.getFileManager().getOptionalFileRef(Entry.Path)) { 13081ad6265SDimitry Andric if (auto HMap = HeaderMap::Create(*EntryFile, CI.getFileManager())) { 13181ad6265SDimitry Andric // If this is a headermap entry, try to reverse lookup the full path 13281ad6265SDimitry Andric // for a spelled name before mapping. 13381ad6265SDimitry Andric StringRef SpelledFilename = HMap->reverseLookupFilename(File); 13481ad6265SDimitry Andric if (!SpelledFilename.empty()) 13581ad6265SDimitry Andric return SpelledFilename.str(); 13681ad6265SDimitry Andric 13781ad6265SDimitry Andric // No matching mapping in this headermap, try next search entry. 13881ad6265SDimitry Andric continue; 13981ad6265SDimitry Andric } 14081ad6265SDimitry Andric } 14181ad6265SDimitry Andric 14281ad6265SDimitry Andric // Entry is a directory search entry, try to check if it's a prefix of File. 14381ad6265SDimitry Andric PrefixLength = CheckDir(Entry.Path); 14481ad6265SDimitry Andric if (PrefixLength > 0) { 14581ad6265SDimitry Andric // The header is found in a framework path, construct the framework-style 14681ad6265SDimitry Andric // include name `<Framework/Header.h>` 14781ad6265SDimitry Andric if (Entry.IsFramework) { 14881ad6265SDimitry Andric SmallVector<StringRef, 4> Matches; 14981ad6265SDimitry Andric Rule.match(File, &Matches); 15081ad6265SDimitry Andric // Returned matches are always in stable order. 15181ad6265SDimitry Andric if (Matches.size() != 4) 152bdd1243dSDimitry Andric return std::nullopt; 15381ad6265SDimitry Andric 15481ad6265SDimitry Andric return path::convert_to_slash( 15581ad6265SDimitry Andric (Matches[1].drop_front(Matches[1].rfind('/') + 1) + "/" + 15681ad6265SDimitry Andric Matches[3]) 15781ad6265SDimitry Andric .str()); 15881ad6265SDimitry Andric } 15981ad6265SDimitry Andric 16081ad6265SDimitry Andric // The header is found in a normal search path, strip the search path 16181ad6265SDimitry Andric // prefix to get an include name. 16281ad6265SDimitry Andric return path::convert_to_slash(File.drop_front(PrefixLength)); 16381ad6265SDimitry Andric } 16481ad6265SDimitry Andric } 16581ad6265SDimitry Andric 16681ad6265SDimitry Andric // Couldn't determine a include name, use full path instead. 167bdd1243dSDimitry Andric return std::nullopt; 16881ad6265SDimitry Andric } 16981ad6265SDimitry Andric 17081ad6265SDimitry Andric struct LocationFileChecker { 171bdd1243dSDimitry Andric bool operator()(SourceLocation Loc) { 17281ad6265SDimitry Andric // If the loc refers to a macro expansion we need to first get the file 17381ad6265SDimitry Andric // location of the expansion. 17481ad6265SDimitry Andric auto &SM = CI.getSourceManager(); 17581ad6265SDimitry Andric auto FileLoc = SM.getFileLoc(Loc); 17681ad6265SDimitry Andric FileID FID = SM.getFileID(FileLoc); 17781ad6265SDimitry Andric if (FID.isInvalid()) 17881ad6265SDimitry Andric return false; 17981ad6265SDimitry Andric 18081ad6265SDimitry Andric const auto *File = SM.getFileEntryForID(FID); 18181ad6265SDimitry Andric if (!File) 18281ad6265SDimitry Andric return false; 18381ad6265SDimitry Andric 18481ad6265SDimitry Andric if (KnownFileEntries.count(File)) 18581ad6265SDimitry Andric return true; 18681ad6265SDimitry Andric 18781ad6265SDimitry Andric if (ExternalFileEntries.count(File)) 18881ad6265SDimitry Andric return false; 18981ad6265SDimitry Andric 19081ad6265SDimitry Andric StringRef FileName = File->tryGetRealPathName().empty() 19181ad6265SDimitry Andric ? File->getName() 19281ad6265SDimitry Andric : File->tryGetRealPathName(); 19381ad6265SDimitry Andric 19481ad6265SDimitry Andric // Try to reduce the include name the same way we tried to include it. 19581ad6265SDimitry Andric bool IsQuoted = false; 19681ad6265SDimitry Andric if (auto IncludeName = getRelativeIncludeName(CI, FileName, &IsQuoted)) 19781ad6265SDimitry Andric if (llvm::any_of(KnownFiles, 19881ad6265SDimitry Andric [&IsQuoted, &IncludeName](const auto &KnownFile) { 19981ad6265SDimitry Andric return KnownFile.first.equals(*IncludeName) && 20081ad6265SDimitry Andric KnownFile.second == IsQuoted; 20181ad6265SDimitry Andric })) { 20281ad6265SDimitry Andric KnownFileEntries.insert(File); 20381ad6265SDimitry Andric return true; 20481ad6265SDimitry Andric } 20581ad6265SDimitry Andric 20681ad6265SDimitry Andric // Record that the file was not found to avoid future reverse lookup for 20781ad6265SDimitry Andric // the same file. 20881ad6265SDimitry Andric ExternalFileEntries.insert(File); 20981ad6265SDimitry Andric return false; 21081ad6265SDimitry Andric } 21181ad6265SDimitry Andric 21281ad6265SDimitry Andric LocationFileChecker(const CompilerInstance &CI, 21381ad6265SDimitry Andric SmallVector<std::pair<SmallString<32>, bool>> &KnownFiles) 21481ad6265SDimitry Andric : CI(CI), KnownFiles(KnownFiles), ExternalFileEntries() { 21581ad6265SDimitry Andric for (const auto &KnownFile : KnownFiles) 21681ad6265SDimitry Andric if (auto FileEntry = CI.getFileManager().getFile(KnownFile.first)) 21781ad6265SDimitry Andric KnownFileEntries.insert(*FileEntry); 21881ad6265SDimitry Andric } 21981ad6265SDimitry Andric 22081ad6265SDimitry Andric private: 22181ad6265SDimitry Andric const CompilerInstance &CI; 22281ad6265SDimitry Andric SmallVector<std::pair<SmallString<32>, bool>> &KnownFiles; 22381ad6265SDimitry Andric llvm::DenseSet<const FileEntry *> KnownFileEntries; 22481ad6265SDimitry Andric llvm::DenseSet<const FileEntry *> ExternalFileEntries; 22581ad6265SDimitry Andric }; 22681ad6265SDimitry Andric 227*06c3fb27SDimitry Andric struct BatchExtractAPIVisitor : ExtractAPIVisitor<BatchExtractAPIVisitor> { 228*06c3fb27SDimitry Andric bool shouldDeclBeIncluded(const Decl *D) const { 229*06c3fb27SDimitry Andric bool ShouldBeIncluded = true; 230*06c3fb27SDimitry Andric // Check that we have the definition for redeclarable types. 231*06c3fb27SDimitry Andric if (auto *TD = llvm::dyn_cast<TagDecl>(D)) 232*06c3fb27SDimitry Andric ShouldBeIncluded = TD->isThisDeclarationADefinition(); 233*06c3fb27SDimitry Andric else if (auto *Interface = llvm::dyn_cast<ObjCInterfaceDecl>(D)) 234*06c3fb27SDimitry Andric ShouldBeIncluded = Interface->isThisDeclarationADefinition(); 235*06c3fb27SDimitry Andric else if (auto *Protocol = llvm::dyn_cast<ObjCProtocolDecl>(D)) 236*06c3fb27SDimitry Andric ShouldBeIncluded = Protocol->isThisDeclarationADefinition(); 237*06c3fb27SDimitry Andric 238*06c3fb27SDimitry Andric ShouldBeIncluded = ShouldBeIncluded && LCF(D->getLocation()); 239*06c3fb27SDimitry Andric return ShouldBeIncluded; 240*06c3fb27SDimitry Andric } 241*06c3fb27SDimitry Andric 242*06c3fb27SDimitry Andric BatchExtractAPIVisitor(LocationFileChecker &LCF, ASTContext &Context, 243*06c3fb27SDimitry Andric APISet &API) 244*06c3fb27SDimitry Andric : ExtractAPIVisitor<BatchExtractAPIVisitor>(Context, API), LCF(LCF) {} 245*06c3fb27SDimitry Andric 246*06c3fb27SDimitry Andric private: 247*06c3fb27SDimitry Andric LocationFileChecker &LCF; 248*06c3fb27SDimitry Andric }; 249*06c3fb27SDimitry Andric 250*06c3fb27SDimitry Andric class WrappingExtractAPIConsumer : public ASTConsumer { 25181ad6265SDimitry Andric public: 252*06c3fb27SDimitry Andric WrappingExtractAPIConsumer(ASTContext &Context, APISet &API) 253*06c3fb27SDimitry Andric : Visitor(Context, API) {} 25481ad6265SDimitry Andric 25581ad6265SDimitry Andric void HandleTranslationUnit(ASTContext &Context) override { 25681ad6265SDimitry Andric // Use ExtractAPIVisitor to traverse symbol declarations in the context. 25781ad6265SDimitry Andric Visitor.TraverseDecl(Context.getTranslationUnitDecl()); 25881ad6265SDimitry Andric } 25981ad6265SDimitry Andric 26081ad6265SDimitry Andric private: 261*06c3fb27SDimitry Andric ExtractAPIVisitor<> Visitor; 262*06c3fb27SDimitry Andric }; 263*06c3fb27SDimitry Andric 264*06c3fb27SDimitry Andric class ExtractAPIConsumer : public ASTConsumer { 265*06c3fb27SDimitry Andric public: 266*06c3fb27SDimitry Andric ExtractAPIConsumer(ASTContext &Context, 267*06c3fb27SDimitry Andric std::unique_ptr<LocationFileChecker> LCF, APISet &API) 268*06c3fb27SDimitry Andric : Visitor(*LCF, Context, API), LCF(std::move(LCF)) {} 269*06c3fb27SDimitry Andric 270*06c3fb27SDimitry Andric void HandleTranslationUnit(ASTContext &Context) override { 271*06c3fb27SDimitry Andric // Use ExtractAPIVisitor to traverse symbol declarations in the context. 272*06c3fb27SDimitry Andric Visitor.TraverseDecl(Context.getTranslationUnitDecl()); 273*06c3fb27SDimitry Andric } 274*06c3fb27SDimitry Andric 275*06c3fb27SDimitry Andric private: 276*06c3fb27SDimitry Andric BatchExtractAPIVisitor Visitor; 27781ad6265SDimitry Andric std::unique_ptr<LocationFileChecker> LCF; 27881ad6265SDimitry Andric }; 27981ad6265SDimitry Andric 28081ad6265SDimitry Andric class MacroCallback : public PPCallbacks { 28181ad6265SDimitry Andric public: 282*06c3fb27SDimitry Andric MacroCallback(const SourceManager &SM, APISet &API, Preprocessor &PP) 283*06c3fb27SDimitry Andric : SM(SM), API(API), PP(PP) {} 28481ad6265SDimitry Andric 28581ad6265SDimitry Andric void MacroDefined(const Token &MacroNameToken, 28681ad6265SDimitry Andric const MacroDirective *MD) override { 28781ad6265SDimitry Andric auto *MacroInfo = MD->getMacroInfo(); 28881ad6265SDimitry Andric 28981ad6265SDimitry Andric if (MacroInfo->isBuiltinMacro()) 29081ad6265SDimitry Andric return; 29181ad6265SDimitry Andric 29281ad6265SDimitry Andric auto SourceLoc = MacroNameToken.getLocation(); 29381ad6265SDimitry Andric if (SM.isWrittenInBuiltinFile(SourceLoc) || 29481ad6265SDimitry Andric SM.isWrittenInCommandLineFile(SourceLoc)) 29581ad6265SDimitry Andric return; 29681ad6265SDimitry Andric 29781ad6265SDimitry Andric PendingMacros.emplace_back(MacroNameToken, MD); 29881ad6265SDimitry Andric } 29981ad6265SDimitry Andric 30081ad6265SDimitry Andric // If a macro gets undefined at some point during preprocessing of the inputs 30181ad6265SDimitry Andric // it means that it isn't an exposed API and we should therefore not add a 30281ad6265SDimitry Andric // macro definition for it. 30381ad6265SDimitry Andric void MacroUndefined(const Token &MacroNameToken, const MacroDefinition &MD, 30481ad6265SDimitry Andric const MacroDirective *Undef) override { 30581ad6265SDimitry Andric // If this macro wasn't previously defined we don't need to do anything 30681ad6265SDimitry Andric // here. 30781ad6265SDimitry Andric if (!Undef) 30881ad6265SDimitry Andric return; 30981ad6265SDimitry Andric 31081ad6265SDimitry Andric llvm::erase_if(PendingMacros, [&MD, this](const PendingMacro &PM) { 31181ad6265SDimitry Andric return MD.getMacroInfo()->isIdenticalTo(*PM.MD->getMacroInfo(), PP, 31281ad6265SDimitry Andric /*Syntactically*/ false); 31381ad6265SDimitry Andric }); 31481ad6265SDimitry Andric } 31581ad6265SDimitry Andric 31681ad6265SDimitry Andric void EndOfMainFile() override { 31781ad6265SDimitry Andric for (auto &PM : PendingMacros) { 31881ad6265SDimitry Andric // `isUsedForHeaderGuard` is only set when the preprocessor leaves the 31981ad6265SDimitry Andric // file so check for it here. 32081ad6265SDimitry Andric if (PM.MD->getMacroInfo()->isUsedForHeaderGuard()) 32181ad6265SDimitry Andric continue; 32281ad6265SDimitry Andric 323*06c3fb27SDimitry Andric if (!shouldMacroBeIncluded(PM)) 32481ad6265SDimitry Andric continue; 32581ad6265SDimitry Andric 32681ad6265SDimitry Andric StringRef Name = PM.MacroNameToken.getIdentifierInfo()->getName(); 32781ad6265SDimitry Andric PresumedLoc Loc = SM.getPresumedLoc(PM.MacroNameToken.getLocation()); 32881ad6265SDimitry Andric StringRef USR = 32981ad6265SDimitry Andric API.recordUSRForMacro(Name, PM.MacroNameToken.getLocation(), SM); 33081ad6265SDimitry Andric 33181ad6265SDimitry Andric API.addMacroDefinition( 33281ad6265SDimitry Andric Name, USR, Loc, 33381ad6265SDimitry Andric DeclarationFragmentsBuilder::getFragmentsForMacro(Name, PM.MD), 334bdd1243dSDimitry Andric DeclarationFragmentsBuilder::getSubHeadingForMacro(Name), 335bdd1243dSDimitry Andric SM.isInSystemHeader(PM.MacroNameToken.getLocation())); 33681ad6265SDimitry Andric } 33781ad6265SDimitry Andric 33881ad6265SDimitry Andric PendingMacros.clear(); 33981ad6265SDimitry Andric } 34081ad6265SDimitry Andric 341*06c3fb27SDimitry Andric protected: 34281ad6265SDimitry Andric struct PendingMacro { 34381ad6265SDimitry Andric Token MacroNameToken; 34481ad6265SDimitry Andric const MacroDirective *MD; 34581ad6265SDimitry Andric 34681ad6265SDimitry Andric PendingMacro(const Token &MacroNameToken, const MacroDirective *MD) 34781ad6265SDimitry Andric : MacroNameToken(MacroNameToken), MD(MD) {} 34881ad6265SDimitry Andric }; 34981ad6265SDimitry Andric 350*06c3fb27SDimitry Andric virtual bool shouldMacroBeIncluded(const PendingMacro &PM) { return true; } 351*06c3fb27SDimitry Andric 35281ad6265SDimitry Andric const SourceManager &SM; 35381ad6265SDimitry Andric APISet &API; 35481ad6265SDimitry Andric Preprocessor &PP; 35581ad6265SDimitry Andric llvm::SmallVector<PendingMacro> PendingMacros; 35681ad6265SDimitry Andric }; 35781ad6265SDimitry Andric 358*06c3fb27SDimitry Andric class APIMacroCallback : public MacroCallback { 359*06c3fb27SDimitry Andric public: 360*06c3fb27SDimitry Andric APIMacroCallback(const SourceManager &SM, APISet &API, Preprocessor &PP, 361*06c3fb27SDimitry Andric LocationFileChecker &LCF) 362*06c3fb27SDimitry Andric : MacroCallback(SM, API, PP), LCF(LCF) {} 363*06c3fb27SDimitry Andric 364*06c3fb27SDimitry Andric bool shouldMacroBeIncluded(const PendingMacro &PM) override { 365*06c3fb27SDimitry Andric // Do not include macros from external files 366*06c3fb27SDimitry Andric return LCF(PM.MacroNameToken.getLocation()); 367*06c3fb27SDimitry Andric } 368*06c3fb27SDimitry Andric 369*06c3fb27SDimitry Andric private: 370*06c3fb27SDimitry Andric LocationFileChecker &LCF; 371*06c3fb27SDimitry Andric }; 372*06c3fb27SDimitry Andric 37381ad6265SDimitry Andric } // namespace 37481ad6265SDimitry Andric 375*06c3fb27SDimitry Andric void ExtractAPIActionBase::ImplEndSourceFileAction() { 376*06c3fb27SDimitry Andric if (!OS) 377*06c3fb27SDimitry Andric return; 378*06c3fb27SDimitry Andric 379*06c3fb27SDimitry Andric // Setup a SymbolGraphSerializer to write out collected API information in 380*06c3fb27SDimitry Andric // the Symbol Graph format. 381*06c3fb27SDimitry Andric // FIXME: Make the kind of APISerializer configurable. 382*06c3fb27SDimitry Andric SymbolGraphSerializer SGSerializer(*API, IgnoresList); 383*06c3fb27SDimitry Andric SGSerializer.serialize(*OS); 384*06c3fb27SDimitry Andric OS.reset(); 385*06c3fb27SDimitry Andric } 386*06c3fb27SDimitry Andric 387*06c3fb27SDimitry Andric std::unique_ptr<raw_pwrite_stream> 388*06c3fb27SDimitry Andric ExtractAPIAction::CreateOutputFile(CompilerInstance &CI, StringRef InFile) { 389*06c3fb27SDimitry Andric std::unique_ptr<raw_pwrite_stream> OS; 390*06c3fb27SDimitry Andric OS = CI.createDefaultOutputFile(/*Binary=*/false, InFile, 391*06c3fb27SDimitry Andric /*Extension=*/"json", 392*06c3fb27SDimitry Andric /*RemoveFileOnSignal=*/false); 393*06c3fb27SDimitry Andric if (!OS) 394*06c3fb27SDimitry Andric return nullptr; 395*06c3fb27SDimitry Andric return OS; 396*06c3fb27SDimitry Andric } 397*06c3fb27SDimitry Andric 39881ad6265SDimitry Andric std::unique_ptr<ASTConsumer> 39981ad6265SDimitry Andric ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { 40081ad6265SDimitry Andric OS = CreateOutputFile(CI, InFile); 401*06c3fb27SDimitry Andric 40281ad6265SDimitry Andric if (!OS) 40381ad6265SDimitry Andric return nullptr; 40481ad6265SDimitry Andric 405bdd1243dSDimitry Andric auto ProductName = CI.getFrontendOpts().ProductName; 40681ad6265SDimitry Andric 40781ad6265SDimitry Andric // Now that we have enough information about the language options and the 40881ad6265SDimitry Andric // target triple, let's create the APISet before anyone uses it. 40981ad6265SDimitry Andric API = std::make_unique<APISet>( 41081ad6265SDimitry Andric CI.getTarget().getTriple(), 411bdd1243dSDimitry Andric CI.getFrontendOpts().Inputs.back().getKind().getLanguage(), ProductName); 41281ad6265SDimitry Andric 41381ad6265SDimitry Andric auto LCF = std::make_unique<LocationFileChecker>(CI, KnownInputFiles); 41481ad6265SDimitry Andric 415*06c3fb27SDimitry Andric CI.getPreprocessor().addPPCallbacks(std::make_unique<APIMacroCallback>( 416*06c3fb27SDimitry Andric CI.getSourceManager(), *API, CI.getPreprocessor(), *LCF)); 41781ad6265SDimitry Andric 418bdd1243dSDimitry Andric // Do not include location in anonymous decls. 419bdd1243dSDimitry Andric PrintingPolicy Policy = CI.getASTContext().getPrintingPolicy(); 420bdd1243dSDimitry Andric Policy.AnonymousTagLocations = false; 421bdd1243dSDimitry Andric CI.getASTContext().setPrintingPolicy(Policy); 422bdd1243dSDimitry Andric 423*06c3fb27SDimitry Andric if (!CI.getFrontendOpts().ExtractAPIIgnoresFileList.empty()) { 424bdd1243dSDimitry Andric llvm::handleAllErrors( 425*06c3fb27SDimitry Andric APIIgnoresList::create(CI.getFrontendOpts().ExtractAPIIgnoresFileList, 426bdd1243dSDimitry Andric CI.getFileManager()) 427bdd1243dSDimitry Andric .moveInto(IgnoresList), 428bdd1243dSDimitry Andric [&CI](const IgnoresFileNotFound &Err) { 429bdd1243dSDimitry Andric CI.getDiagnostics().Report( 430bdd1243dSDimitry Andric diag::err_extract_api_ignores_file_not_found) 431bdd1243dSDimitry Andric << Err.Path; 432bdd1243dSDimitry Andric }); 433bdd1243dSDimitry Andric } 434bdd1243dSDimitry Andric 43581ad6265SDimitry Andric return std::make_unique<ExtractAPIConsumer>(CI.getASTContext(), 43681ad6265SDimitry Andric std::move(LCF), *API); 43781ad6265SDimitry Andric } 43881ad6265SDimitry Andric 43981ad6265SDimitry Andric bool ExtractAPIAction::PrepareToExecuteAction(CompilerInstance &CI) { 44081ad6265SDimitry Andric auto &Inputs = CI.getFrontendOpts().Inputs; 44181ad6265SDimitry Andric if (Inputs.empty()) 44281ad6265SDimitry Andric return true; 44381ad6265SDimitry Andric 44481ad6265SDimitry Andric if (!CI.hasFileManager()) 44581ad6265SDimitry Andric if (!CI.createFileManager()) 44681ad6265SDimitry Andric return false; 44781ad6265SDimitry Andric 44881ad6265SDimitry Andric auto Kind = Inputs[0].getKind(); 44981ad6265SDimitry Andric 45081ad6265SDimitry Andric // Convert the header file inputs into a single input buffer. 45181ad6265SDimitry Andric SmallString<256> HeaderContents; 45281ad6265SDimitry Andric bool IsQuoted = false; 45381ad6265SDimitry Andric for (const FrontendInputFile &FIF : Inputs) { 45481ad6265SDimitry Andric if (Kind.isObjectiveC()) 45581ad6265SDimitry Andric HeaderContents += "#import"; 45681ad6265SDimitry Andric else 45781ad6265SDimitry Andric HeaderContents += "#include"; 45881ad6265SDimitry Andric 45981ad6265SDimitry Andric StringRef FilePath = FIF.getFile(); 46081ad6265SDimitry Andric if (auto RelativeName = getRelativeIncludeName(CI, FilePath, &IsQuoted)) { 46181ad6265SDimitry Andric if (IsQuoted) 46281ad6265SDimitry Andric HeaderContents += " \""; 46381ad6265SDimitry Andric else 46481ad6265SDimitry Andric HeaderContents += " <"; 46581ad6265SDimitry Andric 46681ad6265SDimitry Andric HeaderContents += *RelativeName; 46781ad6265SDimitry Andric 46881ad6265SDimitry Andric if (IsQuoted) 46981ad6265SDimitry Andric HeaderContents += "\"\n"; 47081ad6265SDimitry Andric else 47181ad6265SDimitry Andric HeaderContents += ">\n"; 47281ad6265SDimitry Andric KnownInputFiles.emplace_back(static_cast<SmallString<32>>(*RelativeName), 47381ad6265SDimitry Andric IsQuoted); 47481ad6265SDimitry Andric } else { 47581ad6265SDimitry Andric HeaderContents += " \""; 47681ad6265SDimitry Andric HeaderContents += FilePath; 47781ad6265SDimitry Andric HeaderContents += "\"\n"; 47881ad6265SDimitry Andric KnownInputFiles.emplace_back(FilePath, true); 47981ad6265SDimitry Andric } 48081ad6265SDimitry Andric } 48181ad6265SDimitry Andric 48281ad6265SDimitry Andric if (CI.getHeaderSearchOpts().Verbose) 48381ad6265SDimitry Andric CI.getVerboseOutputStream() << getInputBufferName() << ":\n" 48481ad6265SDimitry Andric << HeaderContents << "\n"; 48581ad6265SDimitry Andric 48681ad6265SDimitry Andric Buffer = llvm::MemoryBuffer::getMemBufferCopy(HeaderContents, 48781ad6265SDimitry Andric getInputBufferName()); 48881ad6265SDimitry Andric 48981ad6265SDimitry Andric // Set that buffer up as our "real" input in the CompilerInstance. 49081ad6265SDimitry Andric Inputs.clear(); 49181ad6265SDimitry Andric Inputs.emplace_back(Buffer->getMemBufferRef(), Kind, /*IsSystem*/ false); 49281ad6265SDimitry Andric 49381ad6265SDimitry Andric return true; 49481ad6265SDimitry Andric } 49581ad6265SDimitry Andric 496*06c3fb27SDimitry Andric void ExtractAPIAction::EndSourceFileAction() { ImplEndSourceFileAction(); } 49781ad6265SDimitry Andric 498*06c3fb27SDimitry Andric std::unique_ptr<ASTConsumer> 499*06c3fb27SDimitry Andric WrappingExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, 500*06c3fb27SDimitry Andric StringRef InFile) { 501*06c3fb27SDimitry Andric auto OtherConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile); 502*06c3fb27SDimitry Andric if (!OtherConsumer) 503*06c3fb27SDimitry Andric return nullptr; 504*06c3fb27SDimitry Andric 505*06c3fb27SDimitry Andric CreatedASTConsumer = true; 506*06c3fb27SDimitry Andric 507*06c3fb27SDimitry Andric OS = CreateOutputFile(CI, InFile); 508*06c3fb27SDimitry Andric if (!OS) 509*06c3fb27SDimitry Andric return nullptr; 510*06c3fb27SDimitry Andric 511*06c3fb27SDimitry Andric auto ProductName = CI.getFrontendOpts().ProductName; 512*06c3fb27SDimitry Andric 513*06c3fb27SDimitry Andric // Now that we have enough information about the language options and the 514*06c3fb27SDimitry Andric // target triple, let's create the APISet before anyone uses it. 515*06c3fb27SDimitry Andric API = std::make_unique<APISet>( 516*06c3fb27SDimitry Andric CI.getTarget().getTriple(), 517*06c3fb27SDimitry Andric CI.getFrontendOpts().Inputs.back().getKind().getLanguage(), ProductName); 518*06c3fb27SDimitry Andric 519*06c3fb27SDimitry Andric CI.getPreprocessor().addPPCallbacks(std::make_unique<MacroCallback>( 520*06c3fb27SDimitry Andric CI.getSourceManager(), *API, CI.getPreprocessor())); 521*06c3fb27SDimitry Andric 522*06c3fb27SDimitry Andric // Do not include location in anonymous decls. 523*06c3fb27SDimitry Andric PrintingPolicy Policy = CI.getASTContext().getPrintingPolicy(); 524*06c3fb27SDimitry Andric Policy.AnonymousTagLocations = false; 525*06c3fb27SDimitry Andric CI.getASTContext().setPrintingPolicy(Policy); 526*06c3fb27SDimitry Andric 527*06c3fb27SDimitry Andric if (!CI.getFrontendOpts().ExtractAPIIgnoresFileList.empty()) { 528*06c3fb27SDimitry Andric llvm::handleAllErrors( 529*06c3fb27SDimitry Andric APIIgnoresList::create(CI.getFrontendOpts().ExtractAPIIgnoresFileList, 530*06c3fb27SDimitry Andric CI.getFileManager()) 531*06c3fb27SDimitry Andric .moveInto(IgnoresList), 532*06c3fb27SDimitry Andric [&CI](const IgnoresFileNotFound &Err) { 533*06c3fb27SDimitry Andric CI.getDiagnostics().Report( 534*06c3fb27SDimitry Andric diag::err_extract_api_ignores_file_not_found) 535*06c3fb27SDimitry Andric << Err.Path; 536*06c3fb27SDimitry Andric }); 537*06c3fb27SDimitry Andric } 538*06c3fb27SDimitry Andric 539*06c3fb27SDimitry Andric auto WrappingConsumer = 540*06c3fb27SDimitry Andric std::make_unique<WrappingExtractAPIConsumer>(CI.getASTContext(), *API); 541*06c3fb27SDimitry Andric std::vector<std::unique_ptr<ASTConsumer>> Consumers; 542*06c3fb27SDimitry Andric Consumers.push_back(std::move(OtherConsumer)); 543*06c3fb27SDimitry Andric Consumers.push_back(std::move(WrappingConsumer)); 544*06c3fb27SDimitry Andric 545*06c3fb27SDimitry Andric return std::make_unique<MultiplexConsumer>(std::move(Consumers)); 546*06c3fb27SDimitry Andric } 547*06c3fb27SDimitry Andric 548*06c3fb27SDimitry Andric void WrappingExtractAPIAction::EndSourceFileAction() { 549*06c3fb27SDimitry Andric // Invoke wrapped action's method. 550*06c3fb27SDimitry Andric WrapperFrontendAction::EndSourceFileAction(); 551*06c3fb27SDimitry Andric 552*06c3fb27SDimitry Andric if (CreatedASTConsumer) { 553*06c3fb27SDimitry Andric ImplEndSourceFileAction(); 554*06c3fb27SDimitry Andric } 55581ad6265SDimitry Andric } 55681ad6265SDimitry Andric 55781ad6265SDimitry Andric std::unique_ptr<raw_pwrite_stream> 558*06c3fb27SDimitry Andric WrappingExtractAPIAction::CreateOutputFile(CompilerInstance &CI, 559*06c3fb27SDimitry Andric StringRef InFile) { 560*06c3fb27SDimitry Andric std::unique_ptr<raw_pwrite_stream> OS; 561*06c3fb27SDimitry Andric std::string OutputDir = CI.getFrontendOpts().SymbolGraphOutputDir; 562*06c3fb27SDimitry Andric 563*06c3fb27SDimitry Andric // The symbol graphs need to be generated as a side effect of regular 564*06c3fb27SDimitry Andric // compilation so the output should be dumped in the directory provided with 565*06c3fb27SDimitry Andric // the command line option. 566*06c3fb27SDimitry Andric llvm::SmallString<128> OutFilePath(OutputDir); 567*06c3fb27SDimitry Andric auto Seperator = llvm::sys::path::get_separator(); 568*06c3fb27SDimitry Andric auto Infilename = llvm::sys::path::filename(InFile); 569*06c3fb27SDimitry Andric OutFilePath.append({Seperator, Infilename}); 570*06c3fb27SDimitry Andric llvm::sys::path::replace_extension(OutFilePath, "json"); 571*06c3fb27SDimitry Andric // StringRef outputFilePathref = *OutFilePath; 572*06c3fb27SDimitry Andric 573*06c3fb27SDimitry Andric // don't use the default output file 574*06c3fb27SDimitry Andric OS = CI.createOutputFile(/*OutputPath=*/OutFilePath, /*Binary=*/false, 575*06c3fb27SDimitry Andric /*RemoveFileOnSignal=*/true, 576*06c3fb27SDimitry Andric /*UseTemporary=*/true, 577*06c3fb27SDimitry Andric /*CreateMissingDirectories=*/true); 57881ad6265SDimitry Andric if (!OS) 57981ad6265SDimitry Andric return nullptr; 58081ad6265SDimitry Andric return OS; 58181ad6265SDimitry Andric } 582