clang 19.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
17#include "clang/Basic/Version.h"
18#include "llvm/ADT/APInt.h"
19#include "llvm/ADT/Hashing.h"
20#include "llvm/ADT/SetVector.h"
21#include "llvm/ADT/SmallPtrSet.h"
22#include "llvm/ADT/SmallVector.h"
23#include "llvm/ADT/Statistic.h"
24#include "llvm/Support/MemoryBuffer.h"
25#include "llvm/Support/Path.h"
26#include "llvm/Support/PrettyStackTrace.h"
27
28using namespace clang;
29using namespace api_notes;
30
31#define DEBUG_TYPE "API Notes"
32STATISTIC(NumHeaderAPINotes, "non-framework API notes files loaded");
33STATISTIC(NumPublicFrameworkAPINotes, "framework public API notes loaded");
34STATISTIC(NumPrivateFrameworkAPINotes, "framework private API notes loaded");
35STATISTIC(NumFrameworksSearched, "frameworks searched");
36STATISTIC(NumDirectoriesSearched, "header directories searched");
37STATISTIC(NumDirectoryCacheHits, "directory cache hits");
38
39namespace {
40/// Prints two successive strings, which much be kept alive as long as the
41/// PrettyStackTrace entry.
42class PrettyStackTraceDoubleString : public llvm::PrettyStackTraceEntry {
43 StringRef First, Second;
44
45public:
46 PrettyStackTraceDoubleString(StringRef First, StringRef Second)
47 : First(First), Second(Second) {}
48 void print(raw_ostream &OS) const override { OS << First << Second; }
49};
50} // namespace
51
53 : SM(SM), ImplicitAPINotes(LangOpts.APINotes) {}
54
56 // Free the API notes readers.
57 for (const auto &Entry : Readers) {
58 if (auto Reader = Entry.second.dyn_cast<APINotesReader *>())
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 assert(Reader && "Could not load the API notes we just generated?");
102 return Reader;
103}
104
105std::unique_ptr<APINotesReader>
106APINotesManager::loadAPINotes(StringRef Buffer) {
107 llvm::SmallVector<char, 1024> APINotesBuffer;
108 std::unique_ptr<llvm::MemoryBuffer> CompiledBuffer;
109 SourceMgrAdapter SMAdapter(
110 SM, SM.getDiagnostics(), diag::err_apinotes_message,
111 diag::warn_apinotes_message, diag::note_apinotes_message, std::nullopt);
112 llvm::raw_svector_ostream OS(APINotesBuffer);
113
114 if (api_notes::compileAPINotes(Buffer, nullptr, OS,
115 SMAdapter.getDiagHandler(),
116 SMAdapter.getDiagContext()))
117 return nullptr;
118
119 CompiledBuffer = llvm::MemoryBuffer::getMemBufferCopy(
120 StringRef(APINotesBuffer.data(), APINotesBuffer.size()));
121 auto Reader = APINotesReader::Create(std::move(CompiledBuffer), SwiftVersion);
122 assert(Reader && "Could not load the API notes we just generated?");
123 return Reader;
124}
125
126bool APINotesManager::loadAPINotes(const DirectoryEntry *HeaderDir,
127 FileEntryRef APINotesFile) {
128 assert(!Readers.contains(HeaderDir));
129 if (auto Reader = loadAPINotes(APINotesFile)) {
130 Readers[HeaderDir] = Reader.release();
131 return false;
132 }
133
134 Readers[HeaderDir] = nullptr;
135 return true;
136}
137
139APINotesManager::findAPINotesFile(DirectoryEntryRef Directory,
140 StringRef Basename, bool WantPublic) {
141 FileManager &FM = SM.getFileManager();
142
143 llvm::SmallString<128> Path(Directory.getName());
144
145 StringRef Suffix = WantPublic ? "" : "_private";
146
147 // Look for the source API notes file.
148 llvm::sys::path::append(Path, llvm::Twine(Basename) + Suffix + "." +
150 return FM.getOptionalFileRef(Path, /*Open*/ true);
151}
152
153OptionalDirectoryEntryRef APINotesManager::loadFrameworkAPINotes(
154 llvm::StringRef FrameworkPath, llvm::StringRef FrameworkName, bool Public) {
155 FileManager &FM = SM.getFileManager();
156
157 llvm::SmallString<128> Path(FrameworkPath);
158 unsigned FrameworkNameLength = Path.size();
159
160 StringRef Suffix = Public ? "" : "_private";
161
162 // Form the path to the APINotes file.
163 llvm::sys::path::append(Path, "APINotes");
164 llvm::sys::path::append(Path, (llvm::Twine(FrameworkName) + Suffix + "." +
166
167 // Try to open the APINotes file.
168 auto APINotesFile = FM.getOptionalFileRef(Path);
169 if (!APINotesFile)
170 return std::nullopt;
171
172 // Form the path to the corresponding header directory.
173 Path.resize(FrameworkNameLength);
174 llvm::sys::path::append(Path, Public ? "Headers" : "PrivateHeaders");
175
176 // Try to access the header directory.
177 auto HeaderDir = FM.getOptionalDirectoryRef(Path);
178 if (!HeaderDir)
179 return std::nullopt;
180
181 // Try to load the API notes.
182 if (loadAPINotes(*HeaderDir, *APINotesFile))
183 return std::nullopt;
184
185 // Success: return the header directory.
186 if (Public)
187 ++NumPublicFrameworkAPINotes;
188 else
189 ++NumPrivateFrameworkAPINotes;
190 return *HeaderDir;
191}
192
194 const FileEntry *File, const Module *M) {
195 if (File->tryGetRealPathName().empty())
196 return;
197
198 StringRef RealFileName =
199 llvm::sys::path::filename(File->tryGetRealPathName());
200 StringRef RealStem = llvm::sys::path::stem(RealFileName);
201 if (RealStem.ends_with("_private"))
202 return;
203
204 unsigned DiagID = diag::warn_apinotes_private_case;
205 if (M->IsSystem)
206 DiagID = diag::warn_apinotes_private_case_system;
207
208 Diags.Report(SourceLocation(), DiagID) << M->Name << RealFileName;
209}
210
211/// \returns true if any of \p module's immediate submodules are defined in a
212/// private module map
213static bool hasPrivateSubmodules(const Module *M) {
214 return llvm::any_of(M->submodules(), [](const Module *Submodule) {
215 return Submodule->ModuleMapIsPrivate;
216 });
217}
218
221 ArrayRef<std::string> SearchPaths) {
222 FileManager &FM = SM.getFileManager();
223 auto ModuleName = M->getTopLevelModuleName();
224 auto ExportedModuleName = M->getTopLevelModule()->ExportAsModule;
226
227 // First, look relative to the module itself.
228 if (LookInModule && M->Directory) {
229 // Local function to try loading an API notes file in the given directory.
230 auto tryAPINotes = [&](DirectoryEntryRef Dir, bool WantPublic) {
231 if (auto File = findAPINotesFile(Dir, ModuleName, WantPublic)) {
232 if (!WantPublic)
234
235 APINotes.push_back(*File);
236 }
237 // If module FooCore is re-exported through module Foo, try Foo.apinotes.
238 if (!ExportedModuleName.empty())
239 if (auto File = findAPINotesFile(Dir, ExportedModuleName, WantPublic))
240 APINotes.push_back(*File);
241 };
242
243 if (M->IsFramework) {
244 // For frameworks, we search in the "Headers" or "PrivateHeaders"
245 // subdirectory.
246 //
247 // Public modules:
248 // - Headers/Foo.apinotes
249 // - PrivateHeaders/Foo_private.apinotes (if there are private submodules)
250 // Private modules:
251 // - PrivateHeaders/Bar.apinotes (except that 'Bar' probably already has
252 // the word "Private" in it in practice)
254
255 if (!M->ModuleMapIsPrivate) {
256 unsigned PathLen = Path.size();
257
258 llvm::sys::path::append(Path, "Headers");
259 if (auto APINotesDir = FM.getOptionalDirectoryRef(Path))
260 tryAPINotes(*APINotesDir, /*wantPublic=*/true);
261
262 Path.resize(PathLen);
263 }
264
266 llvm::sys::path::append(Path, "PrivateHeaders");
267 if (auto PrivateAPINotesDir = FM.getOptionalDirectoryRef(Path))
268 tryAPINotes(*PrivateAPINotesDir,
269 /*wantPublic=*/M->ModuleMapIsPrivate);
270 }
271 } else {
272 // Public modules:
273 // - Foo.apinotes
274 // - Foo_private.apinotes (if there are private submodules)
275 // Private modules:
276 // - Bar.apinotes (except that 'Bar' probably already has the word
277 // "Private" in it in practice)
278 tryAPINotes(*M->Directory, /*wantPublic=*/true);
280 tryAPINotes(*M->Directory, /*wantPublic=*/false);
281 }
282
283 if (!APINotes.empty())
284 return APINotes;
285 }
286
287 // Second, look for API notes for this module in the module API
288 // notes search paths.
289 for (const auto &SearchPath : SearchPaths) {
290 if (auto SearchDir = FM.getOptionalDirectoryRef(SearchPath)) {
291 if (auto File = findAPINotesFile(*SearchDir, ModuleName)) {
292 APINotes.push_back(*File);
293 return APINotes;
294 }
295 }
296 }
297
298 // Didn't find any API notes.
299 return APINotes;
300}
301
303 Module *M, bool LookInModule, ArrayRef<std::string> SearchPaths) {
304 assert(!CurrentModuleReaders[ReaderKind::Public] &&
305 "Already loaded API notes for the current module?");
306
307 auto APINotes = getCurrentModuleAPINotes(M, LookInModule, SearchPaths);
308 unsigned NumReaders = 0;
309 for (auto File : APINotes) {
310 CurrentModuleReaders[NumReaders++] = loadAPINotes(File).release();
311 if (!getCurrentModuleReaders().empty())
312 M->APINotesFile = File.getName().str();
313 }
314
315 return NumReaders > 0;
316}
317
319 ArrayRef<StringRef> Buffers) {
320 unsigned NumReader = 0;
321 for (auto Buf : Buffers) {
322 auto Reader = loadAPINotes(Buf);
323 assert(Reader && "Could not load the API notes we just generated?");
324
325 CurrentModuleReaders[NumReader++] = Reader.release();
326 }
327 return NumReader;
328}
329
333
334 // If there are readers for the current module, return them.
335 if (!getCurrentModuleReaders().empty()) {
336 Results.append(getCurrentModuleReaders().begin(),
338 return Results;
339 }
340
341 // If we're not allowed to implicitly load API notes files, we're done.
342 if (!ImplicitAPINotes)
343 return Results;
344
345 // If we don't have source location information, we're done.
346 if (Loc.isInvalid())
347 return Results;
348
349 // API notes are associated with the expansion location. Retrieve the
350 // file for this location.
351 SourceLocation ExpansionLoc = SM.getExpansionLoc(Loc);
352 FileID ID = SM.getFileID(ExpansionLoc);
353 if (ID.isInvalid())
354 return Results;
356 if (!File)
357 return Results;
358
359 // Look for API notes in the directory corresponding to this file, or one of
360 // its its parent directories.
361 OptionalDirectoryEntryRef Dir = File->getDir();
362 FileManager &FileMgr = SM.getFileManager();
363 llvm::SetVector<const DirectoryEntry *,
366 DirsVisited;
367 do {
368 // Look for an API notes reader for this header search directory.
369 auto Known = Readers.find(*Dir);
370
371 // If we already know the answer, chase it.
372 if (Known != Readers.end()) {
373 ++NumDirectoryCacheHits;
374
375 // We've been redirected to another directory for answers. Follow it.
376 if (Known->second && Known->second.is<DirectoryEntryRef>()) {
377 DirsVisited.insert(*Dir);
378 Dir = Known->second.get<DirectoryEntryRef>();
379 continue;
380 }
381
382 // We have the answer.
383 if (auto Reader = Known->second.dyn_cast<APINotesReader *>())
384 Results.push_back(Reader);
385 break;
386 }
387
388 // Look for API notes corresponding to this directory.
389 StringRef Path = Dir->getName();
390 if (llvm::sys::path::extension(Path) == ".framework") {
391 // If this is a framework directory, check whether there are API notes
392 // in the APINotes subdirectory.
393 auto FrameworkName = llvm::sys::path::stem(Path);
394 ++NumFrameworksSearched;
395
396 // Look for API notes for both the public and private headers.
397 OptionalDirectoryEntryRef PublicDir =
398 loadFrameworkAPINotes(Path, FrameworkName, /*Public=*/true);
399 OptionalDirectoryEntryRef PrivateDir =
400 loadFrameworkAPINotes(Path, FrameworkName, /*Public=*/false);
401
402 if (PublicDir || PrivateDir) {
403 // We found API notes: don't ever look past the framework directory.
404 Readers[*Dir] = nullptr;
405
406 // Pretend we found the result in the public or private directory,
407 // as appropriate. All headers should be in one of those two places,
408 // but be defensive here.
409 if (!DirsVisited.empty()) {
410 if (PublicDir && DirsVisited.back() == *PublicDir) {
411 DirsVisited.pop_back();
412 Dir = *PublicDir;
413 } else if (PrivateDir && DirsVisited.back() == *PrivateDir) {
414 DirsVisited.pop_back();
415 Dir = *PrivateDir;
416 }
417 }
418
419 // Grab the result.
420 if (auto Reader = Readers[*Dir].dyn_cast<APINotesReader *>())
421 Results.push_back(Reader);
422 break;
423 }
424 } else {
425 // Look for an APINotes file in this directory.
426 llvm::SmallString<128> APINotesPath(Dir->getName());
427 llvm::sys::path::append(
428 APINotesPath, (llvm::Twine("APINotes.") + SOURCE_APINOTES_EXTENSION));
429
430 // If there is an API notes file here, try to load it.
431 ++NumDirectoriesSearched;
432 if (auto APINotesFile = FileMgr.getOptionalFileRef(APINotesPath)) {
433 if (!loadAPINotes(*Dir, *APINotesFile)) {
434 ++NumHeaderAPINotes;
435 if (auto Reader = Readers[*Dir].dyn_cast<APINotesReader *>())
436 Results.push_back(Reader);
437 break;
438 }
439 }
440 }
441
442 // We didn't find anything. Look at the parent directory.
443 if (!DirsVisited.insert(*Dir)) {
444 Dir = std::nullopt;
445 break;
446 }
447
448 StringRef ParentPath = llvm::sys::path::parent_path(Path);
449 while (llvm::sys::path::stem(ParentPath) == "..")
450 ParentPath = llvm::sys::path::parent_path(ParentPath);
451
452 Dir = ParentPath.empty() ? std::nullopt
453 : FileMgr.getOptionalDirectoryRef(ParentPath);
454 } while (Dir);
455
456 // Path compression for all of the directories we visited, redirecting
457 // them to the directory we ended on. If no API notes were found, the
458 // resulting directory will be NULL, indicating no API notes.
459 for (const auto Visited : DirsVisited)
460 Readers[Visited] = Dir ? ReaderEntry(*Dir) : ReaderEntry();
461
462 return Results;
463}
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")
#define SM(sm)
Definition: Cuda.cpp:82
Defines the Diagnostic-related interfaces.
Defines the clang::FileManager interface and associated types.
llvm::DenseSet< const void * > Visited
Definition: HTMLLogger.cpp:146
static void print(llvm::raw_ostream &OS, const T &V, ASTContext &, QualType)
Definition: InterpFrame.cpp:96
Defines the clang::LangOptions interface.
Defines the SourceManager interface.
Defines version macros and version-related utility functions for Clang.
Concrete class used by the front-end to report problems and issues.
Definition: Diagnostic.h:192
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
Definition: Diagnostic.h:1547
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:300
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.
Definition: FileManager.h:240
OptionalDirectoryEntryRef getOptionalDirectoryRef(StringRef DirName, bool CacheFailure=true)
Get a DirectoryEntryRef if it exists, without doing anything on error.
Definition: FileManager.h:175
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:461
Describes a module or submodule.
Definition: Module.h:105
StringRef getTopLevelModuleName() const
Retrieve the name of the top-level module.
Definition: Module.h:675
unsigned IsSystem
Whether this is a "system" module (which assumes that all headers in it are system headers).
Definition: Module.h:332
std::string Name
The name of this module.
Definition: Module.h:108
llvm::iterator_range< submodule_iterator > submodules()
Definition: Module.h:782
unsigned ModuleMapIsPrivate
Whether this module came from a "private" module map, found next to a regular (public) module map.
Definition: Module.h:377
OptionalDirectoryEntryRef Directory
The build directory of this module.
Definition: Module.h:159
std::string APINotesFile
For the debug info, the path to this module's .apinotes file, if any.
Definition: Module.h:182
unsigned IsFramework
Whether this is a framework module.
Definition: Module.h:323
std::string ExportAsModule
The module through which entities defined in this module will eventually be exposed,...
Definition: Module.h:179
Module * getTopLevelModule()
Retrieve the top-level module for this (sub)module, which may be this module.
Definition: Module.h:665
Encodes a location in the source.
This class handles loading and caching of source files into memory.
FileID getFileID(SourceLocation SpellingLoc) const
Return the FileID for a SourceLocation.
DiagnosticsEngine & getDiagnostics() const
OptionalFileEntryRef getFileEntryRefForID(FileID FID) const
Returns the FileEntryRef for the provided FileID.
FileManager & getFileManager() const
FileID getOrCreateFileID(FileEntryRef SourceFile, SrcMgr::CharacteristicKind FileCharacter)
Get the FileID for SourceFile if it exists.
const FileEntry * getFileEntryForID(FileID FID) const
Returns the FileEntry record for the provided FileID.
SourceLocation getExpansionLoc(SourceLocation Loc) const
Given a SourceLocation object Loc, return the expansion location referenced by the ID.
std::optional< llvm::MemoryBufferRef > getBufferOrNone(FileID FID, SourceLocation Loc=SourceLocation()) const
Return the buffer for the specified FileID.
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.
A class that reads API notes data from a binary file that was written by the APINotesWriter.
static std::unique_ptr< APINotesReader > Create(std::unique_ptr< llvm::MemoryBuffer > InputBuffer, llvm::VersionTuple SwiftVersion)
Create a new API notes reader from the given member 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:783
@ Public
Represents declarations accessible to all clients.
The JSON file list parser is used to communicate input to InstallAPI.