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 VersionIndependentSwift(LangOpts.SwiftVersionIndependentAPINotes) {}
54
56 // Free the API notes readers.
57 for (const auto &Entry : Readers) {
58 if (auto Reader = dyn_cast_if_present<APINotesReader *>(Entry.second))
59 delete Reader;
60 }
61
62 delete CurrentModuleReaders[ReaderKind::Public];
63 delete CurrentModuleReaders[ReaderKind::Private];
64}
65
66std::unique_ptr<APINotesReader>
67APINotesManager::loadAPINotes(FileEntryRef APINotesFile) {
68 PrettyStackTraceDoubleString Trace("Loading API notes from ",
69 APINotesFile.getName());
70
71 // Open the source file.
72 auto SourceFileID = SM.getOrCreateFileID(APINotesFile, SrcMgr::C_User);
73 auto SourceBuffer = SM.getBufferOrNone(SourceFileID, SourceLocation());
74 if (!SourceBuffer)
75 return nullptr;
76
77 // Compile the API notes source into a buffer.
78 // FIXME: Either propagate OSType through or, better yet, improve the binary
79 // APINotes format to maintain complete availability information.
80 // FIXME: We don't even really need to go through the binary format at all;
81 // we're just going to immediately deserialize it again.
82 llvm::SmallVector<char, 1024> APINotesBuffer;
83 std::unique_ptr<llvm::MemoryBuffer> CompiledBuffer;
84 {
85 SourceMgrAdapter SMAdapter(
86 SM, SM.getDiagnostics(), diag::err_apinotes_message,
87 diag::warn_apinotes_message, diag::note_apinotes_message, APINotesFile);
88 llvm::raw_svector_ostream OS(APINotesBuffer);
90 SourceBuffer->getBuffer(), SM.getFileEntryForID(SourceFileID), OS,
91 SMAdapter.getDiagHandler(), SMAdapter.getDiagContext()))
92 return nullptr;
93
94 // Make a copy of the compiled form into the buffer.
95 CompiledBuffer = llvm::MemoryBuffer::getMemBufferCopy(
96 StringRef(APINotesBuffer.data(), APINotesBuffer.size()));
97 }
98
99 // Load the binary form we just compiled.
100 auto Reader = APINotesReader::Create(std::move(CompiledBuffer), SwiftVersion);
101 if (!Reader) {
102 llvm::consumeError(Reader.takeError());
103 return nullptr;
104 }
105 return std::move(Reader.get());
106}
107
108std::unique_ptr<APINotesReader>
109APINotesManager::loadAPINotes(StringRef Buffer) {
110 llvm::SmallVector<char, 1024> APINotesBuffer;
111 std::unique_ptr<llvm::MemoryBuffer> CompiledBuffer;
112 SourceMgrAdapter SMAdapter(
113 SM, SM.getDiagnostics(), diag::err_apinotes_message,
114 diag::warn_apinotes_message, diag::note_apinotes_message, std::nullopt);
115 llvm::raw_svector_ostream OS(APINotesBuffer);
116
117 if (api_notes::compileAPINotes(Buffer, nullptr, OS,
118 SMAdapter.getDiagHandler(),
119 SMAdapter.getDiagContext()))
120 return nullptr;
121
122 CompiledBuffer = llvm::MemoryBuffer::getMemBufferCopy(
123 StringRef(APINotesBuffer.data(), APINotesBuffer.size()));
124
125 auto Reader = APINotesReader::Create(std::move(CompiledBuffer), SwiftVersion);
126 if (!Reader) {
127 llvm::consumeError(Reader.takeError());
128 return nullptr;
129 }
130 return std::move(Reader.get());
131}
132
133bool APINotesManager::loadAPINotes(const DirectoryEntry *HeaderDir,
134 FileEntryRef APINotesFile) {
135 assert(!Readers.contains(HeaderDir));
136 if (auto Reader = loadAPINotes(APINotesFile)) {
137 Readers[HeaderDir] = Reader.release();
138 return false;
139 }
140
141 Readers[HeaderDir] = nullptr;
142 return true;
143}
144
146APINotesManager::findAPINotesFile(DirectoryEntryRef Directory,
147 StringRef Basename, bool WantPublic) {
148 FileManager &FM = SM.getFileManager();
149
150 llvm::SmallString<128> Path(Directory.getName());
151
152 StringRef Suffix = WantPublic ? "" : "_private";
153
154 // Look for the source API notes file.
155 llvm::sys::path::append(Path, llvm::Twine(Basename) + Suffix + "." +
157 return FM.getOptionalFileRef(Path, /*Open*/ true);
158}
159
160OptionalDirectoryEntryRef APINotesManager::loadFrameworkAPINotes(
161 llvm::StringRef FrameworkPath, llvm::StringRef FrameworkName, bool Public) {
162 FileManager &FM = SM.getFileManager();
163
164 llvm::SmallString<128> Path(FrameworkPath);
165 unsigned FrameworkNameLength = Path.size();
166
167 StringRef Suffix = Public ? "" : "_private";
168
169 // Form the path to the APINotes file.
170 llvm::sys::path::append(Path, "APINotes");
171 llvm::sys::path::append(Path, (llvm::Twine(FrameworkName) + Suffix + "." +
173
174 // Try to open the APINotes file.
175 auto APINotesFile = FM.getOptionalFileRef(Path);
176 if (!APINotesFile)
177 return std::nullopt;
178
179 // Form the path to the corresponding header directory.
180 Path.resize(FrameworkNameLength);
181 llvm::sys::path::append(Path, Public ? "Headers" : "PrivateHeaders");
182
183 // Try to access the header directory.
184 auto HeaderDir = FM.getOptionalDirectoryRef(Path);
185 if (!HeaderDir)
186 return std::nullopt;
187
188 // Try to load the API notes.
189 if (loadAPINotes(*HeaderDir, *APINotesFile))
190 return std::nullopt;
191
192 // Success: return the header directory.
193 if (Public)
194 ++NumPublicFrameworkAPINotes;
195 else
196 ++NumPrivateFrameworkAPINotes;
197 return *HeaderDir;
198}
199
201 const FileEntry *File, const Module *M) {
202 if (File->tryGetRealPathName().empty())
203 return;
204
205 StringRef RealFileName =
206 llvm::sys::path::filename(File->tryGetRealPathName());
207 StringRef RealStem = llvm::sys::path::stem(RealFileName);
208 if (RealStem.ends_with("_private"))
209 return;
210
211 unsigned DiagID = diag::warn_apinotes_private_case;
212 if (M->IsSystem)
213 DiagID = diag::warn_apinotes_private_case_system;
214
215 Diags.Report(SourceLocation(), DiagID) << M->Name << RealFileName;
216}
217
218/// \returns true if any of \p module's immediate submodules are defined in a
219/// private module map
220static bool hasPrivateSubmodules(const Module *M) {
221 return llvm::any_of(M->submodules(), [](const Module *Submodule) {
222 return Submodule->ModuleMapIsPrivate;
223 });
224}
225
226llvm::SmallVector<FileEntryRef, 2>
228 ArrayRef<std::string> SearchPaths) {
229 FileManager &FM = SM.getFileManager();
230 auto ModuleName = M->getTopLevelModuleName();
231 auto ExportedModuleName = M->getTopLevelModule()->ExportAsModule;
233
234 // First, look relative to the module itself.
235 if (LookInModule && M->Directory) {
236 // Local function to try loading an API notes file in the given directory.
237 auto tryAPINotes = [&](DirectoryEntryRef Dir, bool WantPublic) {
238 if (auto File = findAPINotesFile(Dir, ModuleName, WantPublic)) {
239 if (!WantPublic)
240 checkPrivateAPINotesName(SM.getDiagnostics(), *File, M);
241
242 APINotes.push_back(*File);
243 }
244 // If module FooCore is re-exported through module Foo, try Foo.apinotes.
245 if (!ExportedModuleName.empty())
246 if (auto File = findAPINotesFile(Dir, ExportedModuleName, WantPublic))
247 APINotes.push_back(*File);
248 };
249
250 if (M->IsFramework) {
251 // For frameworks, we search in the "Headers" or "PrivateHeaders"
252 // subdirectory.
253 //
254 // Public modules:
255 // - Headers/Foo.apinotes
256 // - PrivateHeaders/Foo_private.apinotes (if there are private submodules)
257 // Private modules:
258 // - PrivateHeaders/Bar.apinotes (except that 'Bar' probably already has
259 // the word "Private" in it in practice)
261
262 if (!M->ModuleMapIsPrivate) {
263 unsigned PathLen = Path.size();
264
265 llvm::sys::path::append(Path, "Headers");
266 if (auto APINotesDir = FM.getOptionalDirectoryRef(Path))
267 tryAPINotes(*APINotesDir, /*wantPublic=*/true);
268
269 Path.resize(PathLen);
270 }
271
273 llvm::sys::path::append(Path, "PrivateHeaders");
274 if (auto PrivateAPINotesDir = FM.getOptionalDirectoryRef(Path))
275 tryAPINotes(*PrivateAPINotesDir,
276 /*wantPublic=*/M->ModuleMapIsPrivate);
277 }
278 } else {
279 // Public modules:
280 // - Foo.apinotes
281 // - Foo_private.apinotes (if there are private submodules)
282 // Private modules:
283 // - Bar.apinotes (except that 'Bar' probably already has the word
284 // "Private" in it in practice)
285 tryAPINotes(*M->Directory, /*wantPublic=*/true);
287 tryAPINotes(*M->Directory, /*wantPublic=*/false);
288 }
289
290 if (!APINotes.empty())
291 return APINotes;
292 }
293
294 // Second, look for API notes for this module in the module API
295 // notes search paths.
296 for (const auto &SearchPath : SearchPaths) {
297 if (auto SearchDir = FM.getOptionalDirectoryRef(SearchPath)) {
298 if (auto File = findAPINotesFile(*SearchDir, ModuleName)) {
299 APINotes.push_back(*File);
300 return APINotes;
301 }
302 }
303 }
304
305 // Didn't find any API notes.
306 return APINotes;
307}
308
310 Module *M, bool LookInModule, ArrayRef<std::string> SearchPaths) {
311 assert(!CurrentModuleReaders[ReaderKind::Public] &&
312 "Already loaded API notes for the current module?");
313
314 auto APINotes = getCurrentModuleAPINotes(M, LookInModule, SearchPaths);
315 unsigned NumReaders = 0;
316 for (auto File : APINotes) {
317 CurrentModuleReaders[NumReaders++] = loadAPINotes(File).release();
318 if (!getCurrentModuleReaders().empty())
319 M->APINotesFile = File.getName().str();
320 }
321
322 return NumReaders > 0;
323}
324
326 ArrayRef<StringRef> Buffers) {
327 unsigned NumReader = 0;
328 for (auto Buf : Buffers) {
329 auto Reader = loadAPINotes(Buf);
330 assert(Reader && "Could not load the API notes we just generated?");
331
332 CurrentModuleReaders[NumReader++] = Reader.release();
333 }
334 return NumReader;
335}
336
340
341 // If there are readers for the current module, return them.
342 if (!getCurrentModuleReaders().empty()) {
343 Results.append(getCurrentModuleReaders().begin(),
345 return Results;
346 }
347
348 // If we're not allowed to implicitly load API notes files, we're done.
349 if (!ImplicitAPINotes)
350 return Results;
351
352 // If we don't have source location information, we're done.
353 if (Loc.isInvalid())
354 return Results;
355
356 // API notes are associated with the expansion location. Retrieve the
357 // file for this location.
358 SourceLocation ExpansionLoc = SM.getExpansionLoc(Loc);
359 FileID ID = SM.getFileID(ExpansionLoc);
360 if (ID.isInvalid())
361 return Results;
362 OptionalFileEntryRef File = SM.getFileEntryRefForID(ID);
363 if (!File)
364 return Results;
365
366 // Look for API notes in the directory corresponding to this file, or one of
367 // its its parent directories.
368 OptionalDirectoryEntryRef Dir = File->getDir();
369 FileManager &FileMgr = SM.getFileManager();
370 llvm::SetVector<const DirectoryEntry *,
373 DirsVisited;
374 do {
375 // Look for an API notes reader for this header search directory.
376 auto Known = Readers.find(*Dir);
377
378 // If we already know the answer, chase it.
379 if (Known != Readers.end()) {
380 ++NumDirectoryCacheHits;
381
382 // We've been redirected to another directory for answers. Follow it.
383 if (Known->second && isa<DirectoryEntryRef>(Known->second)) {
384 DirsVisited.insert(*Dir);
385 Dir = cast<DirectoryEntryRef>(Known->second);
386 continue;
387 }
388
389 // We have the answer.
390 if (auto Reader = dyn_cast_if_present<APINotesReader *>(Known->second))
391 Results.push_back(Reader);
392 break;
393 }
394
395 // Look for API notes corresponding to this directory.
396 StringRef Path = Dir->getName();
397 if (llvm::sys::path::extension(Path) == ".framework") {
398 // If this is a framework directory, check whether there are API notes
399 // in the APINotes subdirectory.
400 auto FrameworkName = llvm::sys::path::stem(Path);
401 ++NumFrameworksSearched;
402
403 // Look for API notes for both the public and private headers.
404 OptionalDirectoryEntryRef PublicDir =
405 loadFrameworkAPINotes(Path, FrameworkName, /*Public=*/true);
406 OptionalDirectoryEntryRef PrivateDir =
407 loadFrameworkAPINotes(Path, FrameworkName, /*Public=*/false);
408
409 if (PublicDir || PrivateDir) {
410 // We found API notes: don't ever look past the framework directory.
411 Readers[*Dir] = nullptr;
412
413 // Pretend we found the result in the public or private directory,
414 // as appropriate. All headers should be in one of those two places,
415 // but be defensive here.
416 if (!DirsVisited.empty()) {
417 if (PublicDir && DirsVisited.back() == *PublicDir) {
418 DirsVisited.pop_back();
419 Dir = *PublicDir;
420 } else if (PrivateDir && DirsVisited.back() == *PrivateDir) {
421 DirsVisited.pop_back();
422 Dir = *PrivateDir;
423 }
424 }
425
426 // Grab the result.
427 if (auto Reader = Readers[*Dir].dyn_cast<APINotesReader *>())
428 Results.push_back(Reader);
429 break;
430 }
431 } else {
432 // Look for an APINotes file in this directory.
433 llvm::SmallString<128> APINotesPath(Dir->getName());
434 llvm::sys::path::append(
435 APINotesPath, (llvm::Twine("APINotes.") + SOURCE_APINOTES_EXTENSION));
436
437 // If there is an API notes file here, try to load it.
438 ++NumDirectoriesSearched;
439 if (auto APINotesFile = FileMgr.getOptionalFileRef(APINotesPath)) {
440 if (!loadAPINotes(*Dir, *APINotesFile)) {
441 ++NumHeaderAPINotes;
442 if (auto Reader = Readers[*Dir].dyn_cast<APINotesReader *>())
443 Results.push_back(Reader);
444 break;
445 }
446 }
447 }
448
449 // We didn't find anything. Look at the parent directory.
450 if (!DirsVisited.insert(*Dir)) {
451 Dir = std::nullopt;
452 break;
453 }
454
455 StringRef ParentPath = llvm::sys::path::parent_path(Path);
456 while (llvm::sys::path::stem(ParentPath) == "..")
457 ParentPath = llvm::sys::path::parent_path(ParentPath);
458
459 Dir = ParentPath.empty() ? std::nullopt
460 : FileMgr.getOptionalDirectoryRef(ParentPath);
461 } while (Dir);
462
463 // Path compression for all of the directories we visited, redirecting
464 // them to the directory we ended on. If no API notes were found, the
465 // resulting directory will be NULL, indicating no API notes.
466 for (const auto Visited : DirsVisited)
467 Readers[Visited] = Dir ? ReaderEntry(*Dir) : ReaderEntry();
468
469 return Results;
470}
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, ASTContext &ASTCtx, 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:232
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:302
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:53
OptionalFileEntryRef getOptionalFileRef(StringRef Filename, bool OpenFile=false, bool CacheFailure=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:144
StringRef getTopLevelModuleName() const
Retrieve the name of the top-level module.
Definition Module.h:732
unsigned IsSystem
Whether this is a "system" module (which assumes that all headers in it are system headers).
Definition Module.h:389
std::string Name
The name of this module.
Definition Module.h:147
llvm::iterator_range< submodule_iterator > submodules()
Definition Module.h:838
unsigned ModuleMapIsPrivate
Whether this module came from a "private" module map, found next to a regular (public) module map.
Definition Module.h:434
OptionalDirectoryEntryRef Directory
The build directory of this module.
Definition Module.h:198
std::string APINotesFile
For the debug info, the path to this module's .apinotes file, if any.
Definition Module.h:221
unsigned IsFramework
Whether this is a framework module.
Definition Module.h:380
std::string ExportAsModule
The module through which entities defined in this module will eventually be exposed,...
Definition Module.h:218
Module * getTopLevelModule()
Retrieve the top-level module for this (sub)module, which may be this module.
Definition Module.h:722
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:897
@ 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:208
CustomizableOptional< DirectoryEntryRef > OptionalDirectoryEntryRef
U cast(CodeGen::Address addr)
Definition Address.h:327