xref: /freebsd-src/contrib/llvm-project/clang/lib/APINotes/APINotesManager.cpp (revision cb14a3fe5122c879eae1fb480ed7ce82a699ddb6)
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