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