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 1506c3fb27SDimitry Andric #include "clang/AST/ASTConcept.h" 1681ad6265SDimitry Andric #include "clang/AST/ASTConsumer.h" 1781ad6265SDimitry Andric #include "clang/AST/ASTContext.h" 1806c3fb27SDimitry Andric #include "clang/AST/DeclObjC.h" 19bdd1243dSDimitry Andric #include "clang/Basic/DiagnosticFrontend.h" 205f757f3fSDimitry Andric #include "clang/Basic/FileEntry.h" 2181ad6265SDimitry Andric #include "clang/Basic/SourceLocation.h" 2281ad6265SDimitry Andric #include "clang/Basic/SourceManager.h" 2381ad6265SDimitry Andric #include "clang/Basic/TargetInfo.h" 2481ad6265SDimitry Andric #include "clang/ExtractAPI/API.h" 25bdd1243dSDimitry Andric #include "clang/ExtractAPI/APIIgnoresList.h" 26bdd1243dSDimitry Andric #include "clang/ExtractAPI/ExtractAPIVisitor.h" 2781ad6265SDimitry Andric #include "clang/ExtractAPI/FrontendActions.h" 2881ad6265SDimitry Andric #include "clang/ExtractAPI/Serialization/SymbolGraphSerializer.h" 2981ad6265SDimitry Andric #include "clang/Frontend/ASTConsumers.h" 3081ad6265SDimitry Andric #include "clang/Frontend/CompilerInstance.h" 3181ad6265SDimitry Andric #include "clang/Frontend/FrontendOptions.h" 3206c3fb27SDimitry Andric #include "clang/Frontend/MultiplexConsumer.h" 33*0fca6ea1SDimitry Andric #include "clang/Index/USRGeneration.h" 34*0fca6ea1SDimitry Andric #include "clang/InstallAPI/HeaderFile.h" 3581ad6265SDimitry Andric #include "clang/Lex/MacroInfo.h" 3681ad6265SDimitry Andric #include "clang/Lex/PPCallbacks.h" 3781ad6265SDimitry Andric #include "clang/Lex/Preprocessor.h" 3881ad6265SDimitry Andric #include "clang/Lex/PreprocessorOptions.h" 3981ad6265SDimitry Andric #include "llvm/ADT/DenseSet.h" 4081ad6265SDimitry Andric #include "llvm/ADT/STLExtras.h" 4106c3fb27SDimitry Andric #include "llvm/ADT/SmallString.h" 4281ad6265SDimitry Andric #include "llvm/ADT/SmallVector.h" 43*0fca6ea1SDimitry Andric #include "llvm/ADT/StringRef.h" 4406c3fb27SDimitry Andric #include "llvm/Support/Casting.h" 45bdd1243dSDimitry Andric #include "llvm/Support/Error.h" 4681ad6265SDimitry Andric #include "llvm/Support/FileSystem.h" 4781ad6265SDimitry Andric #include "llvm/Support/MemoryBuffer.h" 4881ad6265SDimitry Andric #include "llvm/Support/Path.h" 4981ad6265SDimitry Andric #include "llvm/Support/Regex.h" 5081ad6265SDimitry Andric #include "llvm/Support/raw_ostream.h" 5181ad6265SDimitry Andric #include <memory> 52bdd1243dSDimitry Andric #include <optional> 5381ad6265SDimitry Andric #include <utility> 5481ad6265SDimitry Andric 5581ad6265SDimitry Andric using namespace clang; 5681ad6265SDimitry Andric using namespace extractapi; 5781ad6265SDimitry Andric 5881ad6265SDimitry Andric namespace { 5981ad6265SDimitry Andric 60bdd1243dSDimitry Andric std::optional<std::string> getRelativeIncludeName(const CompilerInstance &CI, 6181ad6265SDimitry Andric StringRef File, 6281ad6265SDimitry Andric bool *IsQuoted = nullptr) { 6381ad6265SDimitry Andric assert(CI.hasFileManager() && 6481ad6265SDimitry Andric "CompilerInstance does not have a FileNamager!"); 6581ad6265SDimitry Andric 6681ad6265SDimitry Andric using namespace llvm::sys; 6781ad6265SDimitry Andric const auto &FS = CI.getVirtualFileSystem(); 6881ad6265SDimitry Andric 6981ad6265SDimitry Andric SmallString<128> FilePath(File.begin(), File.end()); 7081ad6265SDimitry Andric FS.makeAbsolute(FilePath); 7181ad6265SDimitry Andric path::remove_dots(FilePath, true); 7281ad6265SDimitry Andric FilePath = path::convert_to_slash(FilePath); 7381ad6265SDimitry Andric File = FilePath; 7481ad6265SDimitry Andric 7581ad6265SDimitry Andric // Checks whether `Dir` is a strict path prefix of `File`. If so returns 7681ad6265SDimitry Andric // the prefix length. Otherwise return 0. 7781ad6265SDimitry Andric auto CheckDir = [&](llvm::StringRef Dir) -> unsigned { 7881ad6265SDimitry Andric llvm::SmallString<32> DirPath(Dir.begin(), Dir.end()); 7981ad6265SDimitry Andric FS.makeAbsolute(DirPath); 8081ad6265SDimitry Andric path::remove_dots(DirPath, true); 8181ad6265SDimitry Andric Dir = DirPath; 8281ad6265SDimitry Andric for (auto NI = path::begin(File), NE = path::end(File), 8381ad6265SDimitry Andric DI = path::begin(Dir), DE = path::end(Dir); 8481ad6265SDimitry Andric /*termination condition in loop*/; ++NI, ++DI) { 8581ad6265SDimitry Andric // '.' components in File are ignored. 8681ad6265SDimitry Andric while (NI != NE && *NI == ".") 8781ad6265SDimitry Andric ++NI; 8881ad6265SDimitry Andric if (NI == NE) 8981ad6265SDimitry Andric break; 9081ad6265SDimitry Andric 9181ad6265SDimitry Andric // '.' components in Dir are ignored. 9281ad6265SDimitry Andric while (DI != DE && *DI == ".") 9381ad6265SDimitry Andric ++DI; 9481ad6265SDimitry Andric 9581ad6265SDimitry Andric // Dir is a prefix of File, up to '.' components and choice of path 9681ad6265SDimitry Andric // separators. 9781ad6265SDimitry Andric if (DI == DE) 9881ad6265SDimitry Andric return NI - path::begin(File); 9981ad6265SDimitry Andric 10081ad6265SDimitry Andric // Consider all path separators equal. 10181ad6265SDimitry Andric if (NI->size() == 1 && DI->size() == 1 && 10281ad6265SDimitry Andric path::is_separator(NI->front()) && path::is_separator(DI->front())) 10381ad6265SDimitry Andric continue; 10481ad6265SDimitry Andric 10581ad6265SDimitry Andric // Special case Apple .sdk folders since the search path is typically a 10681ad6265SDimitry Andric // symlink like `iPhoneSimulator14.5.sdk` while the file is instead 10781ad6265SDimitry Andric // located in `iPhoneSimulator.sdk` (the real folder). 1085f757f3fSDimitry Andric if (NI->ends_with(".sdk") && DI->ends_with(".sdk")) { 10981ad6265SDimitry Andric StringRef NBasename = path::stem(*NI); 11081ad6265SDimitry Andric StringRef DBasename = path::stem(*DI); 1115f757f3fSDimitry Andric if (DBasename.starts_with(NBasename)) 11281ad6265SDimitry Andric continue; 11381ad6265SDimitry Andric } 11481ad6265SDimitry Andric 11581ad6265SDimitry Andric if (*NI != *DI) 11681ad6265SDimitry Andric break; 11781ad6265SDimitry Andric } 11881ad6265SDimitry Andric return 0; 11981ad6265SDimitry Andric }; 12081ad6265SDimitry Andric 12181ad6265SDimitry Andric unsigned PrefixLength = 0; 12281ad6265SDimitry Andric 12381ad6265SDimitry Andric // Go through the search paths and find the first one that is a prefix of 12481ad6265SDimitry Andric // the header. 12581ad6265SDimitry Andric for (const auto &Entry : CI.getHeaderSearchOpts().UserEntries) { 12681ad6265SDimitry Andric // Note whether the match is found in a quoted entry. 12781ad6265SDimitry Andric if (IsQuoted) 12881ad6265SDimitry Andric *IsQuoted = Entry.Group == frontend::Quoted; 12981ad6265SDimitry Andric 13081ad6265SDimitry Andric if (auto EntryFile = CI.getFileManager().getOptionalFileRef(Entry.Path)) { 13181ad6265SDimitry Andric if (auto HMap = HeaderMap::Create(*EntryFile, CI.getFileManager())) { 13281ad6265SDimitry Andric // If this is a headermap entry, try to reverse lookup the full path 13381ad6265SDimitry Andric // for a spelled name before mapping. 13481ad6265SDimitry Andric StringRef SpelledFilename = HMap->reverseLookupFilename(File); 13581ad6265SDimitry Andric if (!SpelledFilename.empty()) 13681ad6265SDimitry Andric return SpelledFilename.str(); 13781ad6265SDimitry Andric 13881ad6265SDimitry Andric // No matching mapping in this headermap, try next search entry. 13981ad6265SDimitry Andric continue; 14081ad6265SDimitry Andric } 14181ad6265SDimitry Andric } 14281ad6265SDimitry Andric 14381ad6265SDimitry Andric // Entry is a directory search entry, try to check if it's a prefix of File. 14481ad6265SDimitry Andric PrefixLength = CheckDir(Entry.Path); 14581ad6265SDimitry Andric if (PrefixLength > 0) { 14681ad6265SDimitry Andric // The header is found in a framework path, construct the framework-style 14781ad6265SDimitry Andric // include name `<Framework/Header.h>` 14881ad6265SDimitry Andric if (Entry.IsFramework) { 14981ad6265SDimitry Andric SmallVector<StringRef, 4> Matches; 150*0fca6ea1SDimitry Andric clang::installapi::HeaderFile::getFrameworkIncludeRule().match( 151*0fca6ea1SDimitry Andric File, &Matches); 15281ad6265SDimitry Andric // Returned matches are always in stable order. 15381ad6265SDimitry Andric if (Matches.size() != 4) 154bdd1243dSDimitry Andric return std::nullopt; 15581ad6265SDimitry Andric 15681ad6265SDimitry Andric return path::convert_to_slash( 15781ad6265SDimitry Andric (Matches[1].drop_front(Matches[1].rfind('/') + 1) + "/" + 15881ad6265SDimitry Andric Matches[3]) 15981ad6265SDimitry Andric .str()); 16081ad6265SDimitry Andric } 16181ad6265SDimitry Andric 16281ad6265SDimitry Andric // The header is found in a normal search path, strip the search path 16381ad6265SDimitry Andric // prefix to get an include name. 16481ad6265SDimitry Andric return path::convert_to_slash(File.drop_front(PrefixLength)); 16581ad6265SDimitry Andric } 16681ad6265SDimitry Andric } 16781ad6265SDimitry Andric 16881ad6265SDimitry Andric // Couldn't determine a include name, use full path instead. 169bdd1243dSDimitry Andric return std::nullopt; 17081ad6265SDimitry Andric } 17181ad6265SDimitry Andric 1725f757f3fSDimitry Andric std::optional<std::string> getRelativeIncludeName(const CompilerInstance &CI, 1735f757f3fSDimitry Andric FileEntryRef FE, 1745f757f3fSDimitry Andric bool *IsQuoted = nullptr) { 1755f757f3fSDimitry Andric return getRelativeIncludeName(CI, FE.getNameAsRequested(), IsQuoted); 1765f757f3fSDimitry Andric } 1775f757f3fSDimitry Andric 17881ad6265SDimitry Andric struct LocationFileChecker { 179bdd1243dSDimitry Andric bool operator()(SourceLocation Loc) { 18081ad6265SDimitry Andric // If the loc refers to a macro expansion we need to first get the file 18181ad6265SDimitry Andric // location of the expansion. 18281ad6265SDimitry Andric auto &SM = CI.getSourceManager(); 18381ad6265SDimitry Andric auto FileLoc = SM.getFileLoc(Loc); 18481ad6265SDimitry Andric FileID FID = SM.getFileID(FileLoc); 18581ad6265SDimitry Andric if (FID.isInvalid()) 18681ad6265SDimitry Andric return false; 18781ad6265SDimitry Andric 1885f757f3fSDimitry Andric OptionalFileEntryRef File = SM.getFileEntryRefForID(FID); 18981ad6265SDimitry Andric if (!File) 19081ad6265SDimitry Andric return false; 19181ad6265SDimitry Andric 1925f757f3fSDimitry Andric if (KnownFileEntries.count(*File)) 19381ad6265SDimitry Andric return true; 19481ad6265SDimitry Andric 1955f757f3fSDimitry Andric if (ExternalFileEntries.count(*File)) 19681ad6265SDimitry Andric return false; 19781ad6265SDimitry Andric 19881ad6265SDimitry Andric // Try to reduce the include name the same way we tried to include it. 19981ad6265SDimitry Andric bool IsQuoted = false; 2005f757f3fSDimitry Andric if (auto IncludeName = getRelativeIncludeName(CI, *File, &IsQuoted)) 20181ad6265SDimitry Andric if (llvm::any_of(KnownFiles, 20281ad6265SDimitry Andric [&IsQuoted, &IncludeName](const auto &KnownFile) { 20381ad6265SDimitry Andric return KnownFile.first.equals(*IncludeName) && 20481ad6265SDimitry Andric KnownFile.second == IsQuoted; 20581ad6265SDimitry Andric })) { 2065f757f3fSDimitry Andric KnownFileEntries.insert(*File); 20781ad6265SDimitry Andric return true; 20881ad6265SDimitry Andric } 20981ad6265SDimitry Andric 21081ad6265SDimitry Andric // Record that the file was not found to avoid future reverse lookup for 21181ad6265SDimitry Andric // the same file. 2125f757f3fSDimitry Andric ExternalFileEntries.insert(*File); 21381ad6265SDimitry Andric return false; 21481ad6265SDimitry Andric } 21581ad6265SDimitry Andric 21681ad6265SDimitry Andric LocationFileChecker(const CompilerInstance &CI, 21781ad6265SDimitry Andric SmallVector<std::pair<SmallString<32>, bool>> &KnownFiles) 21881ad6265SDimitry Andric : CI(CI), KnownFiles(KnownFiles), ExternalFileEntries() { 21981ad6265SDimitry Andric for (const auto &KnownFile : KnownFiles) 22081ad6265SDimitry Andric if (auto FileEntry = CI.getFileManager().getFile(KnownFile.first)) 22181ad6265SDimitry Andric KnownFileEntries.insert(*FileEntry); 22281ad6265SDimitry Andric } 22381ad6265SDimitry Andric 22481ad6265SDimitry Andric private: 22581ad6265SDimitry Andric const CompilerInstance &CI; 22681ad6265SDimitry Andric SmallVector<std::pair<SmallString<32>, bool>> &KnownFiles; 22781ad6265SDimitry Andric llvm::DenseSet<const FileEntry *> KnownFileEntries; 22881ad6265SDimitry Andric llvm::DenseSet<const FileEntry *> ExternalFileEntries; 22981ad6265SDimitry Andric }; 23081ad6265SDimitry Andric 23106c3fb27SDimitry Andric struct BatchExtractAPIVisitor : ExtractAPIVisitor<BatchExtractAPIVisitor> { 23206c3fb27SDimitry Andric bool shouldDeclBeIncluded(const Decl *D) const { 23306c3fb27SDimitry Andric bool ShouldBeIncluded = true; 23406c3fb27SDimitry Andric // Check that we have the definition for redeclarable types. 23506c3fb27SDimitry Andric if (auto *TD = llvm::dyn_cast<TagDecl>(D)) 23606c3fb27SDimitry Andric ShouldBeIncluded = TD->isThisDeclarationADefinition(); 23706c3fb27SDimitry Andric else if (auto *Interface = llvm::dyn_cast<ObjCInterfaceDecl>(D)) 23806c3fb27SDimitry Andric ShouldBeIncluded = Interface->isThisDeclarationADefinition(); 23906c3fb27SDimitry Andric else if (auto *Protocol = llvm::dyn_cast<ObjCProtocolDecl>(D)) 24006c3fb27SDimitry Andric ShouldBeIncluded = Protocol->isThisDeclarationADefinition(); 24106c3fb27SDimitry Andric 24206c3fb27SDimitry Andric ShouldBeIncluded = ShouldBeIncluded && LCF(D->getLocation()); 24306c3fb27SDimitry Andric return ShouldBeIncluded; 24406c3fb27SDimitry Andric } 24506c3fb27SDimitry Andric 24606c3fb27SDimitry Andric BatchExtractAPIVisitor(LocationFileChecker &LCF, ASTContext &Context, 24706c3fb27SDimitry Andric APISet &API) 24806c3fb27SDimitry Andric : ExtractAPIVisitor<BatchExtractAPIVisitor>(Context, API), LCF(LCF) {} 24906c3fb27SDimitry Andric 25006c3fb27SDimitry Andric private: 25106c3fb27SDimitry Andric LocationFileChecker &LCF; 25206c3fb27SDimitry Andric }; 25306c3fb27SDimitry Andric 25406c3fb27SDimitry Andric class WrappingExtractAPIConsumer : public ASTConsumer { 25581ad6265SDimitry Andric public: 25606c3fb27SDimitry Andric WrappingExtractAPIConsumer(ASTContext &Context, APISet &API) 25706c3fb27SDimitry Andric : Visitor(Context, API) {} 25881ad6265SDimitry Andric 25981ad6265SDimitry Andric void HandleTranslationUnit(ASTContext &Context) override { 26081ad6265SDimitry Andric // Use ExtractAPIVisitor to traverse symbol declarations in the context. 26181ad6265SDimitry Andric Visitor.TraverseDecl(Context.getTranslationUnitDecl()); 26281ad6265SDimitry Andric } 26381ad6265SDimitry Andric 26481ad6265SDimitry Andric private: 26506c3fb27SDimitry Andric ExtractAPIVisitor<> Visitor; 26606c3fb27SDimitry Andric }; 26706c3fb27SDimitry Andric 26806c3fb27SDimitry Andric class ExtractAPIConsumer : public ASTConsumer { 26906c3fb27SDimitry Andric public: 27006c3fb27SDimitry Andric ExtractAPIConsumer(ASTContext &Context, 27106c3fb27SDimitry Andric std::unique_ptr<LocationFileChecker> LCF, APISet &API) 27206c3fb27SDimitry Andric : Visitor(*LCF, Context, API), LCF(std::move(LCF)) {} 27306c3fb27SDimitry Andric 27406c3fb27SDimitry Andric void HandleTranslationUnit(ASTContext &Context) override { 27506c3fb27SDimitry Andric // Use ExtractAPIVisitor to traverse symbol declarations in the context. 27606c3fb27SDimitry Andric Visitor.TraverseDecl(Context.getTranslationUnitDecl()); 27706c3fb27SDimitry Andric } 27806c3fb27SDimitry Andric 27906c3fb27SDimitry Andric private: 28006c3fb27SDimitry Andric BatchExtractAPIVisitor Visitor; 28181ad6265SDimitry Andric std::unique_ptr<LocationFileChecker> LCF; 28281ad6265SDimitry Andric }; 28381ad6265SDimitry Andric 28481ad6265SDimitry Andric class MacroCallback : public PPCallbacks { 28581ad6265SDimitry Andric public: 28606c3fb27SDimitry Andric MacroCallback(const SourceManager &SM, APISet &API, Preprocessor &PP) 28706c3fb27SDimitry Andric : SM(SM), API(API), PP(PP) {} 28881ad6265SDimitry Andric 28981ad6265SDimitry Andric void MacroDefined(const Token &MacroNameToken, 29081ad6265SDimitry Andric const MacroDirective *MD) override { 29181ad6265SDimitry Andric auto *MacroInfo = MD->getMacroInfo(); 29281ad6265SDimitry Andric 29381ad6265SDimitry Andric if (MacroInfo->isBuiltinMacro()) 29481ad6265SDimitry Andric return; 29581ad6265SDimitry Andric 29681ad6265SDimitry Andric auto SourceLoc = MacroNameToken.getLocation(); 29781ad6265SDimitry Andric if (SM.isWrittenInBuiltinFile(SourceLoc) || 29881ad6265SDimitry Andric SM.isWrittenInCommandLineFile(SourceLoc)) 29981ad6265SDimitry Andric return; 30081ad6265SDimitry Andric 30181ad6265SDimitry Andric PendingMacros.emplace_back(MacroNameToken, MD); 30281ad6265SDimitry Andric } 30381ad6265SDimitry Andric 30481ad6265SDimitry Andric // If a macro gets undefined at some point during preprocessing of the inputs 30581ad6265SDimitry Andric // it means that it isn't an exposed API and we should therefore not add a 30681ad6265SDimitry Andric // macro definition for it. 30781ad6265SDimitry Andric void MacroUndefined(const Token &MacroNameToken, const MacroDefinition &MD, 30881ad6265SDimitry Andric const MacroDirective *Undef) override { 30981ad6265SDimitry Andric // If this macro wasn't previously defined we don't need to do anything 31081ad6265SDimitry Andric // here. 31181ad6265SDimitry Andric if (!Undef) 31281ad6265SDimitry Andric return; 31381ad6265SDimitry Andric 31481ad6265SDimitry Andric llvm::erase_if(PendingMacros, [&MD, this](const PendingMacro &PM) { 31581ad6265SDimitry Andric return MD.getMacroInfo()->isIdenticalTo(*PM.MD->getMacroInfo(), PP, 31681ad6265SDimitry Andric /*Syntactically*/ false); 31781ad6265SDimitry Andric }); 31881ad6265SDimitry Andric } 31981ad6265SDimitry Andric 32081ad6265SDimitry Andric void EndOfMainFile() override { 32181ad6265SDimitry Andric for (auto &PM : PendingMacros) { 32281ad6265SDimitry Andric // `isUsedForHeaderGuard` is only set when the preprocessor leaves the 32381ad6265SDimitry Andric // file so check for it here. 32481ad6265SDimitry Andric if (PM.MD->getMacroInfo()->isUsedForHeaderGuard()) 32581ad6265SDimitry Andric continue; 32681ad6265SDimitry Andric 32706c3fb27SDimitry Andric if (!shouldMacroBeIncluded(PM)) 32881ad6265SDimitry Andric continue; 32981ad6265SDimitry Andric 33081ad6265SDimitry Andric StringRef Name = PM.MacroNameToken.getIdentifierInfo()->getName(); 33181ad6265SDimitry Andric PresumedLoc Loc = SM.getPresumedLoc(PM.MacroNameToken.getLocation()); 332*0fca6ea1SDimitry Andric SmallString<128> USR; 333*0fca6ea1SDimitry Andric index::generateUSRForMacro(Name, PM.MacroNameToken.getLocation(), SM, 334*0fca6ea1SDimitry Andric USR); 33581ad6265SDimitry Andric 336*0fca6ea1SDimitry Andric API.createRecord<extractapi::MacroDefinitionRecord>( 337*0fca6ea1SDimitry Andric USR, Name, SymbolReference(), Loc, 33881ad6265SDimitry Andric DeclarationFragmentsBuilder::getFragmentsForMacro(Name, PM.MD), 339bdd1243dSDimitry Andric DeclarationFragmentsBuilder::getSubHeadingForMacro(Name), 340bdd1243dSDimitry Andric SM.isInSystemHeader(PM.MacroNameToken.getLocation())); 34181ad6265SDimitry Andric } 34281ad6265SDimitry Andric 34381ad6265SDimitry Andric PendingMacros.clear(); 34481ad6265SDimitry Andric } 34581ad6265SDimitry Andric 34606c3fb27SDimitry Andric protected: 34781ad6265SDimitry Andric struct PendingMacro { 34881ad6265SDimitry Andric Token MacroNameToken; 34981ad6265SDimitry Andric const MacroDirective *MD; 35081ad6265SDimitry Andric 35181ad6265SDimitry Andric PendingMacro(const Token &MacroNameToken, const MacroDirective *MD) 35281ad6265SDimitry Andric : MacroNameToken(MacroNameToken), MD(MD) {} 35381ad6265SDimitry Andric }; 35481ad6265SDimitry Andric 35506c3fb27SDimitry Andric virtual bool shouldMacroBeIncluded(const PendingMacro &PM) { return true; } 35606c3fb27SDimitry Andric 35781ad6265SDimitry Andric const SourceManager &SM; 35881ad6265SDimitry Andric APISet &API; 35981ad6265SDimitry Andric Preprocessor &PP; 36081ad6265SDimitry Andric llvm::SmallVector<PendingMacro> PendingMacros; 36181ad6265SDimitry Andric }; 36281ad6265SDimitry Andric 36306c3fb27SDimitry Andric class APIMacroCallback : public MacroCallback { 36406c3fb27SDimitry Andric public: 36506c3fb27SDimitry Andric APIMacroCallback(const SourceManager &SM, APISet &API, Preprocessor &PP, 36606c3fb27SDimitry Andric LocationFileChecker &LCF) 36706c3fb27SDimitry Andric : MacroCallback(SM, API, PP), LCF(LCF) {} 36806c3fb27SDimitry Andric 36906c3fb27SDimitry Andric bool shouldMacroBeIncluded(const PendingMacro &PM) override { 37006c3fb27SDimitry Andric // Do not include macros from external files 37106c3fb27SDimitry Andric return LCF(PM.MacroNameToken.getLocation()); 37206c3fb27SDimitry Andric } 37306c3fb27SDimitry Andric 37406c3fb27SDimitry Andric private: 37506c3fb27SDimitry Andric LocationFileChecker &LCF; 37606c3fb27SDimitry Andric }; 37706c3fb27SDimitry Andric 378*0fca6ea1SDimitry Andric std::unique_ptr<llvm::raw_pwrite_stream> 379*0fca6ea1SDimitry Andric createAdditionalSymbolGraphFile(CompilerInstance &CI, Twine BaseName) { 380*0fca6ea1SDimitry Andric auto OutputDirectory = CI.getFrontendOpts().SymbolGraphOutputDir; 38181ad6265SDimitry Andric 382*0fca6ea1SDimitry Andric SmallString<256> FileName; 383*0fca6ea1SDimitry Andric llvm::sys::path::append(FileName, OutputDirectory, 384*0fca6ea1SDimitry Andric BaseName + ".symbols.json"); 385*0fca6ea1SDimitry Andric return CI.createOutputFile( 386*0fca6ea1SDimitry Andric FileName, /*Binary*/ false, /*RemoveFileOnSignal*/ false, 387*0fca6ea1SDimitry Andric /*UseTemporary*/ true, /*CreateMissingDirectories*/ true); 38806c3fb27SDimitry Andric } 38906c3fb27SDimitry Andric 390*0fca6ea1SDimitry Andric } // namespace 391*0fca6ea1SDimitry Andric 392*0fca6ea1SDimitry Andric void ExtractAPIActionBase::ImplEndSourceFileAction(CompilerInstance &CI) { 393*0fca6ea1SDimitry Andric SymbolGraphSerializerOption SerializationOptions; 394*0fca6ea1SDimitry Andric SerializationOptions.Compact = !CI.getFrontendOpts().EmitPrettySymbolGraphs; 395*0fca6ea1SDimitry Andric SerializationOptions.EmitSymbolLabelsForTesting = 396*0fca6ea1SDimitry Andric CI.getFrontendOpts().EmitSymbolGraphSymbolLabelsForTesting; 397*0fca6ea1SDimitry Andric 398*0fca6ea1SDimitry Andric if (CI.getFrontendOpts().EmitExtensionSymbolGraphs) { 399*0fca6ea1SDimitry Andric auto ConstructOutputFile = [&CI](Twine BaseName) { 400*0fca6ea1SDimitry Andric return createAdditionalSymbolGraphFile(CI, BaseName); 401*0fca6ea1SDimitry Andric }; 402*0fca6ea1SDimitry Andric 403*0fca6ea1SDimitry Andric SymbolGraphSerializer::serializeWithExtensionGraphs( 404*0fca6ea1SDimitry Andric *OS, *API, IgnoresList, ConstructOutputFile, SerializationOptions); 405*0fca6ea1SDimitry Andric } else { 406*0fca6ea1SDimitry Andric SymbolGraphSerializer::serializeMainSymbolGraph(*OS, *API, IgnoresList, 407*0fca6ea1SDimitry Andric SerializationOptions); 408*0fca6ea1SDimitry Andric } 409*0fca6ea1SDimitry Andric 410*0fca6ea1SDimitry Andric // Flush the stream and close the main output stream. 411*0fca6ea1SDimitry Andric OS.reset(); 41206c3fb27SDimitry Andric } 41306c3fb27SDimitry Andric 41481ad6265SDimitry Andric std::unique_ptr<ASTConsumer> 41581ad6265SDimitry Andric ExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { 416*0fca6ea1SDimitry Andric auto ProductName = CI.getFrontendOpts().ProductName; 417*0fca6ea1SDimitry Andric 418*0fca6ea1SDimitry Andric if (CI.getFrontendOpts().SymbolGraphOutputDir.empty()) 419*0fca6ea1SDimitry Andric OS = CI.createDefaultOutputFile(/*Binary*/ false, InFile, 420*0fca6ea1SDimitry Andric /*Extension*/ "symbols.json", 421*0fca6ea1SDimitry Andric /*RemoveFileOnSignal*/ false, 422*0fca6ea1SDimitry Andric /*CreateMissingDirectories*/ true); 423*0fca6ea1SDimitry Andric else 424*0fca6ea1SDimitry Andric OS = createAdditionalSymbolGraphFile(CI, ProductName); 42506c3fb27SDimitry Andric 42681ad6265SDimitry Andric if (!OS) 42781ad6265SDimitry Andric return nullptr; 42881ad6265SDimitry Andric 42981ad6265SDimitry Andric // Now that we have enough information about the language options and the 43081ad6265SDimitry Andric // target triple, let's create the APISet before anyone uses it. 43181ad6265SDimitry Andric API = std::make_unique<APISet>( 43281ad6265SDimitry Andric CI.getTarget().getTriple(), 433bdd1243dSDimitry Andric CI.getFrontendOpts().Inputs.back().getKind().getLanguage(), ProductName); 43481ad6265SDimitry Andric 43581ad6265SDimitry Andric auto LCF = std::make_unique<LocationFileChecker>(CI, KnownInputFiles); 43681ad6265SDimitry Andric 43706c3fb27SDimitry Andric CI.getPreprocessor().addPPCallbacks(std::make_unique<APIMacroCallback>( 43806c3fb27SDimitry Andric CI.getSourceManager(), *API, CI.getPreprocessor(), *LCF)); 43981ad6265SDimitry Andric 440bdd1243dSDimitry Andric // Do not include location in anonymous decls. 441bdd1243dSDimitry Andric PrintingPolicy Policy = CI.getASTContext().getPrintingPolicy(); 442bdd1243dSDimitry Andric Policy.AnonymousTagLocations = false; 443bdd1243dSDimitry Andric CI.getASTContext().setPrintingPolicy(Policy); 444bdd1243dSDimitry Andric 44506c3fb27SDimitry Andric if (!CI.getFrontendOpts().ExtractAPIIgnoresFileList.empty()) { 446bdd1243dSDimitry Andric llvm::handleAllErrors( 44706c3fb27SDimitry Andric APIIgnoresList::create(CI.getFrontendOpts().ExtractAPIIgnoresFileList, 448bdd1243dSDimitry Andric CI.getFileManager()) 449bdd1243dSDimitry Andric .moveInto(IgnoresList), 450bdd1243dSDimitry Andric [&CI](const IgnoresFileNotFound &Err) { 451bdd1243dSDimitry Andric CI.getDiagnostics().Report( 452bdd1243dSDimitry Andric diag::err_extract_api_ignores_file_not_found) 453bdd1243dSDimitry Andric << Err.Path; 454bdd1243dSDimitry Andric }); 455bdd1243dSDimitry Andric } 456bdd1243dSDimitry Andric 45781ad6265SDimitry Andric return std::make_unique<ExtractAPIConsumer>(CI.getASTContext(), 45881ad6265SDimitry Andric std::move(LCF), *API); 45981ad6265SDimitry Andric } 46081ad6265SDimitry Andric 46181ad6265SDimitry Andric bool ExtractAPIAction::PrepareToExecuteAction(CompilerInstance &CI) { 46281ad6265SDimitry Andric auto &Inputs = CI.getFrontendOpts().Inputs; 46381ad6265SDimitry Andric if (Inputs.empty()) 46481ad6265SDimitry Andric return true; 46581ad6265SDimitry Andric 46681ad6265SDimitry Andric if (!CI.hasFileManager()) 46781ad6265SDimitry Andric if (!CI.createFileManager()) 46881ad6265SDimitry Andric return false; 46981ad6265SDimitry Andric 47081ad6265SDimitry Andric auto Kind = Inputs[0].getKind(); 47181ad6265SDimitry Andric 47281ad6265SDimitry Andric // Convert the header file inputs into a single input buffer. 47381ad6265SDimitry Andric SmallString<256> HeaderContents; 47481ad6265SDimitry Andric bool IsQuoted = false; 47581ad6265SDimitry Andric for (const FrontendInputFile &FIF : Inputs) { 47681ad6265SDimitry Andric if (Kind.isObjectiveC()) 47781ad6265SDimitry Andric HeaderContents += "#import"; 47881ad6265SDimitry Andric else 47981ad6265SDimitry Andric HeaderContents += "#include"; 48081ad6265SDimitry Andric 48181ad6265SDimitry Andric StringRef FilePath = FIF.getFile(); 48281ad6265SDimitry Andric if (auto RelativeName = getRelativeIncludeName(CI, FilePath, &IsQuoted)) { 48381ad6265SDimitry Andric if (IsQuoted) 48481ad6265SDimitry Andric HeaderContents += " \""; 48581ad6265SDimitry Andric else 48681ad6265SDimitry Andric HeaderContents += " <"; 48781ad6265SDimitry Andric 48881ad6265SDimitry Andric HeaderContents += *RelativeName; 48981ad6265SDimitry Andric 49081ad6265SDimitry Andric if (IsQuoted) 49181ad6265SDimitry Andric HeaderContents += "\"\n"; 49281ad6265SDimitry Andric else 49381ad6265SDimitry Andric HeaderContents += ">\n"; 49481ad6265SDimitry Andric KnownInputFiles.emplace_back(static_cast<SmallString<32>>(*RelativeName), 49581ad6265SDimitry Andric IsQuoted); 49681ad6265SDimitry Andric } else { 49781ad6265SDimitry Andric HeaderContents += " \""; 49881ad6265SDimitry Andric HeaderContents += FilePath; 49981ad6265SDimitry Andric HeaderContents += "\"\n"; 50081ad6265SDimitry Andric KnownInputFiles.emplace_back(FilePath, true); 50181ad6265SDimitry Andric } 50281ad6265SDimitry Andric } 50381ad6265SDimitry Andric 50481ad6265SDimitry Andric if (CI.getHeaderSearchOpts().Verbose) 50581ad6265SDimitry Andric CI.getVerboseOutputStream() << getInputBufferName() << ":\n" 50681ad6265SDimitry Andric << HeaderContents << "\n"; 50781ad6265SDimitry Andric 50881ad6265SDimitry Andric Buffer = llvm::MemoryBuffer::getMemBufferCopy(HeaderContents, 50981ad6265SDimitry Andric getInputBufferName()); 51081ad6265SDimitry Andric 51181ad6265SDimitry Andric // Set that buffer up as our "real" input in the CompilerInstance. 51281ad6265SDimitry Andric Inputs.clear(); 51381ad6265SDimitry Andric Inputs.emplace_back(Buffer->getMemBufferRef(), Kind, /*IsSystem*/ false); 51481ad6265SDimitry Andric 51581ad6265SDimitry Andric return true; 51681ad6265SDimitry Andric } 51781ad6265SDimitry Andric 518*0fca6ea1SDimitry Andric void ExtractAPIAction::EndSourceFileAction() { 519*0fca6ea1SDimitry Andric ImplEndSourceFileAction(getCompilerInstance()); 520*0fca6ea1SDimitry Andric } 52181ad6265SDimitry Andric 52206c3fb27SDimitry Andric std::unique_ptr<ASTConsumer> 52306c3fb27SDimitry Andric WrappingExtractAPIAction::CreateASTConsumer(CompilerInstance &CI, 52406c3fb27SDimitry Andric StringRef InFile) { 52506c3fb27SDimitry Andric auto OtherConsumer = WrapperFrontendAction::CreateASTConsumer(CI, InFile); 52606c3fb27SDimitry Andric if (!OtherConsumer) 52706c3fb27SDimitry Andric return nullptr; 52806c3fb27SDimitry Andric 52906c3fb27SDimitry Andric CreatedASTConsumer = true; 53006c3fb27SDimitry Andric 531*0fca6ea1SDimitry Andric ProductName = CI.getFrontendOpts().ProductName; 532*0fca6ea1SDimitry Andric auto InputFilename = llvm::sys::path::filename(InFile); 533*0fca6ea1SDimitry Andric OS = createAdditionalSymbolGraphFile(CI, InputFilename); 53406c3fb27SDimitry Andric 53506c3fb27SDimitry Andric // Now that we have enough information about the language options and the 53606c3fb27SDimitry Andric // target triple, let's create the APISet before anyone uses it. 53706c3fb27SDimitry Andric API = std::make_unique<APISet>( 53806c3fb27SDimitry Andric CI.getTarget().getTriple(), 53906c3fb27SDimitry Andric CI.getFrontendOpts().Inputs.back().getKind().getLanguage(), ProductName); 54006c3fb27SDimitry Andric 54106c3fb27SDimitry Andric CI.getPreprocessor().addPPCallbacks(std::make_unique<MacroCallback>( 54206c3fb27SDimitry Andric CI.getSourceManager(), *API, CI.getPreprocessor())); 54306c3fb27SDimitry Andric 54406c3fb27SDimitry Andric // Do not include location in anonymous decls. 54506c3fb27SDimitry Andric PrintingPolicy Policy = CI.getASTContext().getPrintingPolicy(); 54606c3fb27SDimitry Andric Policy.AnonymousTagLocations = false; 54706c3fb27SDimitry Andric CI.getASTContext().setPrintingPolicy(Policy); 54806c3fb27SDimitry Andric 54906c3fb27SDimitry Andric if (!CI.getFrontendOpts().ExtractAPIIgnoresFileList.empty()) { 55006c3fb27SDimitry Andric llvm::handleAllErrors( 55106c3fb27SDimitry Andric APIIgnoresList::create(CI.getFrontendOpts().ExtractAPIIgnoresFileList, 55206c3fb27SDimitry Andric CI.getFileManager()) 55306c3fb27SDimitry Andric .moveInto(IgnoresList), 55406c3fb27SDimitry Andric [&CI](const IgnoresFileNotFound &Err) { 55506c3fb27SDimitry Andric CI.getDiagnostics().Report( 55606c3fb27SDimitry Andric diag::err_extract_api_ignores_file_not_found) 55706c3fb27SDimitry Andric << Err.Path; 55806c3fb27SDimitry Andric }); 55906c3fb27SDimitry Andric } 56006c3fb27SDimitry Andric 56106c3fb27SDimitry Andric auto WrappingConsumer = 56206c3fb27SDimitry Andric std::make_unique<WrappingExtractAPIConsumer>(CI.getASTContext(), *API); 56306c3fb27SDimitry Andric std::vector<std::unique_ptr<ASTConsumer>> Consumers; 56406c3fb27SDimitry Andric Consumers.push_back(std::move(OtherConsumer)); 56506c3fb27SDimitry Andric Consumers.push_back(std::move(WrappingConsumer)); 56606c3fb27SDimitry Andric 56706c3fb27SDimitry Andric return std::make_unique<MultiplexConsumer>(std::move(Consumers)); 56806c3fb27SDimitry Andric } 56906c3fb27SDimitry Andric 57006c3fb27SDimitry Andric void WrappingExtractAPIAction::EndSourceFileAction() { 57106c3fb27SDimitry Andric // Invoke wrapped action's method. 57206c3fb27SDimitry Andric WrapperFrontendAction::EndSourceFileAction(); 57306c3fb27SDimitry Andric 57406c3fb27SDimitry Andric if (CreatedASTConsumer) { 575*0fca6ea1SDimitry Andric ImplEndSourceFileAction(getCompilerInstance()); 57606c3fb27SDimitry Andric } 57781ad6265SDimitry Andric } 578