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