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