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