1 //===--- APINotesManager.cpp - Manage API Notes Files ---------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "clang/APINotes/APINotesManager.h"
10 #include "clang/APINotes/APINotesReader.h"
11 #include "clang/APINotes/APINotesYAMLCompiler.h"
12 #include "clang/Basic/Diagnostic.h"
13 #include "clang/Basic/FileManager.h"
14 #include "clang/Basic/LangOptions.h"
15 #include "clang/Basic/Module.h"
16 #include "clang/Basic/SourceManager.h"
17 #include "clang/Basic/SourceMgrAdapter.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"
29 using namespace clang
;
30 using namespace api_notes
;
32 #define DEBUG_TYPE "API Notes"
33 STATISTIC(NumHeaderAPINotes
, "non-framework API notes files loaded");
34 STATISTIC(NumPublicFrameworkAPINotes
, "framework public API notes loaded");
35 STATISTIC(NumPrivateFrameworkAPINotes
, "framework private API notes loaded");
36 STATISTIC(NumFrameworksSearched
, "frameworks searched");
37 STATISTIC(NumDirectoriesSearched
, "header directories searched");
38 STATISTIC(NumDirectoryCacheHits
, "directory cache hits");
41 /// Prints two successive strings, which much be kept alive as long as the
42 /// PrettyStackTrace entry.
43 class PrettyStackTraceDoubleString
: public llvm::PrettyStackTraceEntry
{
44 StringRef First
, Second
;
47 PrettyStackTraceDoubleString(StringRef First
, StringRef Second
)
48 : First(First
), Second(Second
) {}
49 void print(raw_ostream
&OS
) const override
{ OS
<< First
<< Second
; }
53 APINotesManager::APINotesManager(SourceManager
&SM
, const LangOptions
&LangOpts
)
54 : SM(SM
), ImplicitAPINotes(LangOpts
.APINotes
) {}
56 APINotesManager::~APINotesManager() {
57 // Free the API notes readers.
58 for (const auto &Entry
: Readers
) {
59 if (auto Reader
= dyn_cast_if_present
<APINotesReader
*>(Entry
.second
))
63 delete CurrentModuleReaders
[ReaderKind::Public
];
64 delete CurrentModuleReaders
[ReaderKind::Private
];
67 std::unique_ptr
<APINotesReader
>
68 APINotesManager::loadAPINotes(FileEntryRef APINotesFile
) {
69 PrettyStackTraceDoubleString
Trace("Loading API notes from ",
70 APINotesFile
.getName());
72 // Open the source file.
73 auto SourceFileID
= SM
.getOrCreateFileID(APINotesFile
, SrcMgr::C_User
);
74 auto SourceBuffer
= SM
.getBufferOrNone(SourceFileID
, SourceLocation());
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
;
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
);
90 if (api_notes::compileAPINotes(
91 SourceBuffer
->getBuffer(), SM
.getFileEntryForID(SourceFileID
), OS
,
92 SMAdapter
.getDiagHandler(), SMAdapter
.getDiagContext()))
95 // Make a copy of the compiled form into the buffer.
96 CompiledBuffer
= llvm::MemoryBuffer::getMemBufferCopy(
97 StringRef(APINotesBuffer
.data(), APINotesBuffer
.size()));
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?");
106 std::unique_ptr
<APINotesReader
>
107 APINotesManager::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
);
115 if (api_notes::compileAPINotes(Buffer
, nullptr, OS
,
116 SMAdapter
.getDiagHandler(),
117 SMAdapter
.getDiagContext()))
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?");
127 bool APINotesManager::loadAPINotes(const DirectoryEntry
*HeaderDir
,
128 FileEntryRef APINotesFile
) {
129 assert(!Readers
.contains(HeaderDir
));
130 if (auto Reader
= loadAPINotes(APINotesFile
)) {
131 Readers
[HeaderDir
] = Reader
.release();
135 Readers
[HeaderDir
] = nullptr;
140 APINotesManager::findAPINotesFile(DirectoryEntryRef Directory
,
141 StringRef Basename
, bool WantPublic
) {
142 FileManager
&FM
= SM
.getFileManager();
144 llvm::SmallString
<128> Path(Directory
.getName());
146 StringRef Suffix
= WantPublic
? "" : "_private";
148 // Look for the source API notes file.
149 llvm::sys::path::append(Path
, llvm::Twine(Basename
) + Suffix
+ "." +
150 SOURCE_APINOTES_EXTENSION
);
151 return FM
.getOptionalFileRef(Path
, /*Open*/ true);
154 OptionalDirectoryEntryRef
APINotesManager::loadFrameworkAPINotes(
155 llvm::StringRef FrameworkPath
, llvm::StringRef FrameworkName
, bool Public
) {
156 FileManager
&FM
= SM
.getFileManager();
158 llvm::SmallString
<128> Path(FrameworkPath
);
159 unsigned FrameworkNameLength
= Path
.size();
161 StringRef Suffix
= Public
? "" : "_private";
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
+ "." +
166 SOURCE_APINOTES_EXTENSION
));
168 // Try to open the APINotes file.
169 auto APINotesFile
= FM
.getOptionalFileRef(Path
);
173 // Form the path to the corresponding header directory.
174 Path
.resize(FrameworkNameLength
);
175 llvm::sys::path::append(Path
, Public
? "Headers" : "PrivateHeaders");
177 // Try to access the header directory.
178 auto HeaderDir
= FM
.getOptionalDirectoryRef(Path
);
182 // Try to load the API notes.
183 if (loadAPINotes(*HeaderDir
, *APINotesFile
))
186 // Success: return the header directory.
188 ++NumPublicFrameworkAPINotes
;
190 ++NumPrivateFrameworkAPINotes
;
194 static void checkPrivateAPINotesName(DiagnosticsEngine
&Diags
,
195 const FileEntry
*File
, const Module
*M
) {
196 if (File
->tryGetRealPathName().empty())
199 StringRef RealFileName
=
200 llvm::sys::path::filename(File
->tryGetRealPathName());
201 StringRef RealStem
= llvm::sys::path::stem(RealFileName
);
202 if (RealStem
.ends_with("_private"))
205 unsigned DiagID
= diag::warn_apinotes_private_case
;
207 DiagID
= diag::warn_apinotes_private_case_system
;
209 Diags
.Report(SourceLocation(), DiagID
) << M
->Name
<< RealFileName
;
212 /// \returns true if any of \p module's immediate submodules are defined in a
213 /// private module map
214 static bool hasPrivateSubmodules(const Module
*M
) {
215 return llvm::any_of(M
->submodules(), [](const Module
*Submodule
) {
216 return Submodule
->ModuleMapIsPrivate
;
220 llvm::SmallVector
<FileEntryRef
, 2>
221 APINotesManager::getCurrentModuleAPINotes(Module
*M
, bool LookInModule
,
222 ArrayRef
<std::string
> SearchPaths
) {
223 FileManager
&FM
= SM
.getFileManager();
224 auto ModuleName
= M
->getTopLevelModuleName();
225 auto ExportedModuleName
= M
->getTopLevelModule()->ExportAsModule
;
226 llvm::SmallVector
<FileEntryRef
, 2> APINotes
;
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
)) {
234 checkPrivateAPINotesName(SM
.getDiagnostics(), *File
, M
);
236 APINotes
.push_back(*File
);
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
);
244 if (M
->IsFramework
) {
245 // For frameworks, we search in the "Headers" or "PrivateHeaders"
249 // - Headers/Foo.apinotes
250 // - PrivateHeaders/Foo_private.apinotes (if there are private submodules)
252 // - PrivateHeaders/Bar.apinotes (except that 'Bar' probably already has
253 // the word "Private" in it in practice)
254 llvm::SmallString
<128> Path(M
->Directory
->getName());
256 if (!M
->ModuleMapIsPrivate
) {
257 unsigned PathLen
= Path
.size();
259 llvm::sys::path::append(Path
, "Headers");
260 if (auto APINotesDir
= FM
.getOptionalDirectoryRef(Path
))
261 tryAPINotes(*APINotesDir
, /*wantPublic=*/true);
263 Path
.resize(PathLen
);
266 if (M
->ModuleMapIsPrivate
|| hasPrivateSubmodules(M
)) {
267 llvm::sys::path::append(Path
, "PrivateHeaders");
268 if (auto PrivateAPINotesDir
= FM
.getOptionalDirectoryRef(Path
))
269 tryAPINotes(*PrivateAPINotesDir
,
270 /*wantPublic=*/M
->ModuleMapIsPrivate
);
275 // - Foo_private.apinotes (if there are private submodules)
277 // - Bar.apinotes (except that 'Bar' probably already has the word
278 // "Private" in it in practice)
279 tryAPINotes(*M
->Directory
, /*wantPublic=*/true);
280 if (!M
->ModuleMapIsPrivate
&& hasPrivateSubmodules(M
))
281 tryAPINotes(*M
->Directory
, /*wantPublic=*/false);
284 if (!APINotes
.empty())
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
);
299 // Didn't find any API notes.
303 bool APINotesManager::loadCurrentModuleAPINotes(
304 Module
*M
, bool LookInModule
, ArrayRef
<std::string
> SearchPaths
) {
305 assert(!CurrentModuleReaders
[ReaderKind::Public
] &&
306 "Already loaded API notes for the current module?");
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();
316 return NumReaders
> 0;
319 bool APINotesManager::loadCurrentModuleAPINotesFromBuffer(
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?");
326 CurrentModuleReaders
[NumReader
++] = Reader
.release();
331 llvm::SmallVector
<APINotesReader
*, 2>
332 APINotesManager::findAPINotes(SourceLocation Loc
) {
333 llvm::SmallVector
<APINotesReader
*, 2> Results
;
335 // If there are readers for the current module, return them.
336 if (!getCurrentModuleReaders().empty()) {
337 Results
.append(getCurrentModuleReaders().begin(),
338 getCurrentModuleReaders().end());
342 // If we're not allowed to implicitly load API notes files, we're done.
343 if (!ImplicitAPINotes
)
346 // If we don't have source location information, we're done.
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
);
356 OptionalFileEntryRef File
= SM
.getFileEntryRefForID(ID
);
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
*,
365 SmallVector
<const DirectoryEntry
*, 4>,
366 llvm::SmallPtrSet
<const DirectoryEntry
*, 4>>
369 // Look for an API notes reader for this header search directory.
370 auto Known
= Readers
.find(*Dir
);
372 // If we already know the answer, chase it.
373 if (Known
!= Readers
.end()) {
374 ++NumDirectoryCacheHits
;
376 // We've been redirected to another directory for answers. Follow it.
377 if (Known
->second
&& isa
<DirectoryEntryRef
>(Known
->second
)) {
378 DirsVisited
.insert(*Dir
);
379 Dir
= cast
<DirectoryEntryRef
>(Known
->second
);
383 // We have the answer.
384 if (auto Reader
= dyn_cast_if_present
<APINotesReader
*>(Known
->second
))
385 Results
.push_back(Reader
);
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
;
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);
403 if (PublicDir
|| PrivateDir
) {
404 // We found API notes: don't ever look past the framework directory.
405 Readers
[*Dir
] = nullptr;
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();
414 } else if (PrivateDir
&& DirsVisited
.back() == *PrivateDir
) {
415 DirsVisited
.pop_back();
421 if (auto Reader
= Readers
[*Dir
].dyn_cast
<APINotesReader
*>())
422 Results
.push_back(Reader
);
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
));
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
)) {
436 if (auto Reader
= Readers
[*Dir
].dyn_cast
<APINotesReader
*>())
437 Results
.push_back(Reader
);
443 // We didn't find anything. Look at the parent directory.
444 if (!DirsVisited
.insert(*Dir
)) {
449 StringRef ParentPath
= llvm::sys::path::parent_path(Path
);
450 while (llvm::sys::path::stem(ParentPath
) == "..")
451 ParentPath
= llvm::sys::path::parent_path(ParentPath
);
453 Dir
= ParentPath
.empty() ? std::nullopt
454 : FileMgr
.getOptionalDirectoryRef(ParentPath
);
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();