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