xref: /llvm-project/clang/lib/APINotes/APINotesManager.cpp (revision 563c7c5539f05e7f8cbb42565c1f24466019f38b)
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