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 
12 #include "clang/Basic/Diagnostic.h"
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 
28 using namespace clang;
29 using namespace api_notes;
30 
31 #define DEBUG_TYPE "API Notes"
32 STATISTIC(NumHeaderAPINotes, "non-framework API notes files loaded");
33 STATISTIC(NumPublicFrameworkAPINotes, "framework public API notes loaded");
34 STATISTIC(NumPrivateFrameworkAPINotes, "framework private API notes loaded");
35 STATISTIC(NumFrameworksSearched, "frameworks searched");
36 STATISTIC(NumDirectoriesSearched, "header directories searched");
37 STATISTIC(NumDirectoryCacheHits, "directory cache hits");
38 
39 namespace {
40 /// Prints two successive strings, which much be kept alive as long as the
41 /// PrettyStackTrace entry.
42 class PrettyStackTraceDoubleString : public llvm::PrettyStackTraceEntry {
43  StringRef First, Second;
44 
45 public:
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 
66 std::unique_ptr<APINotesReader>
67 APINotesManager::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 
105 std::unique_ptr<APINotesReader>
106 APINotesManager::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 
126 bool 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 
139 APINotesManager::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 
153 OptionalDirectoryEntryRef 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
213 static 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(),
337  getCurrentModuleReaders().end());
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")
static char ID
Definition: Arena.cpp:183
#define SM(sm)
Definition: Cuda.cpp:83
Defines the Diagnostic-related interfaces.
Defines the clang::FileManager interface and associated types.
llvm::DenseSet< const void * > Visited
Definition: HTMLLogger.cpp:146
void print(llvm::raw_ostream &OS, const Pointer &P, ASTContext &Ctx, QualType Ty)
Defines the clang::LangOptions interface.
SourceLocation Loc
Definition: SemaObjC.cpp:755
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:193
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
Definition: Diagnostic.h:1553
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:482
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
unsigned ModuleMapIsPrivate
Whether this module came from a "private" module map, found next to a regular (public) module map.
Definition: Module.h:378
llvm::iterator_range< submodule_iterator > submodules()
Definition: Module.h:783
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
Module * getTopLevelModule()
Retrieve the top-level module for this (sub)module, which may be this module.
Definition: Module.h:666
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
Encodes a location in the source.
This class handles loading and caching of source files into memory.
DiagnosticsEngine & getDiagnostics() const
FileID getFileID(SourceLocation SpellingLoc) const
Return the FileID for a SourceLocation.
std::optional< llvm::MemoryBufferRef > getBufferOrNone(FileID FID, SourceLocation Loc=SourceLocation()) const
Return the buffer for the specified FileID.
OptionalFileEntryRef getFileEntryRefForID(FileID FID) const
Returns the FileEntryRef for the provided FileID.
FileManager & getFileManager() const
const FileEntry * getFileEntryForID(FileID FID) const
Returns the FileEntry record for the provided FileID.
FileID getOrCreateFileID(FileEntryRef SourceFile, SrcMgr::CharacteristicKind FileCharacter)
Get the FileID for SourceFile if it exists.
SourceLocation getExpansionLoc(SourceLocation Loc) const
Given a SourceLocation object Loc, return the expansion location referenced by the ID.
An adapter that can be used to translate diagnostics from one or more llvm::SourceMgr instances to a ...
APINotesManager(SourceManager &SM, const LangOptions &LangOpts)
ArrayRef< APINotesReader * > getCurrentModuleReaders() const
Retrieve the set of API notes readers for the current module.
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.
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...
static constexpr const char SOURCE_APINOTES_EXTENSION[]
The file extension used for the source representation of API notes.
Definition: Types.h:783
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.
@ Public
Represents declarations accessible to all clients.
The JSON file list parser is used to communicate input to InstallAPI.