15f757f3fSDimitry Andric //===--- APINotesManager.cpp - Manage API Notes Files ---------------------===// 25f757f3fSDimitry Andric // 35f757f3fSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 45f757f3fSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 55f757f3fSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 65f757f3fSDimitry Andric // 75f757f3fSDimitry Andric //===----------------------------------------------------------------------===// 85f757f3fSDimitry Andric 95f757f3fSDimitry Andric #include "clang/APINotes/APINotesManager.h" 105f757f3fSDimitry Andric #include "clang/APINotes/APINotesReader.h" 115f757f3fSDimitry Andric #include "clang/APINotes/APINotesYAMLCompiler.h" 125f757f3fSDimitry Andric #include "clang/Basic/Diagnostic.h" 135f757f3fSDimitry Andric #include "clang/Basic/FileManager.h" 145f757f3fSDimitry Andric #include "clang/Basic/LangOptions.h" 15*0fca6ea1SDimitry Andric #include "clang/Basic/Module.h" 165f757f3fSDimitry Andric #include "clang/Basic/SourceManager.h" 175f757f3fSDimitry Andric #include "clang/Basic/SourceMgrAdapter.h" 185f757f3fSDimitry Andric #include "clang/Basic/Version.h" 195f757f3fSDimitry Andric #include "llvm/ADT/APInt.h" 205f757f3fSDimitry Andric #include "llvm/ADT/Hashing.h" 215f757f3fSDimitry Andric #include "llvm/ADT/SetVector.h" 225f757f3fSDimitry Andric #include "llvm/ADT/SmallPtrSet.h" 235f757f3fSDimitry Andric #include "llvm/ADT/SmallVector.h" 245f757f3fSDimitry Andric #include "llvm/ADT/Statistic.h" 255f757f3fSDimitry Andric #include "llvm/Support/MemoryBuffer.h" 265f757f3fSDimitry Andric #include "llvm/Support/Path.h" 275f757f3fSDimitry Andric #include "llvm/Support/PrettyStackTrace.h" 285f757f3fSDimitry Andric 295f757f3fSDimitry Andric using namespace clang; 305f757f3fSDimitry Andric using namespace api_notes; 315f757f3fSDimitry Andric 325f757f3fSDimitry Andric #define DEBUG_TYPE "API Notes" 335f757f3fSDimitry Andric STATISTIC(NumHeaderAPINotes, "non-framework API notes files loaded"); 345f757f3fSDimitry Andric STATISTIC(NumPublicFrameworkAPINotes, "framework public API notes loaded"); 355f757f3fSDimitry Andric STATISTIC(NumPrivateFrameworkAPINotes, "framework private API notes loaded"); 365f757f3fSDimitry Andric STATISTIC(NumFrameworksSearched, "frameworks searched"); 375f757f3fSDimitry Andric STATISTIC(NumDirectoriesSearched, "header directories searched"); 385f757f3fSDimitry Andric STATISTIC(NumDirectoryCacheHits, "directory cache hits"); 395f757f3fSDimitry Andric 405f757f3fSDimitry Andric namespace { 415f757f3fSDimitry Andric /// Prints two successive strings, which much be kept alive as long as the 425f757f3fSDimitry Andric /// PrettyStackTrace entry. 435f757f3fSDimitry Andric class PrettyStackTraceDoubleString : public llvm::PrettyStackTraceEntry { 445f757f3fSDimitry Andric StringRef First, Second; 455f757f3fSDimitry Andric 465f757f3fSDimitry Andric public: 475f757f3fSDimitry Andric PrettyStackTraceDoubleString(StringRef First, StringRef Second) 485f757f3fSDimitry Andric : First(First), Second(Second) {} 495f757f3fSDimitry Andric void print(raw_ostream &OS) const override { OS << First << Second; } 505f757f3fSDimitry Andric }; 515f757f3fSDimitry Andric } // namespace 525f757f3fSDimitry Andric 535f757f3fSDimitry Andric APINotesManager::APINotesManager(SourceManager &SM, const LangOptions &LangOpts) 545f757f3fSDimitry Andric : SM(SM), ImplicitAPINotes(LangOpts.APINotes) {} 555f757f3fSDimitry Andric 565f757f3fSDimitry Andric APINotesManager::~APINotesManager() { 575f757f3fSDimitry Andric // Free the API notes readers. 585f757f3fSDimitry Andric for (const auto &Entry : Readers) { 595f757f3fSDimitry Andric if (auto Reader = Entry.second.dyn_cast<APINotesReader *>()) 605f757f3fSDimitry Andric delete Reader; 615f757f3fSDimitry Andric } 625f757f3fSDimitry Andric 635f757f3fSDimitry Andric delete CurrentModuleReaders[ReaderKind::Public]; 645f757f3fSDimitry Andric delete CurrentModuleReaders[ReaderKind::Private]; 655f757f3fSDimitry Andric } 665f757f3fSDimitry Andric 675f757f3fSDimitry Andric std::unique_ptr<APINotesReader> 685f757f3fSDimitry Andric APINotesManager::loadAPINotes(FileEntryRef APINotesFile) { 695f757f3fSDimitry Andric PrettyStackTraceDoubleString Trace("Loading API notes from ", 705f757f3fSDimitry Andric APINotesFile.getName()); 715f757f3fSDimitry Andric 725f757f3fSDimitry Andric // Open the source file. 735f757f3fSDimitry Andric auto SourceFileID = SM.getOrCreateFileID(APINotesFile, SrcMgr::C_User); 745f757f3fSDimitry Andric auto SourceBuffer = SM.getBufferOrNone(SourceFileID, SourceLocation()); 755f757f3fSDimitry Andric if (!SourceBuffer) 765f757f3fSDimitry Andric return nullptr; 775f757f3fSDimitry Andric 785f757f3fSDimitry Andric // Compile the API notes source into a buffer. 795f757f3fSDimitry Andric // FIXME: Either propagate OSType through or, better yet, improve the binary 805f757f3fSDimitry Andric // APINotes format to maintain complete availability information. 815f757f3fSDimitry Andric // FIXME: We don't even really need to go through the binary format at all; 825f757f3fSDimitry Andric // we're just going to immediately deserialize it again. 835f757f3fSDimitry Andric llvm::SmallVector<char, 1024> APINotesBuffer; 845f757f3fSDimitry Andric std::unique_ptr<llvm::MemoryBuffer> CompiledBuffer; 855f757f3fSDimitry Andric { 865f757f3fSDimitry Andric SourceMgrAdapter SMAdapter( 875f757f3fSDimitry Andric SM, SM.getDiagnostics(), diag::err_apinotes_message, 885f757f3fSDimitry Andric diag::warn_apinotes_message, diag::note_apinotes_message, APINotesFile); 895f757f3fSDimitry Andric llvm::raw_svector_ostream OS(APINotesBuffer); 905f757f3fSDimitry Andric if (api_notes::compileAPINotes( 915f757f3fSDimitry Andric SourceBuffer->getBuffer(), SM.getFileEntryForID(SourceFileID), OS, 925f757f3fSDimitry Andric SMAdapter.getDiagHandler(), SMAdapter.getDiagContext())) 935f757f3fSDimitry Andric return nullptr; 945f757f3fSDimitry Andric 955f757f3fSDimitry Andric // Make a copy of the compiled form into the buffer. 965f757f3fSDimitry Andric CompiledBuffer = llvm::MemoryBuffer::getMemBufferCopy( 975f757f3fSDimitry Andric StringRef(APINotesBuffer.data(), APINotesBuffer.size())); 985f757f3fSDimitry Andric } 995f757f3fSDimitry Andric 1005f757f3fSDimitry Andric // Load the binary form we just compiled. 1015f757f3fSDimitry Andric auto Reader = APINotesReader::Create(std::move(CompiledBuffer), SwiftVersion); 1025f757f3fSDimitry Andric assert(Reader && "Could not load the API notes we just generated?"); 1035f757f3fSDimitry Andric return Reader; 1045f757f3fSDimitry Andric } 1055f757f3fSDimitry Andric 1065f757f3fSDimitry Andric std::unique_ptr<APINotesReader> 1075f757f3fSDimitry Andric APINotesManager::loadAPINotes(StringRef Buffer) { 1085f757f3fSDimitry Andric llvm::SmallVector<char, 1024> APINotesBuffer; 1095f757f3fSDimitry Andric std::unique_ptr<llvm::MemoryBuffer> CompiledBuffer; 1105f757f3fSDimitry Andric SourceMgrAdapter SMAdapter( 1115f757f3fSDimitry Andric SM, SM.getDiagnostics(), diag::err_apinotes_message, 1125f757f3fSDimitry Andric diag::warn_apinotes_message, diag::note_apinotes_message, std::nullopt); 1135f757f3fSDimitry Andric llvm::raw_svector_ostream OS(APINotesBuffer); 1145f757f3fSDimitry Andric 1155f757f3fSDimitry Andric if (api_notes::compileAPINotes(Buffer, nullptr, OS, 1165f757f3fSDimitry Andric SMAdapter.getDiagHandler(), 1175f757f3fSDimitry Andric SMAdapter.getDiagContext())) 1185f757f3fSDimitry Andric return nullptr; 1195f757f3fSDimitry Andric 1205f757f3fSDimitry Andric CompiledBuffer = llvm::MemoryBuffer::getMemBufferCopy( 1215f757f3fSDimitry Andric StringRef(APINotesBuffer.data(), APINotesBuffer.size())); 1225f757f3fSDimitry Andric auto Reader = APINotesReader::Create(std::move(CompiledBuffer), SwiftVersion); 1235f757f3fSDimitry Andric assert(Reader && "Could not load the API notes we just generated?"); 1245f757f3fSDimitry Andric return Reader; 1255f757f3fSDimitry Andric } 1265f757f3fSDimitry Andric 1275f757f3fSDimitry Andric bool APINotesManager::loadAPINotes(const DirectoryEntry *HeaderDir, 1285f757f3fSDimitry Andric FileEntryRef APINotesFile) { 129cb14a3feSDimitry Andric assert(!Readers.contains(HeaderDir)); 1305f757f3fSDimitry Andric if (auto Reader = loadAPINotes(APINotesFile)) { 1315f757f3fSDimitry Andric Readers[HeaderDir] = Reader.release(); 1325f757f3fSDimitry Andric return false; 1335f757f3fSDimitry Andric } 1345f757f3fSDimitry Andric 1355f757f3fSDimitry Andric Readers[HeaderDir] = nullptr; 1365f757f3fSDimitry Andric return true; 1375f757f3fSDimitry Andric } 1385f757f3fSDimitry Andric 1395f757f3fSDimitry Andric OptionalFileEntryRef 1405f757f3fSDimitry Andric APINotesManager::findAPINotesFile(DirectoryEntryRef Directory, 1415f757f3fSDimitry Andric StringRef Basename, bool WantPublic) { 1425f757f3fSDimitry Andric FileManager &FM = SM.getFileManager(); 1435f757f3fSDimitry Andric 1445f757f3fSDimitry Andric llvm::SmallString<128> Path(Directory.getName()); 1455f757f3fSDimitry Andric 1465f757f3fSDimitry Andric StringRef Suffix = WantPublic ? "" : "_private"; 1475f757f3fSDimitry Andric 1485f757f3fSDimitry Andric // Look for the source API notes file. 1495f757f3fSDimitry Andric llvm::sys::path::append(Path, llvm::Twine(Basename) + Suffix + "." + 1505f757f3fSDimitry Andric SOURCE_APINOTES_EXTENSION); 1515f757f3fSDimitry Andric return FM.getOptionalFileRef(Path, /*Open*/ true); 1525f757f3fSDimitry Andric } 1535f757f3fSDimitry Andric 1545f757f3fSDimitry Andric OptionalDirectoryEntryRef APINotesManager::loadFrameworkAPINotes( 1555f757f3fSDimitry Andric llvm::StringRef FrameworkPath, llvm::StringRef FrameworkName, bool Public) { 1565f757f3fSDimitry Andric FileManager &FM = SM.getFileManager(); 1575f757f3fSDimitry Andric 1585f757f3fSDimitry Andric llvm::SmallString<128> Path(FrameworkPath); 1595f757f3fSDimitry Andric unsigned FrameworkNameLength = Path.size(); 1605f757f3fSDimitry Andric 1615f757f3fSDimitry Andric StringRef Suffix = Public ? "" : "_private"; 1625f757f3fSDimitry Andric 1635f757f3fSDimitry Andric // Form the path to the APINotes file. 1645f757f3fSDimitry Andric llvm::sys::path::append(Path, "APINotes"); 1655f757f3fSDimitry Andric llvm::sys::path::append(Path, (llvm::Twine(FrameworkName) + Suffix + "." + 1665f757f3fSDimitry Andric SOURCE_APINOTES_EXTENSION)); 1675f757f3fSDimitry Andric 1685f757f3fSDimitry Andric // Try to open the APINotes file. 1695f757f3fSDimitry Andric auto APINotesFile = FM.getOptionalFileRef(Path); 1705f757f3fSDimitry Andric if (!APINotesFile) 1715f757f3fSDimitry Andric return std::nullopt; 1725f757f3fSDimitry Andric 1735f757f3fSDimitry Andric // Form the path to the corresponding header directory. 1745f757f3fSDimitry Andric Path.resize(FrameworkNameLength); 1755f757f3fSDimitry Andric llvm::sys::path::append(Path, Public ? "Headers" : "PrivateHeaders"); 1765f757f3fSDimitry Andric 1775f757f3fSDimitry Andric // Try to access the header directory. 1785f757f3fSDimitry Andric auto HeaderDir = FM.getOptionalDirectoryRef(Path); 1795f757f3fSDimitry Andric if (!HeaderDir) 1805f757f3fSDimitry Andric return std::nullopt; 1815f757f3fSDimitry Andric 1825f757f3fSDimitry Andric // Try to load the API notes. 1835f757f3fSDimitry Andric if (loadAPINotes(*HeaderDir, *APINotesFile)) 1845f757f3fSDimitry Andric return std::nullopt; 1855f757f3fSDimitry Andric 1865f757f3fSDimitry Andric // Success: return the header directory. 1875f757f3fSDimitry Andric if (Public) 1885f757f3fSDimitry Andric ++NumPublicFrameworkAPINotes; 1895f757f3fSDimitry Andric else 1905f757f3fSDimitry Andric ++NumPrivateFrameworkAPINotes; 1915f757f3fSDimitry Andric return *HeaderDir; 1925f757f3fSDimitry Andric } 1935f757f3fSDimitry Andric 1945f757f3fSDimitry Andric static void checkPrivateAPINotesName(DiagnosticsEngine &Diags, 1955f757f3fSDimitry Andric const FileEntry *File, const Module *M) { 1965f757f3fSDimitry Andric if (File->tryGetRealPathName().empty()) 1975f757f3fSDimitry Andric return; 1985f757f3fSDimitry Andric 1995f757f3fSDimitry Andric StringRef RealFileName = 2005f757f3fSDimitry Andric llvm::sys::path::filename(File->tryGetRealPathName()); 2015f757f3fSDimitry Andric StringRef RealStem = llvm::sys::path::stem(RealFileName); 2025f757f3fSDimitry Andric if (RealStem.ends_with("_private")) 2035f757f3fSDimitry Andric return; 2045f757f3fSDimitry Andric 2055f757f3fSDimitry Andric unsigned DiagID = diag::warn_apinotes_private_case; 2065f757f3fSDimitry Andric if (M->IsSystem) 2075f757f3fSDimitry Andric DiagID = diag::warn_apinotes_private_case_system; 2085f757f3fSDimitry Andric 2095f757f3fSDimitry Andric Diags.Report(SourceLocation(), DiagID) << M->Name << RealFileName; 2105f757f3fSDimitry Andric } 2115f757f3fSDimitry Andric 2125f757f3fSDimitry Andric /// \returns true if any of \p module's immediate submodules are defined in a 2135f757f3fSDimitry Andric /// private module map 2145f757f3fSDimitry Andric static bool hasPrivateSubmodules(const Module *M) { 2155f757f3fSDimitry Andric return llvm::any_of(M->submodules(), [](const Module *Submodule) { 2165f757f3fSDimitry Andric return Submodule->ModuleMapIsPrivate; 2175f757f3fSDimitry Andric }); 2185f757f3fSDimitry Andric } 2195f757f3fSDimitry Andric 2205f757f3fSDimitry Andric llvm::SmallVector<FileEntryRef, 2> 2215f757f3fSDimitry Andric APINotesManager::getCurrentModuleAPINotes(Module *M, bool LookInModule, 2225f757f3fSDimitry Andric ArrayRef<std::string> SearchPaths) { 2235f757f3fSDimitry Andric FileManager &FM = SM.getFileManager(); 2245f757f3fSDimitry Andric auto ModuleName = M->getTopLevelModuleName(); 225*0fca6ea1SDimitry Andric auto ExportedModuleName = M->getTopLevelModule()->ExportAsModule; 2265f757f3fSDimitry Andric llvm::SmallVector<FileEntryRef, 2> APINotes; 2275f757f3fSDimitry Andric 2285f757f3fSDimitry Andric // First, look relative to the module itself. 229*0fca6ea1SDimitry Andric if (LookInModule && M->Directory) { 2305f757f3fSDimitry Andric // Local function to try loading an API notes file in the given directory. 2315f757f3fSDimitry Andric auto tryAPINotes = [&](DirectoryEntryRef Dir, bool WantPublic) { 2325f757f3fSDimitry Andric if (auto File = findAPINotesFile(Dir, ModuleName, WantPublic)) { 2335f757f3fSDimitry Andric if (!WantPublic) 2345f757f3fSDimitry Andric checkPrivateAPINotesName(SM.getDiagnostics(), *File, M); 2355f757f3fSDimitry Andric 2365f757f3fSDimitry Andric APINotes.push_back(*File); 2375f757f3fSDimitry Andric } 238*0fca6ea1SDimitry Andric // If module FooCore is re-exported through module Foo, try Foo.apinotes. 239*0fca6ea1SDimitry Andric if (!ExportedModuleName.empty()) 240*0fca6ea1SDimitry Andric if (auto File = findAPINotesFile(Dir, ExportedModuleName, WantPublic)) 241*0fca6ea1SDimitry Andric APINotes.push_back(*File); 2425f757f3fSDimitry Andric }; 2435f757f3fSDimitry Andric 2445f757f3fSDimitry Andric if (M->IsFramework) { 2455f757f3fSDimitry Andric // For frameworks, we search in the "Headers" or "PrivateHeaders" 2465f757f3fSDimitry Andric // subdirectory. 2475f757f3fSDimitry Andric // 2485f757f3fSDimitry Andric // Public modules: 2495f757f3fSDimitry Andric // - Headers/Foo.apinotes 2505f757f3fSDimitry Andric // - PrivateHeaders/Foo_private.apinotes (if there are private submodules) 2515f757f3fSDimitry Andric // Private modules: 2525f757f3fSDimitry Andric // - PrivateHeaders/Bar.apinotes (except that 'Bar' probably already has 2535f757f3fSDimitry Andric // the word "Private" in it in practice) 2545f757f3fSDimitry Andric llvm::SmallString<128> Path(M->Directory->getName()); 2555f757f3fSDimitry Andric 2565f757f3fSDimitry Andric if (!M->ModuleMapIsPrivate) { 2575f757f3fSDimitry Andric unsigned PathLen = Path.size(); 2585f757f3fSDimitry Andric 2595f757f3fSDimitry Andric llvm::sys::path::append(Path, "Headers"); 2605f757f3fSDimitry Andric if (auto APINotesDir = FM.getOptionalDirectoryRef(Path)) 2615f757f3fSDimitry Andric tryAPINotes(*APINotesDir, /*wantPublic=*/true); 2625f757f3fSDimitry Andric 2635f757f3fSDimitry Andric Path.resize(PathLen); 2645f757f3fSDimitry Andric } 2655f757f3fSDimitry Andric 2665f757f3fSDimitry Andric if (M->ModuleMapIsPrivate || hasPrivateSubmodules(M)) { 2675f757f3fSDimitry Andric llvm::sys::path::append(Path, "PrivateHeaders"); 2685f757f3fSDimitry Andric if (auto PrivateAPINotesDir = FM.getOptionalDirectoryRef(Path)) 2695f757f3fSDimitry Andric tryAPINotes(*PrivateAPINotesDir, 2705f757f3fSDimitry Andric /*wantPublic=*/M->ModuleMapIsPrivate); 2715f757f3fSDimitry Andric } 2725f757f3fSDimitry Andric } else { 2735f757f3fSDimitry Andric // Public modules: 2745f757f3fSDimitry Andric // - Foo.apinotes 2755f757f3fSDimitry Andric // - Foo_private.apinotes (if there are private submodules) 2765f757f3fSDimitry Andric // Private modules: 2775f757f3fSDimitry Andric // - Bar.apinotes (except that 'Bar' probably already has the word 2785f757f3fSDimitry Andric // "Private" in it in practice) 2795f757f3fSDimitry Andric tryAPINotes(*M->Directory, /*wantPublic=*/true); 2805f757f3fSDimitry Andric if (!M->ModuleMapIsPrivate && hasPrivateSubmodules(M)) 2815f757f3fSDimitry Andric tryAPINotes(*M->Directory, /*wantPublic=*/false); 2825f757f3fSDimitry Andric } 2835f757f3fSDimitry Andric 2845f757f3fSDimitry Andric if (!APINotes.empty()) 2855f757f3fSDimitry Andric return APINotes; 2865f757f3fSDimitry Andric } 2875f757f3fSDimitry Andric 2885f757f3fSDimitry Andric // Second, look for API notes for this module in the module API 2895f757f3fSDimitry Andric // notes search paths. 2905f757f3fSDimitry Andric for (const auto &SearchPath : SearchPaths) { 2915f757f3fSDimitry Andric if (auto SearchDir = FM.getOptionalDirectoryRef(SearchPath)) { 2925f757f3fSDimitry Andric if (auto File = findAPINotesFile(*SearchDir, ModuleName)) { 2935f757f3fSDimitry Andric APINotes.push_back(*File); 2945f757f3fSDimitry Andric return APINotes; 2955f757f3fSDimitry Andric } 2965f757f3fSDimitry Andric } 2975f757f3fSDimitry Andric } 2985f757f3fSDimitry Andric 2995f757f3fSDimitry Andric // Didn't find any API notes. 3005f757f3fSDimitry Andric return APINotes; 3015f757f3fSDimitry Andric } 3025f757f3fSDimitry Andric 3035f757f3fSDimitry Andric bool APINotesManager::loadCurrentModuleAPINotes( 3045f757f3fSDimitry Andric Module *M, bool LookInModule, ArrayRef<std::string> SearchPaths) { 3055f757f3fSDimitry Andric assert(!CurrentModuleReaders[ReaderKind::Public] && 3065f757f3fSDimitry Andric "Already loaded API notes for the current module?"); 3075f757f3fSDimitry Andric 3085f757f3fSDimitry Andric auto APINotes = getCurrentModuleAPINotes(M, LookInModule, SearchPaths); 3095f757f3fSDimitry Andric unsigned NumReaders = 0; 3105f757f3fSDimitry Andric for (auto File : APINotes) { 3115f757f3fSDimitry Andric CurrentModuleReaders[NumReaders++] = loadAPINotes(File).release(); 3125f757f3fSDimitry Andric if (!getCurrentModuleReaders().empty()) 3135f757f3fSDimitry Andric M->APINotesFile = File.getName().str(); 3145f757f3fSDimitry Andric } 3155f757f3fSDimitry Andric 3165f757f3fSDimitry Andric return NumReaders > 0; 3175f757f3fSDimitry Andric } 3185f757f3fSDimitry Andric 3195f757f3fSDimitry Andric bool APINotesManager::loadCurrentModuleAPINotesFromBuffer( 3205f757f3fSDimitry Andric ArrayRef<StringRef> Buffers) { 3215f757f3fSDimitry Andric unsigned NumReader = 0; 3225f757f3fSDimitry Andric for (auto Buf : Buffers) { 3235f757f3fSDimitry Andric auto Reader = loadAPINotes(Buf); 3245f757f3fSDimitry Andric assert(Reader && "Could not load the API notes we just generated?"); 3255f757f3fSDimitry Andric 3265f757f3fSDimitry Andric CurrentModuleReaders[NumReader++] = Reader.release(); 3275f757f3fSDimitry Andric } 3285f757f3fSDimitry Andric return NumReader; 3295f757f3fSDimitry Andric } 3305f757f3fSDimitry Andric 3315f757f3fSDimitry Andric llvm::SmallVector<APINotesReader *, 2> 3325f757f3fSDimitry Andric APINotesManager::findAPINotes(SourceLocation Loc) { 3335f757f3fSDimitry Andric llvm::SmallVector<APINotesReader *, 2> Results; 3345f757f3fSDimitry Andric 3355f757f3fSDimitry Andric // If there are readers for the current module, return them. 3365f757f3fSDimitry Andric if (!getCurrentModuleReaders().empty()) { 3375f757f3fSDimitry Andric Results.append(getCurrentModuleReaders().begin(), 3385f757f3fSDimitry Andric getCurrentModuleReaders().end()); 3395f757f3fSDimitry Andric return Results; 3405f757f3fSDimitry Andric } 3415f757f3fSDimitry Andric 3425f757f3fSDimitry Andric // If we're not allowed to implicitly load API notes files, we're done. 3435f757f3fSDimitry Andric if (!ImplicitAPINotes) 3445f757f3fSDimitry Andric return Results; 3455f757f3fSDimitry Andric 3465f757f3fSDimitry Andric // If we don't have source location information, we're done. 3475f757f3fSDimitry Andric if (Loc.isInvalid()) 3485f757f3fSDimitry Andric return Results; 3495f757f3fSDimitry Andric 3505f757f3fSDimitry Andric // API notes are associated with the expansion location. Retrieve the 3515f757f3fSDimitry Andric // file for this location. 3525f757f3fSDimitry Andric SourceLocation ExpansionLoc = SM.getExpansionLoc(Loc); 3535f757f3fSDimitry Andric FileID ID = SM.getFileID(ExpansionLoc); 3545f757f3fSDimitry Andric if (ID.isInvalid()) 3555f757f3fSDimitry Andric return Results; 3565f757f3fSDimitry Andric OptionalFileEntryRef File = SM.getFileEntryRefForID(ID); 3575f757f3fSDimitry Andric if (!File) 3585f757f3fSDimitry Andric return Results; 3595f757f3fSDimitry Andric 3605f757f3fSDimitry Andric // Look for API notes in the directory corresponding to this file, or one of 3615f757f3fSDimitry Andric // its its parent directories. 3625f757f3fSDimitry Andric OptionalDirectoryEntryRef Dir = File->getDir(); 3635f757f3fSDimitry Andric FileManager &FileMgr = SM.getFileManager(); 3645f757f3fSDimitry Andric llvm::SetVector<const DirectoryEntry *, 3655f757f3fSDimitry Andric SmallVector<const DirectoryEntry *, 4>, 3665f757f3fSDimitry Andric llvm::SmallPtrSet<const DirectoryEntry *, 4>> 3675f757f3fSDimitry Andric DirsVisited; 3685f757f3fSDimitry Andric do { 3695f757f3fSDimitry Andric // Look for an API notes reader for this header search directory. 3705f757f3fSDimitry Andric auto Known = Readers.find(*Dir); 3715f757f3fSDimitry Andric 3725f757f3fSDimitry Andric // If we already know the answer, chase it. 3735f757f3fSDimitry Andric if (Known != Readers.end()) { 3745f757f3fSDimitry Andric ++NumDirectoryCacheHits; 3755f757f3fSDimitry Andric 3765f757f3fSDimitry Andric // We've been redirected to another directory for answers. Follow it. 3775f757f3fSDimitry Andric if (Known->second && Known->second.is<DirectoryEntryRef>()) { 3785f757f3fSDimitry Andric DirsVisited.insert(*Dir); 3795f757f3fSDimitry Andric Dir = Known->second.get<DirectoryEntryRef>(); 3805f757f3fSDimitry Andric continue; 3815f757f3fSDimitry Andric } 3825f757f3fSDimitry Andric 3835f757f3fSDimitry Andric // We have the answer. 3845f757f3fSDimitry Andric if (auto Reader = Known->second.dyn_cast<APINotesReader *>()) 3855f757f3fSDimitry Andric Results.push_back(Reader); 3865f757f3fSDimitry Andric break; 3875f757f3fSDimitry Andric } 3885f757f3fSDimitry Andric 3895f757f3fSDimitry Andric // Look for API notes corresponding to this directory. 3905f757f3fSDimitry Andric StringRef Path = Dir->getName(); 3915f757f3fSDimitry Andric if (llvm::sys::path::extension(Path) == ".framework") { 3925f757f3fSDimitry Andric // If this is a framework directory, check whether there are API notes 3935f757f3fSDimitry Andric // in the APINotes subdirectory. 3945f757f3fSDimitry Andric auto FrameworkName = llvm::sys::path::stem(Path); 3955f757f3fSDimitry Andric ++NumFrameworksSearched; 3965f757f3fSDimitry Andric 3975f757f3fSDimitry Andric // Look for API notes for both the public and private headers. 3985f757f3fSDimitry Andric OptionalDirectoryEntryRef PublicDir = 3995f757f3fSDimitry Andric loadFrameworkAPINotes(Path, FrameworkName, /*Public=*/true); 4005f757f3fSDimitry Andric OptionalDirectoryEntryRef PrivateDir = 4015f757f3fSDimitry Andric loadFrameworkAPINotes(Path, FrameworkName, /*Public=*/false); 4025f757f3fSDimitry Andric 4035f757f3fSDimitry Andric if (PublicDir || PrivateDir) { 4045f757f3fSDimitry Andric // We found API notes: don't ever look past the framework directory. 4055f757f3fSDimitry Andric Readers[*Dir] = nullptr; 4065f757f3fSDimitry Andric 4075f757f3fSDimitry Andric // Pretend we found the result in the public or private directory, 4085f757f3fSDimitry Andric // as appropriate. All headers should be in one of those two places, 4095f757f3fSDimitry Andric // but be defensive here. 4105f757f3fSDimitry Andric if (!DirsVisited.empty()) { 4115f757f3fSDimitry Andric if (PublicDir && DirsVisited.back() == *PublicDir) { 4125f757f3fSDimitry Andric DirsVisited.pop_back(); 4135f757f3fSDimitry Andric Dir = *PublicDir; 4145f757f3fSDimitry Andric } else if (PrivateDir && DirsVisited.back() == *PrivateDir) { 4155f757f3fSDimitry Andric DirsVisited.pop_back(); 4165f757f3fSDimitry Andric Dir = *PrivateDir; 4175f757f3fSDimitry Andric } 4185f757f3fSDimitry Andric } 4195f757f3fSDimitry Andric 4205f757f3fSDimitry Andric // Grab the result. 4215f757f3fSDimitry Andric if (auto Reader = Readers[*Dir].dyn_cast<APINotesReader *>()) 4225f757f3fSDimitry Andric Results.push_back(Reader); 4235f757f3fSDimitry Andric break; 4245f757f3fSDimitry Andric } 4255f757f3fSDimitry Andric } else { 4265f757f3fSDimitry Andric // Look for an APINotes file in this directory. 4275f757f3fSDimitry Andric llvm::SmallString<128> APINotesPath(Dir->getName()); 4285f757f3fSDimitry Andric llvm::sys::path::append( 4295f757f3fSDimitry Andric APINotesPath, (llvm::Twine("APINotes.") + SOURCE_APINOTES_EXTENSION)); 4305f757f3fSDimitry Andric 4315f757f3fSDimitry Andric // If there is an API notes file here, try to load it. 4325f757f3fSDimitry Andric ++NumDirectoriesSearched; 4335f757f3fSDimitry Andric if (auto APINotesFile = FileMgr.getOptionalFileRef(APINotesPath)) { 4345f757f3fSDimitry Andric if (!loadAPINotes(*Dir, *APINotesFile)) { 4355f757f3fSDimitry Andric ++NumHeaderAPINotes; 4365f757f3fSDimitry Andric if (auto Reader = Readers[*Dir].dyn_cast<APINotesReader *>()) 4375f757f3fSDimitry Andric Results.push_back(Reader); 4385f757f3fSDimitry Andric break; 4395f757f3fSDimitry Andric } 4405f757f3fSDimitry Andric } 4415f757f3fSDimitry Andric } 4425f757f3fSDimitry Andric 4435f757f3fSDimitry Andric // We didn't find anything. Look at the parent directory. 4445f757f3fSDimitry Andric if (!DirsVisited.insert(*Dir)) { 4455f757f3fSDimitry Andric Dir = std::nullopt; 4465f757f3fSDimitry Andric break; 4475f757f3fSDimitry Andric } 4485f757f3fSDimitry Andric 4495f757f3fSDimitry Andric StringRef ParentPath = llvm::sys::path::parent_path(Path); 4505f757f3fSDimitry Andric while (llvm::sys::path::stem(ParentPath) == "..") 4515f757f3fSDimitry Andric ParentPath = llvm::sys::path::parent_path(ParentPath); 4525f757f3fSDimitry Andric 4535f757f3fSDimitry Andric Dir = ParentPath.empty() ? std::nullopt 4545f757f3fSDimitry Andric : FileMgr.getOptionalDirectoryRef(ParentPath); 4555f757f3fSDimitry Andric } while (Dir); 4565f757f3fSDimitry Andric 4575f757f3fSDimitry Andric // Path compression for all of the directories we visited, redirecting 4585f757f3fSDimitry Andric // them to the directory we ended on. If no API notes were found, the 4595f757f3fSDimitry Andric // resulting directory will be NULL, indicating no API notes. 4605f757f3fSDimitry Andric for (const auto Visited : DirsVisited) 4615f757f3fSDimitry Andric Readers[Visited] = Dir ? ReaderEntry(*Dir) : ReaderEntry(); 4625f757f3fSDimitry Andric 4635f757f3fSDimitry Andric return Results; 4645f757f3fSDimitry Andric } 465