Merge remote-tracking branch 'origin/swift-3.1-branch' into stable
* origin/swift-3.1-branch:
[API Notes] Ensure that modules get rebuilt when binary API notes change.
[API Notes] Ensure that modules get rebuilt when API notes change.
[API Notes] Load both public and private API notes when they are present.
[API Notes] Load API notes from framework/header search paths.
diff --git a/include/clang/APINotes/APINotesManager.h b/include/clang/APINotes/APINotesManager.h
index 7f540b9..a300c14 100644
--- a/include/clang/APINotes/APINotesManager.h
+++ b/include/clang/APINotes/APINotesManager.h
@@ -15,6 +15,7 @@
#define LLVM_CLANG_APINOTES_APINOTESMANAGER_H
#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/Module.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/PointerUnion.h"
@@ -54,8 +55,11 @@
/// source file from which an entity was declared.
bool ImplicitAPINotes;
- /// The API notes reader for the current module.
- std::unique_ptr<APINotesReader> CurrentModuleReader;
+ /// API notes readers for the current module.
+ ///
+ /// There can be up to two of these, one for public headers and one
+ /// for private headers.
+ APINotesReader *CurrentModuleReaders[2] = { nullptr, nullptr };
/// Whether we have already pruned the API notes cache.
bool PrunedCache;
@@ -81,6 +85,13 @@
bool loadAPINotes(const DirectoryEntry *HeaderDir,
const FileEntry *APINotesFile);
+ /// Look for API notes in the given directory.
+ ///
+ /// This might find either a binary or source API notes.
+ const FileEntry *findAPINotesFile(const DirectoryEntry *directory,
+ StringRef filename,
+ bool wantPublic = true);
+
/// Attempt to load API notes for the given framework.
///
/// \param FrameworkPath The path to the framework.
@@ -100,21 +111,25 @@
/// Load the API notes for the current module.
///
- /// \param moduleName The name of the current module.
+ /// \param module The current module.
+ /// \param lookInModule Whether to look inside the module itself.
/// \param searchPaths The paths in which we should search for API notes
/// for the current module.
///
- /// \returns the file entry for the API notes file loaded, or nullptr if
- /// no API notes were found.
- const FileEntry *loadCurrentModuleAPINotes(StringRef moduleName,
- ArrayRef<std::string> searchPaths);
+ /// \returns true if API notes were successfully loaded, \c false otherwise.
+ bool loadCurrentModuleAPINotes(const Module *module,
+ bool lookInModule,
+ ArrayRef<std::string> searchPaths);
- /// Find the API notes reader that corresponds to the given source location.
- APINotesReader *findAPINotes(SourceLocation Loc);
-
- APINotesReader *getCurrentModuleReader() {
- return CurrentModuleReader.get();
+ /// Retrieve the set of API notes readers for the current module.
+ ArrayRef<APINotesReader *> getCurrentModuleReaders() const {
+ unsigned numReaders = static_cast<unsigned>(CurrentModuleReaders[0] != nullptr) +
+ static_cast<unsigned>(CurrentModuleReaders[1] != nullptr);
+ return llvm::makeArrayRef(CurrentModuleReaders).slice(0, numReaders);
}
+
+ /// Find the API notes readers that correspond to the given source location.
+ llvm::SmallVector<APINotesReader *, 2> findAPINotes(SourceLocation Loc);
};
} // end namespace api_notes
diff --git a/include/clang/APINotes/APINotesReader.h b/include/clang/APINotes/APINotesReader.h
index cc6dafb..aa88bac 100644
--- a/include/clang/APINotes/APINotesReader.h
+++ b/include/clang/APINotes/APINotesReader.h
@@ -31,7 +31,8 @@
Implementation &Impl;
- APINotesReader(std::unique_ptr<llvm::MemoryBuffer> inputBuffer, bool &failed);
+ APINotesReader(llvm::MemoryBuffer *inputBuffer, bool ownsInputBuffer,
+ bool &failed);
public:
/// Create a new API notes reader from the given member buffer, which
@@ -41,6 +42,13 @@
static std::unique_ptr<APINotesReader>
get(std::unique_ptr<llvm::MemoryBuffer> inputBuffer);
+ /// Create a new API notes reader from the given member buffer, which
+ /// contains the contents of a binary API notes file.
+ ///
+ /// \returns the new API notes reader, or null if an error occurred.
+ static std::unique_ptr<APINotesReader>
+ getUnmanaged(llvm::MemoryBuffer *inputBuffer);
+
~APINotesReader();
APINotesReader(const APINotesReader &) = delete;
@@ -50,6 +58,10 @@
/// notes.
StringRef getModuleName() const;
+ /// Retrieve the size and modification time of the source file from
+ /// which this API notes file was created, if known.
+ Optional<std::pair<off_t, time_t>> getSourceFileSizeAndModTime() const;
+
/// Retrieve the module options
ModuleOptions getModuleOptions() const;
diff --git a/include/clang/APINotes/APINotesWriter.h b/include/clang/APINotes/APINotesWriter.h
index cc93a5f..4bf3ce1 100644
--- a/include/clang/APINotes/APINotesWriter.h
+++ b/include/clang/APINotes/APINotesWriter.h
@@ -23,6 +23,9 @@
}
namespace clang {
+
+class FileEntry;
+
namespace api_notes {
/// A class that writes API notes data to a binary representation that can be
@@ -32,8 +35,9 @@
Implementation &Impl;
public:
- /// Create a new API notes writer with the given module name.
- APINotesWriter(StringRef moduleName);
+ /// Create a new API notes writer with the given module name and
+ /// (optional) source file.
+ APINotesWriter(StringRef moduleName, const FileEntry *sourceFile);
~APINotesWriter();
APINotesWriter(const APINotesWriter &) = delete;
diff --git a/include/clang/APINotes/APINotesYAMLCompiler.h b/include/clang/APINotes/APINotesYAMLCompiler.h
index f459e07..508da65 100644
--- a/include/clang/APINotes/APINotesYAMLCompiler.h
+++ b/include/clang/APINotes/APINotesYAMLCompiler.h
@@ -23,6 +23,9 @@
}
namespace clang {
+
+class FileEntry;
+
namespace api_notes {
enum class ActionType {
@@ -42,6 +45,7 @@
/// Converts API notes from YAML format to binary format.
bool compileAPINotes(llvm::StringRef yamlInput,
+ const FileEntry *sourceFile,
llvm::raw_ostream &os,
OSType targetOS,
llvm::SourceMgr::DiagHandlerTy diagHandler = nullptr,
diff --git a/include/clang/Basic/LangOptions.def b/include/clang/Basic/LangOptions.def
index a9b6d27..d192601 100644
--- a/include/clang/Basic/LangOptions.def
+++ b/include/clang/Basic/LangOptions.def
@@ -252,6 +252,7 @@
LANGOPT(RetainCommentsFromSystemHeaders, 1, 0, "retain documentation comments from system headers in the AST")
LANGOPT(APINotes, 1, 0, "use external API notes")
+LANGOPT(APINotesModules, 1, 0, "use external API notes")
LANGOPT(SanitizeAddressFieldPadding, 2, 0, "controls how aggressive is ASan "
"field padding (0: none, 1:least "
diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td
index b86ef88..cbd2a4a 100644
--- a/include/clang/Driver/Options.td
+++ b/include/clang/Driver/Options.td
@@ -542,8 +542,12 @@
def fapinotes : Flag<["-"], "fapinotes">, Group<f_clang_Group>,
Flags<[CC1Option]>, HelpText<"Enable external API notes support">;
+def fapinotes_modules : Flag<["-"], "fapinotes-modules">, Group<f_clang_Group>,
+ Flags<[CC1Option]>, HelpText<"Enable module-based external API notes support">;
def fno_apinotes : Flag<["-"], "fno-apinotes">, Group<f_clang_Group>,
Flags<[CC1Option]>, HelpText<"Disable external API notes support">;
+def fno_apinotes_modules : Flag<["-"], "fno-apinotes-modules">, Group<f_clang_Group>,
+ Flags<[CC1Option]>, HelpText<"Disable module-based external API notes support">;
def fapinotes_cache_path : Joined<["-"], "fapinotes-cache-path=">,
Group<i_Group>, Flags<[DriverOption, CC1Option]>, MetaVarName<"<directory>">,
HelpText<"Specify the API notes cache path">;
diff --git a/lib/APINotes/APINotesFormat.h b/lib/APINotes/APINotesFormat.h
index 1db2380..542f908 100644
--- a/lib/APINotes/APINotesFormat.h
+++ b/lib/APINotes/APINotesFormat.h
@@ -36,7 +36,7 @@
/// API notes file minor version number.
///
/// When the format changes IN ANY WAY, this number should be incremented.
-const uint16_t VERSION_MINOR = 14; // Objective-C class properties
+const uint16_t VERSION_MINOR = 15; // source file info
using IdentifierID = PointerEmbeddedInt<unsigned, 31>;
using IdentifierIDField = BCVBR<16>;
@@ -106,7 +106,8 @@
enum {
METADATA = 1,
MODULE_NAME = 2,
- MODULE_OPTIONS = 3
+ MODULE_OPTIONS = 3,
+ SOURCE_FILE = 4,
};
using MetadataLayout = BCRecordLayout<
@@ -124,6 +125,12 @@
MODULE_OPTIONS,
BCFixed<1> // SwiftInferImportAsMember
>;
+
+ using SourceFileLayout = BCRecordLayout<
+ SOURCE_FILE,
+ BCVBR<16>, // file size
+ BCVBR<16> // creation time
+ >;
}
namespace identifier_block {
diff --git a/lib/APINotes/APINotesManager.cpp b/lib/APINotes/APINotesManager.cpp
index 3c3d5fd..834d7e1 100644
--- a/lib/APINotes/APINotesManager.cpp
+++ b/lib/APINotes/APINotesManager.cpp
@@ -1,4 +1,4 @@
-//===--- APINotesMAnager.cpp - Manage API Notes Files ---------------------===//
+//===--- APINotesManager.cpp - Manage API Notes Files ---------------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -63,6 +63,9 @@
delete reader;
}
}
+
+ delete CurrentModuleReaders[0];
+ delete CurrentModuleReaders[1];
}
/// \brief Write a new timestamp file with the given path.
@@ -143,12 +146,14 @@
StringRef apiNotesFileExt = llvm::sys::path::extension(apiNotesFileName);
if (!apiNotesFileExt.empty() &&
apiNotesFileExt.substr(1) == BINARY_APINOTES_EXTENSION) {
+ auto compiledFileID = SourceMgr.createFileID(apiNotesFile, SourceLocation(), SrcMgr::C_User);
+
// Load the file.
- auto buffer = fileMgr.getBufferForFile(apiNotesFile);
+ auto buffer = SourceMgr.getBuffer(compiledFileID, SourceLocation());
if (!buffer) return nullptr;
// Load the binary form.
- return APINotesReader::get(std::move(buffer.get()));
+ return APINotesReader::getUnmanaged(buffer);
}
// If we haven't pruned the API notes cache yet during this execution, do
@@ -178,11 +183,16 @@
/*cacheFailure=*/false)) {
// Load the file contents.
if (auto buffer = fileMgr.getBufferForFile(compiledFile)) {
- // Make sure the file is up-to-date.
- if (compiledFile->getModificationTime()
- >= apiNotesFile->getModificationTime()) {
- // Load the file.
- if (auto reader = APINotesReader::get(std::move(buffer.get()))) {
+ // Load the file.
+ if (auto reader = APINotesReader::get(std::move(buffer.get()))) {
+ bool outOfDate = false;
+ if (auto sizeAndModTime = reader->getSourceFileSizeAndModTime()) {
+ if (sizeAndModTime->first != apiNotesFile->getSize() ||
+ sizeAndModTime->second != apiNotesFile->getModificationTime())
+ outOfDate = true;
+ }
+
+ if (!outOfDate) {
// Success.
++NumBinaryCacheHits;
return reader;
@@ -199,13 +209,15 @@
}
// Open the source file.
- auto buffer = fileMgr.getBufferForFile(apiNotesFile);
- if (!buffer) return nullptr;
+ auto sourceFileID = SourceMgr.createFileID(apiNotesFile, SourceLocation(), SrcMgr::C_User);
+ auto sourceBuffer = SourceMgr.getBuffer(sourceFileID, SourceLocation());
+ if (!sourceBuffer) return nullptr;
// Compile the API notes source into a buffer.
// FIXME: Either propagate OSType through or, better yet, improve the binary
// APINotes format to maintain complete availability information.
llvm::SmallVector<char, 1024> apiNotesBuffer;
+ std::unique_ptr<llvm::MemoryBuffer> compiledBuffer;
{
SourceMgrAdapter srcMgrAdapter(SourceMgr, SourceMgr.getDiagnostics(),
diag::err_apinotes_message,
@@ -213,7 +225,8 @@
diag::note_apinotes_message,
apiNotesFile);
llvm::raw_svector_ostream OS(apiNotesBuffer);
- if (api_notes::compileAPINotes(buffer.get()->getBuffer(),
+ if (api_notes::compileAPINotes(sourceBuffer->getBuffer(),
+ SourceMgr.getFileEntryForID(sourceFileID),
OS,
api_notes::OSType::Absent,
srcMgrAdapter.getDiagHandler(),
@@ -221,7 +234,7 @@
return nullptr;
// Make a copy of the compiled form into the buffer.
- buffer = llvm::MemoryBuffer::getMemBufferCopy(
+ compiledBuffer = llvm::MemoryBuffer::getMemBufferCopy(
StringRef(apiNotesBuffer.data(), apiNotesBuffer.size()));
}
@@ -244,7 +257,8 @@
bool hadError;
{
llvm::raw_fd_ostream out(temporaryFD, /*shouldClose=*/true);
- out.write(buffer.get()->getBufferStart(), buffer.get()->getBufferSize());
+ out.write(compiledBuffer.get()->getBufferStart(),
+ compiledBuffer.get()->getBufferSize());
out.flush();
hadError = out.has_error();
@@ -258,7 +272,7 @@
}
// Load the binary form we just compiled.
- auto reader = APINotesReader::get(std::move(*buffer));
+ auto reader = APINotesReader::get(std::move(compiledBuffer));
assert(reader && "Could not load the API notes we just generated?");
return reader;
}
@@ -275,6 +289,34 @@
return true;
}
+const FileEntry *APINotesManager::findAPINotesFile(const DirectoryEntry *directory,
+ StringRef basename,
+ bool wantPublic) {
+ FileManager &fileMgr = SourceMgr.getFileManager();
+
+ llvm::SmallString<128> path;
+ path += directory->getName();
+
+ unsigned pathLen = path.size();
+
+ StringRef basenameSuffix = "";
+ if (!wantPublic) basenameSuffix = "_private";
+
+ // Look for a binary API notes file.
+ llvm::sys::path::append(path,
+ llvm::Twine(basename) + basenameSuffix + "." + BINARY_APINOTES_EXTENSION);
+ if (const FileEntry *binaryFile = fileMgr.getFile(path))
+ return binaryFile;
+
+ // Go back to the original path.
+ path.resize(pathLen);
+
+ // Look for the source API notes file.
+ llvm::sys::path::append(path,
+ llvm::Twine(basename) + basenameSuffix + "." + SOURCE_APINOTES_EXTENSION);
+ return fileMgr.getFile(path);
+}
+
const DirectoryEntry *APINotesManager::loadFrameworkAPINotes(
llvm::StringRef FrameworkPath,
llvm::StringRef FrameworkName,
@@ -325,65 +367,88 @@
return HeaderDir;
}
-const FileEntry *APINotesManager::loadCurrentModuleAPINotes(
- StringRef moduleName,
+bool APINotesManager::loadCurrentModuleAPINotes(
+ const Module *module,
+ bool lookInModule,
ArrayRef<std::string> searchPaths) {
- assert(!CurrentModuleReader &&
+ assert(!CurrentModuleReaders[0] &&
"Already loaded API notes for the current module?");
FileManager &fileMgr = SourceMgr.getFileManager();
+ auto moduleName = module->getTopLevelModuleName();
- // Look for API notes for this module in the module search paths.
- for (const auto &searchPath : searchPaths) {
- // First, look for a binary API notes file.
- llvm::SmallString<128> apiNotesFilePath;
- apiNotesFilePath += searchPath;
- llvm::sys::path::append(
- apiNotesFilePath,
- llvm::Twine(moduleName) + "." + BINARY_APINOTES_EXTENSION);
+ // First, look relative to the module itself.
+ if (lookInModule) {
+ bool foundAny = false;
+ unsigned numReaders = 0;
- // Try to open the binary API Notes file.
- if (const FileEntry *binaryAPINotesFile
- = fileMgr.getFile(apiNotesFilePath)) {
- CurrentModuleReader = loadAPINotes(binaryAPINotesFile);
- return CurrentModuleReader ? binaryAPINotesFile : nullptr;
+ // Local function to try loading an API notes file in the given directory.
+ auto tryAPINotes = [&](const DirectoryEntry *dir, bool wantPublic) {
+ if (auto file = findAPINotesFile(dir, moduleName, wantPublic)) {
+ foundAny = true;
+
+ // Try to load the API notes file.
+ CurrentModuleReaders[numReaders] = loadAPINotes(file).release();
+ if (CurrentModuleReaders[numReaders])
+ ++numReaders;
+ }
+ };
+
+ if (module->IsFramework) {
+ // For frameworks, we search in the "APINotes" subdirectory.
+ llvm::SmallString<128> path;
+ path += module->Directory->getName();
+ llvm::sys::path::append(path, "APINotes");
+ if (auto apinotesDir = fileMgr.getDirectory(path)) {
+ tryAPINotes(apinotesDir, /*wantPublic=*/true);
+ tryAPINotes(apinotesDir, /*wantPublic=*/false);
+ }
+ } else {
+ tryAPINotes(module->Directory, /*wantPublic=*/true);
+ tryAPINotes(module->Directory, /*wantPublic=*/false);
}
- // Try to open the source API Notes file.
- apiNotesFilePath = searchPath;
- llvm::sys::path::append(
- apiNotesFilePath,
- llvm::Twine(moduleName) + "." + SOURCE_APINOTES_EXTENSION);
- if (const FileEntry *sourceAPINotesFile
- = fileMgr.getFile(apiNotesFilePath)) {
- CurrentModuleReader = loadAPINotes(sourceAPINotesFile);
- return CurrentModuleReader ? sourceAPINotesFile : nullptr;
+ if (foundAny)
+ return numReaders > 0;
+ }
+
+ // Second, look for API notes for this module in the module API
+ // notes search paths.
+ for (const auto &searchPath : searchPaths) {
+ if (auto searchDir = fileMgr.getDirectory(searchPath)) {
+ if (auto file = findAPINotesFile(searchDir, moduleName)) {
+ CurrentModuleReaders[0] = loadAPINotes(file).release();
+ return !getCurrentModuleReaders().empty();
+ }
}
}
// Didn't find any API notes.
- return nullptr;
+ return false;
}
-APINotesReader *APINotesManager::findAPINotes(SourceLocation Loc) {
- // If there is a reader for the current module, return it.
- if (CurrentModuleReader) return CurrentModuleReader.get();
+llvm::SmallVector<APINotesReader *, 2> APINotesManager::findAPINotes(SourceLocation Loc) {
+ llvm::SmallVector<APINotesReader *, 2> Results;
+
+ // If there are readers for the current module, return them.
+ if (!getCurrentModuleReaders().empty()) {
+ Results.append(getCurrentModuleReaders().begin(), getCurrentModuleReaders().end());
+ return Results;
+ }
// If we're not allowed to implicitly load API notes files, we're done.
- if (!ImplicitAPINotes) return nullptr;
+ if (!ImplicitAPINotes) return Results;
// If we don't have source location information, we're done.
- if (Loc.isInvalid()) return nullptr;
+ if (Loc.isInvalid()) return Results;
// API notes are associated with the expansion location. Retrieve the
// file for this location.
SourceLocation ExpansionLoc = SourceMgr.getExpansionLoc(Loc);
FileID ID = SourceMgr.getFileID(ExpansionLoc);
- if (ID.isInvalid())
- return nullptr;
+ if (ID.isInvalid()) return Results;
const FileEntry *File = SourceMgr.getFileEntryForID(ID);
- if (!File)
- return nullptr;
+ if (!File) return Results;
// Look for API notes in the directory corresponding to this file, or one of
// its its parent directories.
@@ -392,7 +457,6 @@
llvm::SetVector<const DirectoryEntry *,
SmallVector<const DirectoryEntry *, 4>,
llvm::SmallPtrSet<const DirectoryEntry *, 4>> DirsVisited;
- APINotesReader *Result = nullptr;
do {
// Look for an API notes reader for this header search directory.
auto Known = Readers.find(Dir);
@@ -409,7 +473,8 @@
}
// We have the answer.
- Result = Known->second.dyn_cast<APINotesReader *>();
+ if (auto Reader = Known->second.dyn_cast<APINotesReader *>())
+ Results.push_back(Reader);
break;
}
@@ -445,7 +510,8 @@
}
// Grab the result.
- Result = Readers[Dir].dyn_cast<APINotesReader *>();;
+ if (auto Reader = Readers[Dir].dyn_cast<APINotesReader *>())
+ Results.push_back(Reader);
break;
}
} else {
@@ -461,7 +527,8 @@
if (const FileEntry *APINotesFile = FileMgr.getFile(APINotesPath)) {
if (!loadAPINotes(Dir, APINotesFile)) {
++NumHeaderAPINotes;
- Result = Readers[Dir].dyn_cast<APINotesReader *>();
+ if (auto Reader = Readers[Dir].dyn_cast<APINotesReader *>())
+ Results.push_back(Reader);
break;
}
}
@@ -491,5 +558,5 @@
Readers[Visited] = Dir;
}
- return Result;
+ return Results;
}
diff --git a/lib/APINotes/APINotesReader.cpp b/lib/APINotes/APINotesReader.cpp
index 2b49125..0c93756 100644
--- a/lib/APINotes/APINotesReader.cpp
+++ b/lib/APINotes/APINotesReader.cpp
@@ -578,7 +578,10 @@
class APINotesReader::Implementation {
public:
/// The input buffer for the API notes data.
- std::unique_ptr<llvm::MemoryBuffer> InputBuffer;
+ llvm::MemoryBuffer *InputBuffer;
+
+ /// Whether we own the input buffer.
+ bool OwnsInputBuffer;
/// The reader attached to \c InputBuffer.
llvm::BitstreamReader InputReader;
@@ -586,6 +589,10 @@
/// The name of the module that we read from the control block.
std::string ModuleName;
+ // The size and modification time of the source file from
+ // which this API notes file was created, if known.
+ Optional<std::pair<off_t, time_t>> SourceFileSizeAndModTime;
+
/// Various options and attributes for the module
ModuleOptions ModuleOpts;
@@ -766,6 +773,10 @@
ModuleOpts.SwiftInferImportAsMember = (scratch.front() & 1) != 0;
break;
+ case control_block::SOURCE_FILE:
+ SourceFileSizeAndModTime = { scratch[0], scratch[1] };
+ break;
+
default:
// Unknown metadata record, possibly for use by a future version of the
// module format.
@@ -1313,14 +1324,16 @@
return false;
}
-APINotesReader::APINotesReader(std::unique_ptr<llvm::MemoryBuffer> inputBuffer,
- bool &failed)
+APINotesReader::APINotesReader(llvm::MemoryBuffer *inputBuffer,
+ bool ownsInputBuffer,
+ bool &failed)
: Impl(*new Implementation)
{
failed = false;
// Initialize the input buffer.
- Impl.InputBuffer = std::move(inputBuffer);
+ Impl.InputBuffer = inputBuffer;
+ Impl.OwnsInputBuffer = ownsInputBuffer;
Impl.InputReader.init(
reinterpret_cast<const uint8_t *>(Impl.InputBuffer->getBufferStart()),
reinterpret_cast<const uint8_t *>(Impl.InputBuffer->getBufferEnd()));
@@ -1453,6 +1466,9 @@
}
APINotesReader::~APINotesReader() {
+ if (Impl.OwnsInputBuffer)
+ delete Impl.InputBuffer;
+
delete &Impl;
}
@@ -1460,7 +1476,20 @@
APINotesReader::get(std::unique_ptr<llvm::MemoryBuffer> inputBuffer) {
bool failed = false;
std::unique_ptr<APINotesReader>
- reader(new APINotesReader(std::move(inputBuffer), failed));
+ reader(new APINotesReader(inputBuffer.release(), /*ownsInputBuffer=*/true,
+ failed));
+ if (failed)
+ return nullptr;
+
+ return reader;
+}
+
+std::unique_ptr<APINotesReader>
+APINotesReader::getUnmanaged(llvm::MemoryBuffer *inputBuffer) {
+ bool failed = false;
+ std::unique_ptr<APINotesReader>
+ reader(new APINotesReader(inputBuffer, /*ownsInputBuffer=*/false,
+ failed));
if (failed)
return nullptr;
@@ -1471,6 +1500,11 @@
return Impl.ModuleName;
}
+Optional<std::pair<off_t, time_t>>
+APINotesReader::getSourceFileSizeAndModTime() const {
+ return Impl.SourceFileSizeAndModTime;
+}
+
ModuleOptions APINotesReader::getModuleOptions() const {
return Impl.ModuleOpts;
}
diff --git a/lib/APINotes/APINotesWriter.cpp b/lib/APINotes/APINotesWriter.cpp
index b0a4650..2df5e76 100644
--- a/lib/APINotes/APINotesWriter.cpp
+++ b/lib/APINotes/APINotesWriter.cpp
@@ -15,6 +15,7 @@
//===----------------------------------------------------------------------===//
#include "clang/APINotes/APINotesWriter.h"
#include "APINotesFormat.h"
+#include "clang/Basic/FileManager.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/SmallString.h"
@@ -43,6 +44,10 @@
/// The name of the module
std::string ModuleName;
+ /// The source file from which this binary representation was
+ /// created, if known.
+ const FileEntry *SourceFile;
+
bool SwiftInferImportAsMember = false;
/// Information about Objective-C contexts (classes or protocols).
@@ -222,6 +227,12 @@
control_block::ModuleOptionsLayout moduleOptions(writer);
moduleOptions.emit(ScratchRecord, SwiftInferImportAsMember);
}
+
+ if (SourceFile) {
+ control_block::SourceFileLayout sourceFile(writer);
+ sourceFile.emit(ScratchRecord, SourceFile->getSize(),
+ SourceFile->getModificationTime());
+ }
}
namespace {
@@ -1006,10 +1017,11 @@
os.flush();
}
-APINotesWriter::APINotesWriter(StringRef moduleName)
+APINotesWriter::APINotesWriter(StringRef moduleName, const FileEntry *sourceFile)
: Impl(*new Implementation)
{
Impl.ModuleName = moduleName;
+ Impl.SourceFile = sourceFile;
}
APINotesWriter::~APINotesWriter() {
diff --git a/lib/APINotes/APINotesYAMLCompiler.cpp b/lib/APINotes/APINotesYAMLCompiler.cpp
index b964095..0d30731 100644
--- a/lib/APINotes/APINotesYAMLCompiler.cpp
+++ b/lib/APINotes/APINotesYAMLCompiler.cpp
@@ -501,6 +501,7 @@
class YAMLConverter {
const Module &TheModule;
+ const FileEntry *SourceFile;
APINotesWriter *Writer;
OSType TargetOS;
llvm::raw_ostream &OS;
@@ -519,11 +520,12 @@
public:
YAMLConverter(const Module &module,
- OSType targetOS,
- llvm::raw_ostream &os,
- llvm::SourceMgr::DiagHandlerTy diagHandler,
- void *diagHandlerCtxt) :
- TheModule(module), Writer(0), TargetOS(targetOS), OS(os),
+ const FileEntry *sourceFile,
+ OSType targetOS,
+ llvm::raw_ostream &os,
+ llvm::SourceMgr::DiagHandlerTy diagHandler,
+ void *diagHandlerCtxt) :
+ TheModule(module), SourceFile(sourceFile), Writer(0), TargetOS(targetOS), OS(os),
DiagHandler(diagHandler), DiagHandlerCtxt(diagHandlerCtxt),
ErrorOccured(false) {}
@@ -739,7 +741,7 @@
// Set up the writer.
// FIXME: This is kindof ugly.
- APINotesWriter writer(TheModule.Name);
+ APINotesWriter writer(TheModule.Name, SourceFile);
Writer = &writer;
// Write all classes.
@@ -877,13 +879,14 @@
}
static bool compile(const Module &module,
+ const FileEntry *sourceFile,
llvm::raw_ostream &os,
api_notes::OSType targetOS,
llvm::SourceMgr::DiagHandlerTy diagHandler,
void *diagHandlerCtxt){
using namespace api_notes;
- YAMLConverter c(module, targetOS, os, diagHandler, diagHandlerCtxt);
+ YAMLConverter c(module, sourceFile, targetOS, os, diagHandler, diagHandlerCtxt);
return c.convertModule();
}
@@ -905,6 +908,7 @@
}
bool api_notes::compileAPINotes(StringRef yamlInput,
+ const FileEntry *sourceFile,
llvm::raw_ostream &os,
OSType targetOS,
llvm::SourceMgr::DiagHandlerTy diagHandler,
@@ -918,7 +922,7 @@
if (parseAPINotes(yamlInput, module, diagHandler, diagHandlerCtxt))
return true;
- return compile(module, os, targetOS, diagHandler, diagHandlerCtxt);
+ return compile(module, sourceFile, os, targetOS, diagHandler, diagHandlerCtxt);
}
namespace {
diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp
index ff311b3..0cb09f1 100644
--- a/lib/Driver/Tools.cpp
+++ b/lib/Driver/Tools.cpp
@@ -5334,9 +5334,14 @@
if (Args.hasFlag(options::OPT_fapinotes, options::OPT_fno_apinotes,
false) ||
+ Args.hasFlag(options::OPT_fapinotes_modules,
+ options::OPT_fno_apinotes_modules, false) ||
Args.hasArg(options::OPT_iapinotes_modules)) {
if (Args.hasFlag(options::OPT_fapinotes, options::OPT_fno_apinotes, false))
CmdArgs.push_back("-fapinotes");
+ if (Args.hasFlag(options::OPT_fapinotes_modules,
+ options::OPT_fno_apinotes_modules, false))
+ CmdArgs.push_back("-fapinotes-modules");
SmallString<128> APINotesCachePath;
if (Arg *A = Args.getLastArg(options::OPT_fapinotes_cache_path)) {
diff --git a/lib/Frontend/CompilerInstance.cpp b/lib/Frontend/CompilerInstance.cpp
index 70f6a54..a68d87b 100644
--- a/lib/Frontend/CompilerInstance.cpp
+++ b/lib/Frontend/CompilerInstance.cpp
@@ -540,20 +540,19 @@
TheSema.reset(new Sema(getPreprocessor(), getASTContext(), getASTConsumer(),
TUKind, CompletionConsumer));
- // If we're building a module, notify the API notes manager.
- StringRef currentModuleName = getLangOpts().CurrentModule;
- if (!currentModuleName.empty()) {
+ // If we're building a module and are supposed to load API notes,
+ // notify the API notes manager.
+ if (auto currentModule = getPreprocessor().getCurrentModule()) {
(void)TheSema->APINotes.loadCurrentModuleAPINotes(
- currentModuleName,
+ currentModule,
+ getLangOpts().APINotesModules,
getAPINotesOpts().ModuleSearchPaths);
// Check for any attributes we should add to the module
- if (auto curReader = TheSema->APINotes.getCurrentModuleReader()) {
- auto currentModule = getPreprocessor().getCurrentModule();
- assert(currentModule && "how can we have a reader for it?");
-
+ for (auto reader : TheSema->APINotes.getCurrentModuleReaders()) {
// swift_infer_import_as_member
- if (curReader->getModuleOptions().SwiftInferImportAsMember) {
+ if (reader->getModuleOptions().SwiftInferImportAsMember) {
currentModule->IsSwiftInferImportAsMember = true;
+ break;
}
}
}
@@ -953,7 +952,7 @@
SourceLocation ImportLoc,
Module *Module,
StringRef ModuleFileName) {
- ModuleMap &ModMap
+ ModuleMap &ModMap
= ImportingInstance.getPreprocessor().getHeaderSearchInfo().getModuleMap();
// Construct a compiler invocation for creating this module.
diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp
index 140a4aa..bf65770 100644
--- a/lib/Frontend/CompilerInvocation.cpp
+++ b/lib/Frontend/CompilerInvocation.cpp
@@ -1969,6 +1969,7 @@
Opts.HalfArgsAndReturns = Args.hasArg(OPT_fallow_half_arguments_and_returns)
| Opts.NativeHalfArgsAndReturns;
Opts.APINotes = Args.hasArg(OPT_fapinotes);
+ Opts.APINotesModules = Args.hasArg(OPT_fapinotes_modules);
Opts.GNUAsm = !Args.hasArg(OPT_fno_gnu_inline_asm);
// __declspec is enabled by default for the PS4 by the driver, and also
@@ -2389,8 +2390,8 @@
if (Res.getFrontendOpts().ProgramAction == frontend::RewriteObjC)
LangOpts.ObjCExceptions = 1;
- // -fapinotes requires -fapinotes-cache-path=<directory>.
- if (LangOpts.APINotes &&
+ // -fapinotes and -fapinotes-modules requires -fapinotes-cache-path=<directory>.
+ if ((LangOpts.APINotes || LangOpts.APINotesModules) &&
Res.getFileSystemOpts().APINotesCachePath.empty()) {
Diags.Report(diag::err_no_apinotes_cache_path);
Success = false;
diff --git a/lib/Sema/SemaAPINotes.cpp b/lib/Sema/SemaAPINotes.cpp
index a0c5ca8..29861d1 100644
--- a/lib/Sema/SemaAPINotes.cpp
+++ b/lib/Sema/SemaAPINotes.cpp
@@ -327,8 +327,7 @@
if (D->getDeclContext()->isFileContext()) {
// Global variables.
if (auto VD = dyn_cast<VarDecl>(D)) {
- if (api_notes::APINotesReader *Reader
- = APINotes.findAPINotes(D->getLocation())) {
+ for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
if (auto Info = Reader->lookupGlobalVariable(VD->getName())) {
::ProcessAPINotes(*this, VD, *Info);
}
@@ -340,8 +339,7 @@
// Global functions.
if (auto FD = dyn_cast<FunctionDecl>(D)) {
if (FD->getDeclName().isIdentifier()) {
- if (api_notes::APINotesReader *Reader
- = APINotes.findAPINotes(D->getLocation())) {
+ for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
if (auto Info = Reader->lookupGlobalFunction(FD->getName())) {
::ProcessAPINotes(*this, FD, *Info);
}
@@ -353,8 +351,7 @@
// Objective-C classes.
if (auto Class = dyn_cast<ObjCInterfaceDecl>(D)) {
- if (api_notes::APINotesReader *Reader
- = APINotes.findAPINotes(D->getLocation())) {
+ for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
if (auto Info = Reader->lookupObjCClass(Class->getName())) {
::ProcessAPINotes(*this, Class, Info->second);
}
@@ -365,8 +362,7 @@
// Objective-C protocols.
if (auto Protocol = dyn_cast<ObjCProtocolDecl>(D)) {
- if (api_notes::APINotesReader *Reader
- = APINotes.findAPINotes(D->getLocation())) {
+ for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
if (auto Info = Reader->lookupObjCProtocol(Protocol->getName())) {
::ProcessAPINotes(*this, Protocol, Info->second);
}
@@ -377,8 +373,7 @@
// Tags
if (auto Tag = dyn_cast<TagDecl>(D)) {
- if (api_notes::APINotesReader *Reader
- = APINotes.findAPINotes(D->getLocation())) {
+ for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
if (auto Info = Reader->lookupTag(Tag->getName())) {
::ProcessAPINotes(*this, Tag, *Info);
}
@@ -389,8 +384,7 @@
// Typedefs
if (auto Typedef = dyn_cast<TypedefNameDecl>(D)) {
- if (api_notes::APINotesReader *Reader
- = APINotes.findAPINotes(D->getLocation())) {
+ for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
if (auto Info = Reader->lookupTypedef(Typedef->getName())) {
::ProcessAPINotes(*this, Typedef, *Info);
}
@@ -405,8 +399,7 @@
// Enumerators.
if (D->getDeclContext()->getRedeclContext()->isFileContext()) {
if (auto EnumConstant = dyn_cast<EnumConstantDecl>(D)) {
- if (api_notes::APINotesReader *Reader
- = APINotes.findAPINotes(D->getLocation())) {
+ for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
if (auto Info = Reader->lookupEnumConstant(EnumConstant->getName())) {
::ProcessAPINotes(*this, EnumConstant, *Info);
}
@@ -461,8 +454,7 @@
// Objective-C methods.
if (auto Method = dyn_cast<ObjCMethodDecl>(D)) {
- if (api_notes::APINotesReader *Reader
- = APINotes.findAPINotes(D->getLocation())) {
+ for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
if (auto Context = GetContext(Reader)) {
// Map the selector.
Selector Sel = Method->getSelector();
@@ -488,8 +480,7 @@
// Objective-C properties.
if (auto Property = dyn_cast<ObjCPropertyDecl>(D)) {
- if (api_notes::APINotesReader *Reader
- = APINotes.findAPINotes(D->getLocation())) {
+ for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
if (auto Context = GetContext(Reader)) {
bool isInstanceProperty =
(Property->getPropertyAttributesAsWritten() &
diff --git a/test/APINotes/Inputs/APINotes/SomeKit.apinotes b/test/APINotes/Inputs/APINotes/SomeKit.apinotes
deleted file mode 100644
index 52336df..0000000
--- a/test/APINotes/Inputs/APINotes/SomeKit.apinotes
+++ /dev/null
@@ -1,48 +0,0 @@
-Name: SomeKit
-Classes:
- - Name: A
- Methods:
- - Selector: "transform:"
- MethodKind: Instance
- Availability: none
- AvailabilityMsg: "anything but this"
- - Selector: "transform:integer:"
- MethodKind: Instance
- NullabilityOfRet: N
- Nullability: [ N, S ]
- - Selector: "privateTransform:input:"
- MethodKind: Instance
- NullabilityOfRet: N
- Nullability: [ N, S ]
- Properties:
- - Name: intValue
- Availability: none
- AvailabilityMsg: "wouldn't work anyway"
- - Name: internalProperty
- Nullability: N
- - Name: nonnullAInstance
- PropertyKind: Instance
- Nullability: N
- - Name: nonnullAClass
- PropertyKind: Class
- Nullability: N
- - Name: nonnullABoth
- Nullability: N
- - Name: B
- Availability: none
- AvailabilityMsg: "just don't"
- - Name: C
- Methods:
- - Selector: "initWithA:"
- MethodKind: Instance
- DesignatedInit: true
- - Name: ProcessInfo
- Methods:
- - Selector: "processInfo"
- MethodKind: Class
- FactoryAsInit: C
-
-Protocols:
- - Name: InternalProtocol
- Availability: none
- AvailabilityMsg: "not for you"
diff --git a/test/APINotes/Inputs/APINotes/SomeOtherKit.apinotes b/test/APINotes/Inputs/APINotes/SomeOtherKit.apinotes
new file mode 100644
index 0000000..ccdc4e1
--- /dev/null
+++ b/test/APINotes/Inputs/APINotes/SomeOtherKit.apinotes
@@ -0,0 +1,8 @@
+Name: SomeOtherKit
+Classes:
+ - Name: A
+ Methods:
+ - Selector: "methodB"
+ MethodKind: Instance
+ Availability: none
+ AvailabilityMsg: "anything but this"
diff --git a/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes b/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes
index ade66a1..e79a210 100644
--- a/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes
+++ b/test/APINotes/Inputs/Frameworks/SomeKit.framework/APINotes/SomeKit.apinotes
@@ -1,7 +1,7 @@
Name: SomeKit
Classes:
- Name: A
- Methods:
+ Methods:
- Selector: "transform:"
MethodKind: Instance
Availability: none
diff --git a/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/APINotes/SomeOtherKit.apinotes b/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/APINotes/SomeOtherKit.apinotes
new file mode 100644
index 0000000..2ad546b
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/APINotes/SomeOtherKit.apinotes
@@ -0,0 +1,8 @@
+Name: SomeOtherKit
+Classes:
+ - Name: A
+ Methods:
+ - Selector: "methodA"
+ MethodKind: Instance
+ Availability: none
+ AvailabilityMsg: "anything but this"
diff --git a/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.h b/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.h
new file mode 100644
index 0000000..3911d76
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Headers/SomeOtherKit.h
@@ -0,0 +1,9 @@
+#ifndef SOME_OTHER_KIT_H
+
+__attribute__((objc_root_class))
+@interface A
+-(void)methodA;
+-(void)methodB;
+@end
+
+#endif
diff --git a/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Modules/module.modulemap b/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Modules/module.modulemap
new file mode 100644
index 0000000..0aaad92
--- /dev/null
+++ b/test/APINotes/Inputs/Frameworks/SomeOtherKit.framework/Modules/module.modulemap
@@ -0,0 +1,5 @@
+framework module SomeOtherKit {
+ umbrella header "SomeOtherKit.h"
+ export *
+ module * { export * }
+}
diff --git a/test/APINotes/Inputs/APINotes/HeaderLib.apinotes b/test/APINotes/Inputs/Headers/HeaderLib.apinotes
similarity index 95%
rename from test/APINotes/Inputs/APINotes/HeaderLib.apinotes
rename to test/APINotes/Inputs/Headers/HeaderLib.apinotes
index 9df8c3d..f1cd086 100644
--- a/test/APINotes/Inputs/APINotes/HeaderLib.apinotes
+++ b/test/APINotes/Inputs/Headers/HeaderLib.apinotes
@@ -1,4 +1,5 @@
Name: HeaderLib
+SwiftInferImportAsMember: true
Functions:
- Name: custom_realloc
NullabilityOfRet: N
@@ -16,17 +17,15 @@
NoEscape: true
- Position: 1
NoEscape: true
-
Globals:
- Name: global_int
Nullability: N
- Name: unavailable_global_int
Availability: none
-
Tags:
- Name: unavailable_struct
Availability: none
Typedefs:
- Name: unavailable_typedef
- Availability: none
+ Availability: none
\ No newline at end of file
diff --git a/test/APINotes/availability.m b/test/APINotes/availability.m
index 1cfc658..5537316 100644
--- a/test/APINotes/availability.m
+++ b/test/APINotes/availability.m
@@ -1,5 +1,5 @@
// RUN: rm -rf %t
-// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %S/Inputs/APINotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify
#include "HeaderLib.h"
#import <SomeKit/SomeKit.h>
diff --git a/test/APINotes/cache.m b/test/APINotes/cache.m
index 6a2c2f5..b87bdf1 100644
--- a/test/APINotes/cache.m
+++ b/test/APINotes/cache.m
@@ -1,19 +1,19 @@
// RUN: rm -rf %t/APINotesCache
-// RUN: %clang_cc1 -fapinotes -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify
+// RUN: %clang_cc1 -fapinotes -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify
// Check for the presence of the cached compiled form.
// RUN: ls %t/APINotesCache | grep "APINotes-.*.apinotesc"
// RUN: ls %t/APINotesCache | grep "SomeKit-.*.apinotesc"
// Run test again to ensure that caching doesn't cause problems.
-// RUN: %clang_cc1 -fapinotes -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify
+// RUN: %clang_cc1 -fapinotes -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify
// Check that the driver provides a default -fapinotes-cache-path=
-// RUN: %clang -fsyntax-only -fapinotes -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -### 2>&1 | FileCheck --check-prefix=CHECK-DEFAULT-PATH %s
+// RUN: %clang -fsyntax-only -fapinotes -fapinotes-modules -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -### 2>&1 | FileCheck --check-prefix=CHECK-DEFAULT-PATH %s
// CHECK-DEFAULT-PATH: -fapinotes-cache-path={{.*}}org.llvm.clang/APINotesCache
// Check that the driver passes through a provided -fapinotes-cache-path=
-// RUN: %clang -fsyntax-only -fapinotes -fapinotes-cache-path=/wobble -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -### 2>&1 | FileCheck --check-prefix=CHECK-PATH %s
+// RUN: %clang -fsyntax-only -fapinotes -fapinotes-modules -fapinotes-cache-path=/wobble -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -### 2>&1 | FileCheck --check-prefix=CHECK-PATH %s
// CHECK-PATH: -fapinotes-cache-path=/wobble
#include "HeaderLib.h"
@@ -30,4 +30,3 @@
return 0;
}
-
diff --git a/test/APINotes/cache_pruning.m b/test/APINotes/cache_pruning.m
index 54b0c64..1a36570 100644
--- a/test/APINotes/cache_pruning.m
+++ b/test/APINotes/cache_pruning.m
@@ -4,7 +4,7 @@
// RUN: rm -rf %t/APINotesCache
// Run Clang. This should generated the cached versions of both and a timestamp.
-// RUN: %clang_cc1 -fapinotes -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -DINCLUDE_HEADERLIB
+// RUN: %clang_cc1 -fapinotes -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -DINCLUDE_HEADERLIB
// RUN: ls %t/APINotesCache | grep "APINotes-.*.apinotesc"
// RUN: ls %t/APINotesCache | grep "SomeKit-.*.apinotesc"
// RUN: ls %t/APINotesCache | grep "APINotes.timestamp"
@@ -12,7 +12,7 @@
// Set the timestamp back a very long time. We should try to prune,
// but nothing gets pruned because the API Notes files are new enough.
// RUN: touch -m -a -t 201101010000 %t/APINotes.timestamp
-// RUN: %clang_cc1 -fapinotes -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s
+// RUN: %clang_cc1 -fapinotes -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s
// RUN: ls %t/APINotesCache | grep "APINotes-.*.apinotesc"
// RUN: ls %t/APINotesCache | grep "SomeKit-.*.apinotesc"
// RUN: ls %t/APINotesCache | grep "APINotes.timestamp"
@@ -21,7 +21,7 @@
// This shouldn't prune anything, because the timestamp has been updated, so
// the pruning mechanism won't fire.
// RUN: find %t/APINotesCache -name APINotes-*.apinotesc | xargs touch -a -t 201101010000
-// RUN: %clang_cc1 -fapinotes -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s
+// RUN: %clang_cc1 -fapinotes -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s
// RUN: ls %t/APINotesCache | grep "APINotes-.*.apinotesc"
// RUN: ls %t/APINotesCache | grep "SomeKit-.*.apinotesc"
// RUN: ls %t/APINotesCache | grep "APINotes.timestamp"
@@ -30,13 +30,13 @@
// HeaderLib file, because the pruning mechanism should fire and
// HeaderLib is both old and not used.
// RUN: touch -m -a -t 201101010000 %t/APINotesCache/APINotes.timestamp
-// RUN: %clang_cc1 -fapinotes -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s
+// RUN: %clang_cc1 -fapinotes -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s
// RUN: ls %t/APINotesCache | not grep "APINotes-.*.apinotesc"
// RUN: ls %t/APINotesCache | grep "SomeKit-.*.apinotesc"
// RUN: ls %t/APINotesCache | grep "APINotes.timestamp"
// Run Clang. This should generated the cached versions of both and a timestamp.
-// RUN: %clang_cc1 -fapinotes -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -DINCLUDE_HEADERLIB
+// RUN: %clang_cc1 -fapinotes -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -DINCLUDE_HEADERLIB
// RUN: ls %t/APINotesCache | grep "APINotes-.*.apinotesc"
// RUN: ls %t/APINotesCache | grep "SomeKit-.*.apinotesc"
// RUN: ls %t/APINotesCache | grep "APINotes.timestamp"
diff --git a/test/APINotes/module-cache.m b/test/APINotes/module-cache.m
new file mode 100644
index 0000000..2324697
--- /dev/null
+++ b/test/APINotes/module-cache.m
@@ -0,0 +1,90 @@
+// RUN: rm -rf %t
+
+// Set up a directory with API notes
+// RUN: mkdir -p %t/APINotes
+// RUN: cp %S/Inputs/APINotes/SomeOtherKit.apinotes %t/APINotes/SomeOtherKit.apinotes
+
+// First build: check that 'methodB' is unavailable but 'methodA' is available.
+// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/APINotes -fapinotes-cache-path=%t/APINotesCache -F %S/Inputs/Frameworks %s > %t/before.log 2>&1
+// RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/before.log
+// RUN: FileCheck -check-prefix=CHECK-REBUILD %s < %t/before.log
+// RUN: FileCheck -check-prefix=CHECK-ONE-ERROR %s < %t/before.log
+
+// Do it again; now we're using caches.
+// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/APINotes -fapinotes-cache-path=%t/APINotesCache -F %S/Inputs/Frameworks %s > %t/before.log 2>&1
+// RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/before.log
+// RUN: FileCheck -check-prefix=CHECK-WITHOUT-REBUILD %s < %t/before.log
+// RUN: FileCheck -check-prefix=CHECK-ONE-ERROR %s < %t/before.log
+
+// Change the API notes file.
+// RUN: echo ' - Selector: "methodA"' >> %t/APINotes/SomeOtherKit.apinotes
+// RUN: echo ' MethodKind: Instance' >> %t/APINotes/SomeOtherKit.apinotes
+// RUN: echo ' Availability: none' >> %t/APINotes/SomeOtherKit.apinotes
+// RUN: echo ' AvailabilityMsg: "not here either"' >> %t/APINotes/SomeOtherKit.apinotes
+
+// Build again: check that both methods are now unavailable and that the module rebuilt.
+// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/APINotes -fapinotes-cache-path=%t/APINotesCache -F %S/Inputs/Frameworks %s > %t/after.log 2>&1
+// RUN: FileCheck -check-prefix=CHECK-METHODA %s < %t/after.log
+// RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/after.log
+// RUN: FileCheck -check-prefix=CHECK-REBUILD %s < %t/after.log
+// RUN: FileCheck -check-prefix=CHECK-TWO-ERRORS %s < %t/after.log
+
+// Run the build again: check that both methods are now unavailable
+// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/APINotes -fapinotes-cache-path=%t/APINotesCache -F %S/Inputs/Frameworks %s > %t/after.log 2>&1
+// RUN: FileCheck -check-prefix=CHECK-METHODA %s < %t/after.log
+// RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/after.log
+// RUN: FileCheck -check-prefix=CHECK-WITHOUT-REBUILD %s < %t/after.log
+// RUN: FileCheck -check-prefix=CHECK-TWO-ERRORS %s < %t/after.log
+
+// Set up a directory with pre-compiled API notes.
+// RUN: mkdir -p %t/CompiledAPINotes
+// RUN: rm -rf %t/ModulesCache
+// RUN: rm -rf %t/APINotesCache
+// RUN: %clang -cc1apinotes -yaml-to-binary -o %t/CompiledAPINotes/SomeOtherKit.apinotesc %S/Inputs/APINotes/SomeOtherKit.apinotes
+
+// First build: check that 'methodB' is unavailable but 'methodA' is available.
+// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/CompiledAPINotes -fapinotes-cache-path=%t/APINotesCache -F %S/Inputs/Frameworks %s > %t/compiled-before.log 2>&1
+// RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/compiled-before.log
+// RUN: FileCheck -check-prefix=CHECK-REBUILD %s < %t/compiled-before.log
+// RUN: FileCheck -check-prefix=CHECK-ONE-ERROR %s < %t/compiled-before.log
+
+// Do it again; now we're using caches.
+// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/CompiledAPINotes -fapinotes-cache-path=%t/APINotesCache -F %S/Inputs/Frameworks %s > %t/compiled-before.log 2>&1
+// RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/compiled-before.log
+// RUN: FileCheck -check-prefix=CHECK-WITHOUT-REBUILD %s < %t/compiled-before.log
+// RUN: FileCheck -check-prefix=CHECK-ONE-ERROR %s < %t/compiled-before.log
+
+// Compile a new API notes file to replace the old one.
+// RUN: %clang -cc1apinotes -yaml-to-binary -o %t/CompiledAPINotes/SomeOtherKit.apinotesc %t/APINotes/SomeOtherKit.apinotes
+
+// Build again: check that both methods are now unavailable and that the module rebuilt.
+// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/CompiledAPINotes -fapinotes-cache-path=%t/APINotesCache -F %S/Inputs/Frameworks %s > %t/compiled-after.log 2>&1
+// RUN: FileCheck -check-prefix=CHECK-METHODA %s < %t/compiled-after.log
+// RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/compiled-after.log
+// RUN: FileCheck -check-prefix=CHECK-REBUILD %s < %t/compiled-after.log
+// RUN: FileCheck -check-prefix=CHECK-TWO-ERRORS %s < %t/compiled-after.log
+
+// Run the build again: check that both methods are now unavailable
+// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -Rmodule-build -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %t/CompiledAPINotes -fapinotes-cache-path=%t/APINotesCache -F %S/Inputs/Frameworks %s > %t/compiled-after.log 2>&1
+// RUN: FileCheck -check-prefix=CHECK-METHODA %s < %t/compiled-after.log
+// RUN: FileCheck -check-prefix=CHECK-METHODB %s < %t/compiled-after.log
+// RUN: FileCheck -check-prefix=CHECK-WITHOUT-REBUILD %s < %t/compiled-after.log
+// RUN: FileCheck -check-prefix=CHECK-TWO-ERRORS %s < %t/compiled-after.log
+
+@import SomeOtherKit;
+
+void test(A *a) {
+ // CHECK-METHODA: error: 'methodA' is unavailable: not here either
+ [a methodA];
+
+ // CHECK-METHODB: error: 'methodB' is unavailable: anything but this
+ [a methodB];
+}
+
+// CHECK-REBUILD: remark: building module{{.*}}SomeOtherKit
+
+// CHECK-WITHOUT-REBUILD-NOT: remark: building module{{.*}}SomeOtherKit
+
+// CHECK-ONE-ERROR: 1 error generated.
+// CHECK-TWO-ERRORS: 2 errors generated.
+
diff --git a/test/APINotes/nullability.c b/test/APINotes/nullability.c
index 1d5939b..36507f1 100644
--- a/test/APINotes/nullability.c
+++ b/test/APINotes/nullability.c
@@ -1,5 +1,5 @@
// RUN: rm -rf %t && mkdir -p %t
-// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %S/Inputs/APINotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify
#include "HeaderLib.h"
diff --git a/test/APINotes/nullability.m b/test/APINotes/nullability.m
index 486b2c5..fc149b4 100644
--- a/test/APINotes/nullability.m
+++ b/test/APINotes/nullability.m
@@ -1,5 +1,5 @@
// RUN: rm -rf %t && mkdir -p %t
-// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %S/Inputs/APINotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify
#import <SomeKit/SomeKit.h>
@@ -17,6 +17,8 @@
[a setNonnullABoth: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}}
[A setNonnullABoth: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}}
+ [a setInternalProperty: 0]; // expected-warning{{null passed to a callee that requires a non-null argument}}
+
return 0;
}
diff --git a/test/APINotes/objc_designated_inits.m b/test/APINotes/objc_designated_inits.m
index bbb50ba..1df8cf8 100644
--- a/test/APINotes/objc_designated_inits.m
+++ b/test/APINotes/objc_designated_inits.m
@@ -1,5 +1,5 @@
// RUN: rm -rf %t && mkdir -p %t
-// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %S/Inputs/APINotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -verify
#include "HeaderLib.h"
#import <SomeKit/SomeKit.h>
diff --git a/test/APINotes/search-order.m b/test/APINotes/search-order.m
new file mode 100644
index 0000000..2c667be
--- /dev/null
+++ b/test/APINotes/search-order.m
@@ -0,0 +1,25 @@
+// RUN: rm -rf %t && mkdir -p %t
+
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -DFROM_FRAMEWORK=1 -verify
+
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -iapinotes-modules %S/Inputs/APINotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -DFROM_SEARCH_PATH=1 -verify
+
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t/ModulesCache -fapinotes-modules -iapinotes-modules %S/Inputs/APINotes -fapinotes-cache-path=%t/APINotesCache -fsyntax-only -I %S/Inputs/Headers -F %S/Inputs/Frameworks %s -DFROM_FRAMEWORK=1 -verify
+
+@import SomeOtherKit;
+
+void test(A *a) {
+#if FROM_FRAMEWORK
+ [a methodA]; // expected-error{{unavailable}}
+ [a methodB];
+
+ // expected-note@SomeOtherKit/SomeOtherKit.h:5{{'methodA' has been explicitly marked unavailable here}}
+#elif FROM_SEARCH_PATH
+ [a methodA];
+ [a methodB]; // expected-error{{unavailable}}
+
+ // expected-note@SomeOtherKit/SomeOtherKit.h:6{{'methodB' has been explicitly marked unavailable here}}
+#else
+# error Not something we need to test
+#endif
+}
diff --git a/tools/driver/apinotes_main.cpp b/tools/driver/apinotes_main.cpp
index cbd8045..e4ccc2e 100644
--- a/tools/driver/apinotes_main.cpp
+++ b/tools/driver/apinotes_main.cpp
@@ -119,7 +119,7 @@
llvm::raw_fd_ostream os(OutputFilename, EC,
llvm::sys::fs::OpenFlags::F_None);
- if (api_notes::compileAPINotes(input, os, targetOS))
+ if (api_notes::compileAPINotes(input, /*sourceFile=*/nullptr, os, targetOS))
return 1;
os.flush();