1f049395fSEgor Zhdan //===--- APINotesManager.cpp - Manage API Notes Files ---------------------===// 2f049395fSEgor Zhdan // 3f049395fSEgor Zhdan // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4f049395fSEgor Zhdan // See https://llvm.org/LICENSE.txt for license information. 5f049395fSEgor Zhdan // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6f049395fSEgor Zhdan // 7f049395fSEgor Zhdan //===----------------------------------------------------------------------===// 8f049395fSEgor Zhdan 9f049395fSEgor Zhdan #include "clang/APINotes/APINotesManager.h" 10f049395fSEgor Zhdan #include "clang/APINotes/APINotesReader.h" 11f049395fSEgor Zhdan #include "clang/APINotes/APINotesYAMLCompiler.h" 12f049395fSEgor Zhdan #include "clang/Basic/Diagnostic.h" 13f049395fSEgor Zhdan #include "clang/Basic/FileManager.h" 14f049395fSEgor Zhdan #include "clang/Basic/LangOptions.h" 15aaa4ff88SDavid Stone #include "clang/Basic/Module.h" 16f049395fSEgor Zhdan #include "clang/Basic/SourceManager.h" 17f049395fSEgor Zhdan #include "clang/Basic/SourceMgrAdapter.h" 18f049395fSEgor Zhdan #include "clang/Basic/Version.h" 19f049395fSEgor Zhdan #include "llvm/ADT/APInt.h" 20f049395fSEgor Zhdan #include "llvm/ADT/Hashing.h" 21f049395fSEgor Zhdan #include "llvm/ADT/SetVector.h" 22f049395fSEgor Zhdan #include "llvm/ADT/SmallPtrSet.h" 23f049395fSEgor Zhdan #include "llvm/ADT/SmallVector.h" 24f049395fSEgor Zhdan #include "llvm/ADT/Statistic.h" 25f049395fSEgor Zhdan #include "llvm/Support/MemoryBuffer.h" 26f049395fSEgor Zhdan #include "llvm/Support/Path.h" 27f049395fSEgor Zhdan #include "llvm/Support/PrettyStackTrace.h" 28f049395fSEgor Zhdan 29f049395fSEgor Zhdan using namespace clang; 30f049395fSEgor Zhdan using namespace api_notes; 31f049395fSEgor Zhdan 32f049395fSEgor Zhdan #define DEBUG_TYPE "API Notes" 33f049395fSEgor Zhdan STATISTIC(NumHeaderAPINotes, "non-framework API notes files loaded"); 34f049395fSEgor Zhdan STATISTIC(NumPublicFrameworkAPINotes, "framework public API notes loaded"); 35f049395fSEgor Zhdan STATISTIC(NumPrivateFrameworkAPINotes, "framework private API notes loaded"); 36f049395fSEgor Zhdan STATISTIC(NumFrameworksSearched, "frameworks searched"); 37f049395fSEgor Zhdan STATISTIC(NumDirectoriesSearched, "header directories searched"); 38f049395fSEgor Zhdan STATISTIC(NumDirectoryCacheHits, "directory cache hits"); 39f049395fSEgor Zhdan 40f049395fSEgor Zhdan namespace { 41f049395fSEgor Zhdan /// Prints two successive strings, which much be kept alive as long as the 42f049395fSEgor Zhdan /// PrettyStackTrace entry. 43f049395fSEgor Zhdan class PrettyStackTraceDoubleString : public llvm::PrettyStackTraceEntry { 44f049395fSEgor Zhdan StringRef First, Second; 45f049395fSEgor Zhdan 46f049395fSEgor Zhdan public: 47f049395fSEgor Zhdan PrettyStackTraceDoubleString(StringRef First, StringRef Second) 48f049395fSEgor Zhdan : First(First), Second(Second) {} 49f049395fSEgor Zhdan void print(raw_ostream &OS) const override { OS << First << Second; } 50f049395fSEgor Zhdan }; 51f049395fSEgor Zhdan } // namespace 52f049395fSEgor Zhdan 53f049395fSEgor Zhdan APINotesManager::APINotesManager(SourceManager &SM, const LangOptions &LangOpts) 54f049395fSEgor Zhdan : SM(SM), ImplicitAPINotes(LangOpts.APINotes) {} 55f049395fSEgor Zhdan 56f049395fSEgor Zhdan APINotesManager::~APINotesManager() { 57f049395fSEgor Zhdan // Free the API notes readers. 58f049395fSEgor Zhdan for (const auto &Entry : Readers) { 59*563c7c55SKazu Hirata if (auto Reader = dyn_cast_if_present<APINotesReader *>(Entry.second)) 60f049395fSEgor Zhdan delete Reader; 61f049395fSEgor Zhdan } 62f049395fSEgor Zhdan 63f049395fSEgor Zhdan delete CurrentModuleReaders[ReaderKind::Public]; 64f049395fSEgor Zhdan delete CurrentModuleReaders[ReaderKind::Private]; 65f049395fSEgor Zhdan } 66f049395fSEgor Zhdan 67f049395fSEgor Zhdan std::unique_ptr<APINotesReader> 68f049395fSEgor Zhdan APINotesManager::loadAPINotes(FileEntryRef APINotesFile) { 69f049395fSEgor Zhdan PrettyStackTraceDoubleString Trace("Loading API notes from ", 70f049395fSEgor Zhdan APINotesFile.getName()); 71f049395fSEgor Zhdan 72f049395fSEgor Zhdan // Open the source file. 73f049395fSEgor Zhdan auto SourceFileID = SM.getOrCreateFileID(APINotesFile, SrcMgr::C_User); 74f049395fSEgor Zhdan auto SourceBuffer = SM.getBufferOrNone(SourceFileID, SourceLocation()); 75f049395fSEgor Zhdan if (!SourceBuffer) 76f049395fSEgor Zhdan return nullptr; 77f049395fSEgor Zhdan 78f049395fSEgor Zhdan // Compile the API notes source into a buffer. 79f049395fSEgor Zhdan // FIXME: Either propagate OSType through or, better yet, improve the binary 80f049395fSEgor Zhdan // APINotes format to maintain complete availability information. 81f049395fSEgor Zhdan // FIXME: We don't even really need to go through the binary format at all; 82f049395fSEgor Zhdan // we're just going to immediately deserialize it again. 83f049395fSEgor Zhdan llvm::SmallVector<char, 1024> APINotesBuffer; 84f049395fSEgor Zhdan std::unique_ptr<llvm::MemoryBuffer> CompiledBuffer; 85f049395fSEgor Zhdan { 86f049395fSEgor Zhdan SourceMgrAdapter SMAdapter( 87f049395fSEgor Zhdan SM, SM.getDiagnostics(), diag::err_apinotes_message, 88f049395fSEgor Zhdan diag::warn_apinotes_message, diag::note_apinotes_message, APINotesFile); 89f049395fSEgor Zhdan llvm::raw_svector_ostream OS(APINotesBuffer); 90f049395fSEgor Zhdan if (api_notes::compileAPINotes( 91f049395fSEgor Zhdan SourceBuffer->getBuffer(), SM.getFileEntryForID(SourceFileID), OS, 92f049395fSEgor Zhdan SMAdapter.getDiagHandler(), SMAdapter.getDiagContext())) 93f049395fSEgor Zhdan return nullptr; 94f049395fSEgor Zhdan 95f049395fSEgor Zhdan // Make a copy of the compiled form into the buffer. 96f049395fSEgor Zhdan CompiledBuffer = llvm::MemoryBuffer::getMemBufferCopy( 97f049395fSEgor Zhdan StringRef(APINotesBuffer.data(), APINotesBuffer.size())); 98f049395fSEgor Zhdan } 99f049395fSEgor Zhdan 100f049395fSEgor Zhdan // Load the binary form we just compiled. 101f049395fSEgor Zhdan auto Reader = APINotesReader::Create(std::move(CompiledBuffer), SwiftVersion); 102f049395fSEgor Zhdan assert(Reader && "Could not load the API notes we just generated?"); 103f049395fSEgor Zhdan return Reader; 104f049395fSEgor Zhdan } 105f049395fSEgor Zhdan 106f049395fSEgor Zhdan std::unique_ptr<APINotesReader> 107f049395fSEgor Zhdan APINotesManager::loadAPINotes(StringRef Buffer) { 108f049395fSEgor Zhdan llvm::SmallVector<char, 1024> APINotesBuffer; 109f049395fSEgor Zhdan std::unique_ptr<llvm::MemoryBuffer> CompiledBuffer; 110f049395fSEgor Zhdan SourceMgrAdapter SMAdapter( 111f049395fSEgor Zhdan SM, SM.getDiagnostics(), diag::err_apinotes_message, 112f049395fSEgor Zhdan diag::warn_apinotes_message, diag::note_apinotes_message, std::nullopt); 113f049395fSEgor Zhdan llvm::raw_svector_ostream OS(APINotesBuffer); 114f049395fSEgor Zhdan 115f049395fSEgor Zhdan if (api_notes::compileAPINotes(Buffer, nullptr, OS, 116f049395fSEgor Zhdan SMAdapter.getDiagHandler(), 117f049395fSEgor Zhdan SMAdapter.getDiagContext())) 118f049395fSEgor Zhdan return nullptr; 119f049395fSEgor Zhdan 120f049395fSEgor Zhdan CompiledBuffer = llvm::MemoryBuffer::getMemBufferCopy( 121f049395fSEgor Zhdan StringRef(APINotesBuffer.data(), APINotesBuffer.size())); 122f049395fSEgor Zhdan auto Reader = APINotesReader::Create(std::move(CompiledBuffer), SwiftVersion); 123f049395fSEgor Zhdan assert(Reader && "Could not load the API notes we just generated?"); 124f049395fSEgor Zhdan return Reader; 125f049395fSEgor Zhdan } 126f049395fSEgor Zhdan 127f049395fSEgor Zhdan bool APINotesManager::loadAPINotes(const DirectoryEntry *HeaderDir, 128f049395fSEgor Zhdan FileEntryRef APINotesFile) { 1290e039fc3SKazu Hirata assert(!Readers.contains(HeaderDir)); 130f049395fSEgor Zhdan if (auto Reader = loadAPINotes(APINotesFile)) { 131f049395fSEgor Zhdan Readers[HeaderDir] = Reader.release(); 132f049395fSEgor Zhdan return false; 133f049395fSEgor Zhdan } 134f049395fSEgor Zhdan 135f049395fSEgor Zhdan Readers[HeaderDir] = nullptr; 136f049395fSEgor Zhdan return true; 137f049395fSEgor Zhdan } 138f049395fSEgor Zhdan 139f049395fSEgor Zhdan OptionalFileEntryRef 140f049395fSEgor Zhdan APINotesManager::findAPINotesFile(DirectoryEntryRef Directory, 141f049395fSEgor Zhdan StringRef Basename, bool WantPublic) { 142f049395fSEgor Zhdan FileManager &FM = SM.getFileManager(); 143f049395fSEgor Zhdan 144f049395fSEgor Zhdan llvm::SmallString<128> Path(Directory.getName()); 145f049395fSEgor Zhdan 146f049395fSEgor Zhdan StringRef Suffix = WantPublic ? "" : "_private"; 147f049395fSEgor Zhdan 148f049395fSEgor Zhdan // Look for the source API notes file. 149f049395fSEgor Zhdan llvm::sys::path::append(Path, llvm::Twine(Basename) + Suffix + "." + 150f049395fSEgor Zhdan SOURCE_APINOTES_EXTENSION); 151f049395fSEgor Zhdan return FM.getOptionalFileRef(Path, /*Open*/ true); 152f049395fSEgor Zhdan } 153f049395fSEgor Zhdan 154f049395fSEgor Zhdan OptionalDirectoryEntryRef APINotesManager::loadFrameworkAPINotes( 155f049395fSEgor Zhdan llvm::StringRef FrameworkPath, llvm::StringRef FrameworkName, bool Public) { 156f049395fSEgor Zhdan FileManager &FM = SM.getFileManager(); 157f049395fSEgor Zhdan 158f049395fSEgor Zhdan llvm::SmallString<128> Path(FrameworkPath); 159f049395fSEgor Zhdan unsigned FrameworkNameLength = Path.size(); 160f049395fSEgor Zhdan 161f049395fSEgor Zhdan StringRef Suffix = Public ? "" : "_private"; 162f049395fSEgor Zhdan 163f049395fSEgor Zhdan // Form the path to the APINotes file. 164f049395fSEgor Zhdan llvm::sys::path::append(Path, "APINotes"); 165f049395fSEgor Zhdan llvm::sys::path::append(Path, (llvm::Twine(FrameworkName) + Suffix + "." + 166f049395fSEgor Zhdan SOURCE_APINOTES_EXTENSION)); 167f049395fSEgor Zhdan 168f049395fSEgor Zhdan // Try to open the APINotes file. 169f049395fSEgor Zhdan auto APINotesFile = FM.getOptionalFileRef(Path); 170f049395fSEgor Zhdan if (!APINotesFile) 171f049395fSEgor Zhdan return std::nullopt; 172f049395fSEgor Zhdan 173f049395fSEgor Zhdan // Form the path to the corresponding header directory. 174f049395fSEgor Zhdan Path.resize(FrameworkNameLength); 175f049395fSEgor Zhdan llvm::sys::path::append(Path, Public ? "Headers" : "PrivateHeaders"); 176f049395fSEgor Zhdan 177f049395fSEgor Zhdan // Try to access the header directory. 178f049395fSEgor Zhdan auto HeaderDir = FM.getOptionalDirectoryRef(Path); 179f049395fSEgor Zhdan if (!HeaderDir) 180f049395fSEgor Zhdan return std::nullopt; 181f049395fSEgor Zhdan 182f049395fSEgor Zhdan // Try to load the API notes. 183f049395fSEgor Zhdan if (loadAPINotes(*HeaderDir, *APINotesFile)) 184f049395fSEgor Zhdan return std::nullopt; 185f049395fSEgor Zhdan 186f049395fSEgor Zhdan // Success: return the header directory. 187f049395fSEgor Zhdan if (Public) 188f049395fSEgor Zhdan ++NumPublicFrameworkAPINotes; 189f049395fSEgor Zhdan else 190f049395fSEgor Zhdan ++NumPrivateFrameworkAPINotes; 191f049395fSEgor Zhdan return *HeaderDir; 192f049395fSEgor Zhdan } 193f049395fSEgor Zhdan 194f049395fSEgor Zhdan static void checkPrivateAPINotesName(DiagnosticsEngine &Diags, 195f049395fSEgor Zhdan const FileEntry *File, const Module *M) { 196f049395fSEgor Zhdan if (File->tryGetRealPathName().empty()) 197f049395fSEgor Zhdan return; 198f049395fSEgor Zhdan 199f049395fSEgor Zhdan StringRef RealFileName = 200f049395fSEgor Zhdan llvm::sys::path::filename(File->tryGetRealPathName()); 201f049395fSEgor Zhdan StringRef RealStem = llvm::sys::path::stem(RealFileName); 202f3dcc235SKazu Hirata if (RealStem.ends_with("_private")) 203f049395fSEgor Zhdan return; 204f049395fSEgor Zhdan 205f049395fSEgor Zhdan unsigned DiagID = diag::warn_apinotes_private_case; 206f049395fSEgor Zhdan if (M->IsSystem) 207f049395fSEgor Zhdan DiagID = diag::warn_apinotes_private_case_system; 208f049395fSEgor Zhdan 209f049395fSEgor Zhdan Diags.Report(SourceLocation(), DiagID) << M->Name << RealFileName; 210f049395fSEgor Zhdan } 211f049395fSEgor Zhdan 212f049395fSEgor Zhdan /// \returns true if any of \p module's immediate submodules are defined in a 213f049395fSEgor Zhdan /// private module map 214f049395fSEgor Zhdan static bool hasPrivateSubmodules(const Module *M) { 215f049395fSEgor Zhdan return llvm::any_of(M->submodules(), [](const Module *Submodule) { 216f049395fSEgor Zhdan return Submodule->ModuleMapIsPrivate; 217f049395fSEgor Zhdan }); 218f049395fSEgor Zhdan } 219f049395fSEgor Zhdan 220f049395fSEgor Zhdan llvm::SmallVector<FileEntryRef, 2> 221f049395fSEgor Zhdan APINotesManager::getCurrentModuleAPINotes(Module *M, bool LookInModule, 222f049395fSEgor Zhdan ArrayRef<std::string> SearchPaths) { 223f049395fSEgor Zhdan FileManager &FM = SM.getFileManager(); 224f049395fSEgor Zhdan auto ModuleName = M->getTopLevelModuleName(); 22596c8e2e8SEgor Zhdan auto ExportedModuleName = M->getTopLevelModule()->ExportAsModule; 226f049395fSEgor Zhdan llvm::SmallVector<FileEntryRef, 2> APINotes; 227f049395fSEgor Zhdan 228f049395fSEgor Zhdan // First, look relative to the module itself. 229878097dfSEgor Zhdan if (LookInModule && M->Directory) { 230f049395fSEgor Zhdan // Local function to try loading an API notes file in the given directory. 231f049395fSEgor Zhdan auto tryAPINotes = [&](DirectoryEntryRef Dir, bool WantPublic) { 232f049395fSEgor Zhdan if (auto File = findAPINotesFile(Dir, ModuleName, WantPublic)) { 233f049395fSEgor Zhdan if (!WantPublic) 234f049395fSEgor Zhdan checkPrivateAPINotesName(SM.getDiagnostics(), *File, M); 235f049395fSEgor Zhdan 236f049395fSEgor Zhdan APINotes.push_back(*File); 237f049395fSEgor Zhdan } 23896c8e2e8SEgor Zhdan // If module FooCore is re-exported through module Foo, try Foo.apinotes. 23996c8e2e8SEgor Zhdan if (!ExportedModuleName.empty()) 24096c8e2e8SEgor Zhdan if (auto File = findAPINotesFile(Dir, ExportedModuleName, WantPublic)) 24196c8e2e8SEgor Zhdan APINotes.push_back(*File); 242f049395fSEgor Zhdan }; 243f049395fSEgor Zhdan 244f049395fSEgor Zhdan if (M->IsFramework) { 245f049395fSEgor Zhdan // For frameworks, we search in the "Headers" or "PrivateHeaders" 246f049395fSEgor Zhdan // subdirectory. 247f049395fSEgor Zhdan // 248f049395fSEgor Zhdan // Public modules: 249f049395fSEgor Zhdan // - Headers/Foo.apinotes 250f049395fSEgor Zhdan // - PrivateHeaders/Foo_private.apinotes (if there are private submodules) 251f049395fSEgor Zhdan // Private modules: 252f049395fSEgor Zhdan // - PrivateHeaders/Bar.apinotes (except that 'Bar' probably already has 253f049395fSEgor Zhdan // the word "Private" in it in practice) 254f049395fSEgor Zhdan llvm::SmallString<128> Path(M->Directory->getName()); 255f049395fSEgor Zhdan 256f049395fSEgor Zhdan if (!M->ModuleMapIsPrivate) { 257f049395fSEgor Zhdan unsigned PathLen = Path.size(); 258f049395fSEgor Zhdan 259f049395fSEgor Zhdan llvm::sys::path::append(Path, "Headers"); 260f049395fSEgor Zhdan if (auto APINotesDir = FM.getOptionalDirectoryRef(Path)) 261f049395fSEgor Zhdan tryAPINotes(*APINotesDir, /*wantPublic=*/true); 262f049395fSEgor Zhdan 263f049395fSEgor Zhdan Path.resize(PathLen); 264f049395fSEgor Zhdan } 265f049395fSEgor Zhdan 266f049395fSEgor Zhdan if (M->ModuleMapIsPrivate || hasPrivateSubmodules(M)) { 267f049395fSEgor Zhdan llvm::sys::path::append(Path, "PrivateHeaders"); 268f049395fSEgor Zhdan if (auto PrivateAPINotesDir = FM.getOptionalDirectoryRef(Path)) 269f049395fSEgor Zhdan tryAPINotes(*PrivateAPINotesDir, 270f049395fSEgor Zhdan /*wantPublic=*/M->ModuleMapIsPrivate); 271f049395fSEgor Zhdan } 272f049395fSEgor Zhdan } else { 273f049395fSEgor Zhdan // Public modules: 274f049395fSEgor Zhdan // - Foo.apinotes 275f049395fSEgor Zhdan // - Foo_private.apinotes (if there are private submodules) 276f049395fSEgor Zhdan // Private modules: 277f049395fSEgor Zhdan // - Bar.apinotes (except that 'Bar' probably already has the word 278f049395fSEgor Zhdan // "Private" in it in practice) 279f049395fSEgor Zhdan tryAPINotes(*M->Directory, /*wantPublic=*/true); 280f049395fSEgor Zhdan if (!M->ModuleMapIsPrivate && hasPrivateSubmodules(M)) 281f049395fSEgor Zhdan tryAPINotes(*M->Directory, /*wantPublic=*/false); 282f049395fSEgor Zhdan } 283f049395fSEgor Zhdan 284f049395fSEgor Zhdan if (!APINotes.empty()) 285f049395fSEgor Zhdan return APINotes; 286f049395fSEgor Zhdan } 287f049395fSEgor Zhdan 288f049395fSEgor Zhdan // Second, look for API notes for this module in the module API 289f049395fSEgor Zhdan // notes search paths. 290f049395fSEgor Zhdan for (const auto &SearchPath : SearchPaths) { 291f049395fSEgor Zhdan if (auto SearchDir = FM.getOptionalDirectoryRef(SearchPath)) { 292f049395fSEgor Zhdan if (auto File = findAPINotesFile(*SearchDir, ModuleName)) { 293f049395fSEgor Zhdan APINotes.push_back(*File); 294f049395fSEgor Zhdan return APINotes; 295f049395fSEgor Zhdan } 296f049395fSEgor Zhdan } 297f049395fSEgor Zhdan } 298f049395fSEgor Zhdan 299f049395fSEgor Zhdan // Didn't find any API notes. 300f049395fSEgor Zhdan return APINotes; 301f049395fSEgor Zhdan } 302f049395fSEgor Zhdan 303f049395fSEgor Zhdan bool APINotesManager::loadCurrentModuleAPINotes( 304f049395fSEgor Zhdan Module *M, bool LookInModule, ArrayRef<std::string> SearchPaths) { 305f049395fSEgor Zhdan assert(!CurrentModuleReaders[ReaderKind::Public] && 306f049395fSEgor Zhdan "Already loaded API notes for the current module?"); 307f049395fSEgor Zhdan 308f049395fSEgor Zhdan auto APINotes = getCurrentModuleAPINotes(M, LookInModule, SearchPaths); 309f049395fSEgor Zhdan unsigned NumReaders = 0; 310f049395fSEgor Zhdan for (auto File : APINotes) { 311f049395fSEgor Zhdan CurrentModuleReaders[NumReaders++] = loadAPINotes(File).release(); 312f049395fSEgor Zhdan if (!getCurrentModuleReaders().empty()) 313f049395fSEgor Zhdan M->APINotesFile = File.getName().str(); 314f049395fSEgor Zhdan } 315f049395fSEgor Zhdan 316f049395fSEgor Zhdan return NumReaders > 0; 317f049395fSEgor Zhdan } 318f049395fSEgor Zhdan 319f049395fSEgor Zhdan bool APINotesManager::loadCurrentModuleAPINotesFromBuffer( 320f049395fSEgor Zhdan ArrayRef<StringRef> Buffers) { 321f049395fSEgor Zhdan unsigned NumReader = 0; 322f049395fSEgor Zhdan for (auto Buf : Buffers) { 323f049395fSEgor Zhdan auto Reader = loadAPINotes(Buf); 324f049395fSEgor Zhdan assert(Reader && "Could not load the API notes we just generated?"); 325f049395fSEgor Zhdan 326f049395fSEgor Zhdan CurrentModuleReaders[NumReader++] = Reader.release(); 327f049395fSEgor Zhdan } 328f049395fSEgor Zhdan return NumReader; 329f049395fSEgor Zhdan } 330f049395fSEgor Zhdan 331f049395fSEgor Zhdan llvm::SmallVector<APINotesReader *, 2> 332f049395fSEgor Zhdan APINotesManager::findAPINotes(SourceLocation Loc) { 333f049395fSEgor Zhdan llvm::SmallVector<APINotesReader *, 2> Results; 334f049395fSEgor Zhdan 335f049395fSEgor Zhdan // If there are readers for the current module, return them. 336f049395fSEgor Zhdan if (!getCurrentModuleReaders().empty()) { 337f049395fSEgor Zhdan Results.append(getCurrentModuleReaders().begin(), 338f049395fSEgor Zhdan getCurrentModuleReaders().end()); 339f049395fSEgor Zhdan return Results; 340f049395fSEgor Zhdan } 341f049395fSEgor Zhdan 342f049395fSEgor Zhdan // If we're not allowed to implicitly load API notes files, we're done. 343f049395fSEgor Zhdan if (!ImplicitAPINotes) 344f049395fSEgor Zhdan return Results; 345f049395fSEgor Zhdan 346f049395fSEgor Zhdan // If we don't have source location information, we're done. 347f049395fSEgor Zhdan if (Loc.isInvalid()) 348f049395fSEgor Zhdan return Results; 349f049395fSEgor Zhdan 350f049395fSEgor Zhdan // API notes are associated with the expansion location. Retrieve the 351f049395fSEgor Zhdan // file for this location. 352f049395fSEgor Zhdan SourceLocation ExpansionLoc = SM.getExpansionLoc(Loc); 353f049395fSEgor Zhdan FileID ID = SM.getFileID(ExpansionLoc); 354f049395fSEgor Zhdan if (ID.isInvalid()) 355f049395fSEgor Zhdan return Results; 356f049395fSEgor Zhdan OptionalFileEntryRef File = SM.getFileEntryRefForID(ID); 357f049395fSEgor Zhdan if (!File) 358f049395fSEgor Zhdan return Results; 359f049395fSEgor Zhdan 360f049395fSEgor Zhdan // Look for API notes in the directory corresponding to this file, or one of 361f049395fSEgor Zhdan // its its parent directories. 362f049395fSEgor Zhdan OptionalDirectoryEntryRef Dir = File->getDir(); 363f049395fSEgor Zhdan FileManager &FileMgr = SM.getFileManager(); 364f049395fSEgor Zhdan llvm::SetVector<const DirectoryEntry *, 365f049395fSEgor Zhdan SmallVector<const DirectoryEntry *, 4>, 366f049395fSEgor Zhdan llvm::SmallPtrSet<const DirectoryEntry *, 4>> 367f049395fSEgor Zhdan DirsVisited; 368f049395fSEgor Zhdan do { 369f049395fSEgor Zhdan // Look for an API notes reader for this header search directory. 370f049395fSEgor Zhdan auto Known = Readers.find(*Dir); 371f049395fSEgor Zhdan 372f049395fSEgor Zhdan // If we already know the answer, chase it. 373f049395fSEgor Zhdan if (Known != Readers.end()) { 374f049395fSEgor Zhdan ++NumDirectoryCacheHits; 375f049395fSEgor Zhdan 376f049395fSEgor Zhdan // We've been redirected to another directory for answers. Follow it. 377d01c11dfSKazu Hirata if (Known->second && isa<DirectoryEntryRef>(Known->second)) { 378f049395fSEgor Zhdan DirsVisited.insert(*Dir); 379d01c11dfSKazu Hirata Dir = cast<DirectoryEntryRef>(Known->second); 380f049395fSEgor Zhdan continue; 381f049395fSEgor Zhdan } 382f049395fSEgor Zhdan 383f049395fSEgor Zhdan // We have the answer. 384*563c7c55SKazu Hirata if (auto Reader = dyn_cast_if_present<APINotesReader *>(Known->second)) 385f049395fSEgor Zhdan Results.push_back(Reader); 386f049395fSEgor Zhdan break; 387f049395fSEgor Zhdan } 388f049395fSEgor Zhdan 389f049395fSEgor Zhdan // Look for API notes corresponding to this directory. 390f049395fSEgor Zhdan StringRef Path = Dir->getName(); 391f049395fSEgor Zhdan if (llvm::sys::path::extension(Path) == ".framework") { 392f049395fSEgor Zhdan // If this is a framework directory, check whether there are API notes 393f049395fSEgor Zhdan // in the APINotes subdirectory. 394f049395fSEgor Zhdan auto FrameworkName = llvm::sys::path::stem(Path); 395f049395fSEgor Zhdan ++NumFrameworksSearched; 396f049395fSEgor Zhdan 397f049395fSEgor Zhdan // Look for API notes for both the public and private headers. 398f049395fSEgor Zhdan OptionalDirectoryEntryRef PublicDir = 399f049395fSEgor Zhdan loadFrameworkAPINotes(Path, FrameworkName, /*Public=*/true); 400f049395fSEgor Zhdan OptionalDirectoryEntryRef PrivateDir = 401f049395fSEgor Zhdan loadFrameworkAPINotes(Path, FrameworkName, /*Public=*/false); 402f049395fSEgor Zhdan 403f049395fSEgor Zhdan if (PublicDir || PrivateDir) { 404f049395fSEgor Zhdan // We found API notes: don't ever look past the framework directory. 405f049395fSEgor Zhdan Readers[*Dir] = nullptr; 406f049395fSEgor Zhdan 407f049395fSEgor Zhdan // Pretend we found the result in the public or private directory, 408f049395fSEgor Zhdan // as appropriate. All headers should be in one of those two places, 409f049395fSEgor Zhdan // but be defensive here. 410f049395fSEgor Zhdan if (!DirsVisited.empty()) { 411f049395fSEgor Zhdan if (PublicDir && DirsVisited.back() == *PublicDir) { 412f049395fSEgor Zhdan DirsVisited.pop_back(); 413f049395fSEgor Zhdan Dir = *PublicDir; 414f049395fSEgor Zhdan } else if (PrivateDir && DirsVisited.back() == *PrivateDir) { 415f049395fSEgor Zhdan DirsVisited.pop_back(); 416f049395fSEgor Zhdan Dir = *PrivateDir; 417f049395fSEgor Zhdan } 418f049395fSEgor Zhdan } 419f049395fSEgor Zhdan 420f049395fSEgor Zhdan // Grab the result. 421f049395fSEgor Zhdan if (auto Reader = Readers[*Dir].dyn_cast<APINotesReader *>()) 422f049395fSEgor Zhdan Results.push_back(Reader); 423f049395fSEgor Zhdan break; 424f049395fSEgor Zhdan } 425f049395fSEgor Zhdan } else { 426f049395fSEgor Zhdan // Look for an APINotes file in this directory. 427f049395fSEgor Zhdan llvm::SmallString<128> APINotesPath(Dir->getName()); 428f049395fSEgor Zhdan llvm::sys::path::append( 429f049395fSEgor Zhdan APINotesPath, (llvm::Twine("APINotes.") + SOURCE_APINOTES_EXTENSION)); 430f049395fSEgor Zhdan 431f049395fSEgor Zhdan // If there is an API notes file here, try to load it. 432f049395fSEgor Zhdan ++NumDirectoriesSearched; 433f049395fSEgor Zhdan if (auto APINotesFile = FileMgr.getOptionalFileRef(APINotesPath)) { 434f049395fSEgor Zhdan if (!loadAPINotes(*Dir, *APINotesFile)) { 435f049395fSEgor Zhdan ++NumHeaderAPINotes; 436f049395fSEgor Zhdan if (auto Reader = Readers[*Dir].dyn_cast<APINotesReader *>()) 437f049395fSEgor Zhdan Results.push_back(Reader); 438f049395fSEgor Zhdan break; 439f049395fSEgor Zhdan } 440f049395fSEgor Zhdan } 441f049395fSEgor Zhdan } 442f049395fSEgor Zhdan 443f049395fSEgor Zhdan // We didn't find anything. Look at the parent directory. 444f049395fSEgor Zhdan if (!DirsVisited.insert(*Dir)) { 445f049395fSEgor Zhdan Dir = std::nullopt; 446f049395fSEgor Zhdan break; 447f049395fSEgor Zhdan } 448f049395fSEgor Zhdan 449f049395fSEgor Zhdan StringRef ParentPath = llvm::sys::path::parent_path(Path); 450f049395fSEgor Zhdan while (llvm::sys::path::stem(ParentPath) == "..") 451f049395fSEgor Zhdan ParentPath = llvm::sys::path::parent_path(ParentPath); 452f049395fSEgor Zhdan 453f049395fSEgor Zhdan Dir = ParentPath.empty() ? std::nullopt 454f049395fSEgor Zhdan : FileMgr.getOptionalDirectoryRef(ParentPath); 455f049395fSEgor Zhdan } while (Dir); 456f049395fSEgor Zhdan 457f049395fSEgor Zhdan // Path compression for all of the directories we visited, redirecting 458f049395fSEgor Zhdan // them to the directory we ended on. If no API notes were found, the 459f049395fSEgor Zhdan // resulting directory will be NULL, indicating no API notes. 460f049395fSEgor Zhdan for (const auto Visited : DirsVisited) 461f049395fSEgor Zhdan Readers[Visited] = Dir ? ReaderEntry(*Dir) : ReaderEntry(); 462f049395fSEgor Zhdan 463f049395fSEgor Zhdan return Results; 464f049395fSEgor Zhdan } 465