llvm-project/clang/lib/APINotes/APINotesReader.cpp
Björn Pettersson 26f5d1ee9c
[APINotes] Avoid assertion failure with expensive checks (#120487)
Found assertion failures when using EXPENSIVE_CHECKS and running lit
tests for APINotes:
Assertion `left.first != right.first && "two entries for the same
version"' failed.

It seems like std::is_sorted is verifying that the comparison function
is reflective (comp(a,a)=false) when using expensive checks. So we would
get callbacks to the lambda used for comparison, even for vectors with a
single element in APINotesReader::VersionedInfo<T>::VersionedInfo, with
"left" and "right" being the same object. Therefore the assert checking
that we never found equal values would fail.

Fix makes sure that we skip the check for equal values when "left" and
"right" is the same object.
2024-12-18 23:36:45 +01:00

2347 lines
72 KiB
C++

//===--- APINotesReader.cpp - API Notes Reader ------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file implements the \c APINotesReader class that reads source
// API notes data providing additional information about source code as
// a separate input, such as the non-nil/nilable annotations for
// method parameters.
//
//===----------------------------------------------------------------------===//
#include "clang/APINotes/APINotesReader.h"
#include "APINotesFormat.h"
#include "clang/APINotes/Types.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Bitstream/BitstreamReader.h"
#include "llvm/Support/DJB.h"
#include "llvm/Support/EndianStream.h"
#include "llvm/Support/OnDiskHashTable.h"
namespace clang {
namespace api_notes {
using namespace llvm::support;
namespace {
/// Deserialize a version tuple.
llvm::VersionTuple ReadVersionTuple(const uint8_t *&Data) {
uint8_t NumVersions = (*Data++) & 0x03;
unsigned Major = endian::readNext<uint32_t, llvm::endianness::little>(Data);
if (NumVersions == 0)
return llvm::VersionTuple(Major);
unsigned Minor = endian::readNext<uint32_t, llvm::endianness::little>(Data);
if (NumVersions == 1)
return llvm::VersionTuple(Major, Minor);
unsigned Subminor =
endian::readNext<uint32_t, llvm::endianness::little>(Data);
if (NumVersions == 2)
return llvm::VersionTuple(Major, Minor, Subminor);
unsigned Build = endian::readNext<uint32_t, llvm::endianness::little>(Data);
return llvm::VersionTuple(Major, Minor, Subminor, Build);
}
/// An on-disk hash table whose data is versioned based on the Swift version.
template <typename Derived, typename KeyType, typename UnversionedDataType>
class VersionedTableInfo {
public:
using internal_key_type = KeyType;
using external_key_type = KeyType;
using data_type =
llvm::SmallVector<std::pair<llvm::VersionTuple, UnversionedDataType>, 1>;
using hash_value_type = size_t;
using offset_type = unsigned;
internal_key_type GetInternalKey(external_key_type Key) { return Key; }
external_key_type GetExternalKey(internal_key_type Key) { return Key; }
static bool EqualKey(internal_key_type LHS, internal_key_type RHS) {
return LHS == RHS;
}
static std::pair<unsigned, unsigned> ReadKeyDataLength(const uint8_t *&Data) {
unsigned KeyLength =
endian::readNext<uint16_t, llvm::endianness::little>(Data);
unsigned DataLength =
endian::readNext<uint16_t, llvm::endianness::little>(Data);
return {KeyLength, DataLength};
}
static data_type ReadData(internal_key_type Key, const uint8_t *Data,
unsigned Length) {
unsigned NumElements =
endian::readNext<uint16_t, llvm::endianness::little>(Data);
data_type Result;
Result.reserve(NumElements);
for (unsigned i = 0; i != NumElements; ++i) {
auto version = ReadVersionTuple(Data);
const auto *DataBefore = Data;
(void)DataBefore;
auto UnversionedData = Derived::readUnversioned(Key, Data);
assert(Data != DataBefore &&
"Unversioned data reader didn't move pointer");
Result.push_back({version, UnversionedData});
}
return Result;
}
};
/// Read serialized CommonEntityInfo.
void ReadCommonEntityInfo(const uint8_t *&Data, CommonEntityInfo &Info) {
uint8_t UnavailableBits = *Data++;
Info.Unavailable = (UnavailableBits >> 1) & 0x01;
Info.UnavailableInSwift = UnavailableBits & 0x01;
if ((UnavailableBits >> 2) & 0x01)
Info.setSwiftPrivate(static_cast<bool>((UnavailableBits >> 3) & 0x01));
unsigned MsgLength =
endian::readNext<uint16_t, llvm::endianness::little>(Data);
Info.UnavailableMsg =
std::string(reinterpret_cast<const char *>(Data),
reinterpret_cast<const char *>(Data) + MsgLength);
Data += MsgLength;
unsigned SwiftNameLength =
endian::readNext<uint16_t, llvm::endianness::little>(Data);
Info.SwiftName =
std::string(reinterpret_cast<const char *>(Data),
reinterpret_cast<const char *>(Data) + SwiftNameLength);
Data += SwiftNameLength;
}
/// Read serialized CommonTypeInfo.
void ReadCommonTypeInfo(const uint8_t *&Data, CommonTypeInfo &Info) {
ReadCommonEntityInfo(Data, Info);
unsigned SwiftBridgeLength =
endian::readNext<uint16_t, llvm::endianness::little>(Data);
if (SwiftBridgeLength > 0) {
Info.setSwiftBridge(std::string(reinterpret_cast<const char *>(Data),
SwiftBridgeLength - 1));
Data += SwiftBridgeLength - 1;
}
unsigned ErrorDomainLength =
endian::readNext<uint16_t, llvm::endianness::little>(Data);
if (ErrorDomainLength > 0) {
Info.setNSErrorDomain(std::optional<std::string>(std::string(
reinterpret_cast<const char *>(Data), ErrorDomainLength - 1)));
Data += ErrorDomainLength - 1;
}
}
/// Used to deserialize the on-disk identifier table.
class IdentifierTableInfo {
public:
using internal_key_type = llvm::StringRef;
using external_key_type = llvm::StringRef;
using data_type = IdentifierID;
using hash_value_type = uint32_t;
using offset_type = unsigned;
internal_key_type GetInternalKey(external_key_type Key) { return Key; }
external_key_type GetExternalKey(internal_key_type Key) { return Key; }
hash_value_type ComputeHash(internal_key_type Key) {
return llvm::djbHash(Key);
}
static bool EqualKey(internal_key_type LHS, internal_key_type RHS) {
return LHS == RHS;
}
static std::pair<unsigned, unsigned> ReadKeyDataLength(const uint8_t *&Data) {
unsigned KeyLength =
endian::readNext<uint16_t, llvm::endianness::little>(Data);
unsigned DataLength =
endian::readNext<uint16_t, llvm::endianness::little>(Data);
return {KeyLength, DataLength};
}
static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) {
return llvm::StringRef(reinterpret_cast<const char *>(Data), Length);
}
static data_type ReadData(internal_key_type key, const uint8_t *Data,
unsigned Length) {
return endian::readNext<uint32_t, llvm::endianness::little>(Data);
}
};
/// Used to deserialize the on-disk table of Objective-C classes and C++
/// namespaces.
class ContextIDTableInfo {
public:
using internal_key_type = ContextTableKey;
using external_key_type = internal_key_type;
using data_type = unsigned;
using hash_value_type = size_t;
using offset_type = unsigned;
internal_key_type GetInternalKey(external_key_type Key) { return Key; }
external_key_type GetExternalKey(internal_key_type Key) { return Key; }
hash_value_type ComputeHash(internal_key_type Key) {
return static_cast<size_t>(Key.hashValue());
}
static bool EqualKey(internal_key_type LHS, internal_key_type RHS) {
return LHS == RHS;
}
static std::pair<unsigned, unsigned> ReadKeyDataLength(const uint8_t *&Data) {
unsigned KeyLength =
endian::readNext<uint16_t, llvm::endianness::little>(Data);
unsigned DataLength =
endian::readNext<uint16_t, llvm::endianness::little>(Data);
return {KeyLength, DataLength};
}
static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) {
auto ParentCtxID =
endian::readNext<uint32_t, llvm::endianness::little>(Data);
auto ContextKind =
endian::readNext<uint8_t, llvm::endianness::little>(Data);
auto NameID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
return {ParentCtxID, ContextKind, NameID};
}
static data_type ReadData(internal_key_type Key, const uint8_t *Data,
unsigned Length) {
return endian::readNext<uint32_t, llvm::endianness::little>(Data);
}
};
/// Used to deserialize the on-disk Objective-C property table.
class ContextInfoTableInfo
: public VersionedTableInfo<ContextInfoTableInfo, unsigned, ContextInfo> {
public:
static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) {
return endian::readNext<uint32_t, llvm::endianness::little>(Data);
}
hash_value_type ComputeHash(internal_key_type Key) {
return static_cast<size_t>(llvm::hash_value(Key));
}
static ContextInfo readUnversioned(internal_key_type Key,
const uint8_t *&Data) {
ContextInfo Info;
ReadCommonTypeInfo(Data, Info);
uint8_t Payload = *Data++;
if (Payload & 0x01)
Info.setHasDesignatedInits(true);
Payload = Payload >> 1;
if (Payload & 0x4)
Info.setDefaultNullability(static_cast<NullabilityKind>(Payload & 0x03));
Payload >>= 3;
if (Payload & (1 << 1))
Info.setSwiftObjCMembers(Payload & 1);
Payload >>= 2;
if (Payload & (1 << 1))
Info.setSwiftImportAsNonGeneric(Payload & 1);
return Info;
}
};
/// Read serialized VariableInfo.
void ReadVariableInfo(const uint8_t *&Data, VariableInfo &Info) {
ReadCommonEntityInfo(Data, Info);
if (*Data++) {
Info.setNullabilityAudited(static_cast<NullabilityKind>(*Data));
}
++Data;
auto TypeLen = endian::readNext<uint16_t, llvm::endianness::little>(Data);
Info.setType(std::string(Data, Data + TypeLen));
Data += TypeLen;
}
/// Used to deserialize the on-disk Objective-C property table.
class ObjCPropertyTableInfo
: public VersionedTableInfo<ObjCPropertyTableInfo,
std::tuple<uint32_t, uint32_t, uint8_t>,
ObjCPropertyInfo> {
public:
static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) {
auto ClassID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
auto NameID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
char IsInstance = endian::readNext<uint8_t, llvm::endianness::little>(Data);
return {ClassID, NameID, IsInstance};
}
hash_value_type ComputeHash(internal_key_type Key) {
return static_cast<size_t>(llvm::hash_value(Key));
}
static ObjCPropertyInfo readUnversioned(internal_key_type Key,
const uint8_t *&Data) {
ObjCPropertyInfo Info;
ReadVariableInfo(Data, Info);
uint8_t Flags = *Data++;
if (Flags & (1 << 0))
Info.setSwiftImportAsAccessors(Flags & (1 << 1));
return Info;
}
};
/// Used to deserialize the on-disk C record field table.
class FieldTableInfo
: public VersionedTableInfo<FieldTableInfo, SingleDeclTableKey, FieldInfo> {
public:
static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) {
auto CtxID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
auto NameID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
return {CtxID, NameID};
}
hash_value_type ComputeHash(internal_key_type Key) {
return static_cast<size_t>(Key.hashValue());
}
static FieldInfo readUnversioned(internal_key_type Key,
const uint8_t *&Data) {
FieldInfo Info;
ReadVariableInfo(Data, Info);
return Info;
}
};
/// Read serialized ParamInfo.
void ReadParamInfo(const uint8_t *&Data, ParamInfo &Info) {
ReadVariableInfo(Data, Info);
uint8_t Payload = endian::readNext<uint8_t, llvm::endianness::little>(Data);
if (auto RawConvention = Payload & 0x7) {
auto Convention = static_cast<RetainCountConventionKind>(RawConvention - 1);
Info.setRetainCountConvention(Convention);
}
Payload >>= 3;
if (Payload & 0x01)
Info.setLifetimebound(Payload & 0x02);
Payload >>= 2;
if (Payload & 0x01)
Info.setNoEscape(Payload & 0x02);
Payload >>= 2;
assert(Payload == 0 && "Bad API notes");
}
/// Read serialized FunctionInfo.
void ReadFunctionInfo(const uint8_t *&Data, FunctionInfo &Info) {
ReadCommonEntityInfo(Data, Info);
uint8_t Payload = endian::readNext<uint8_t, llvm::endianness::little>(Data);
if (auto RawConvention = Payload & 0x7) {
auto Convention = static_cast<RetainCountConventionKind>(RawConvention - 1);
Info.setRetainCountConvention(Convention);
}
Payload >>= 3;
Info.NullabilityAudited = Payload & 0x1;
Payload >>= 1;
assert(Payload == 0 && "Bad API notes");
Info.NumAdjustedNullable =
endian::readNext<uint8_t, llvm::endianness::little>(Data);
Info.NullabilityPayload =
endian::readNext<uint64_t, llvm::endianness::little>(Data);
unsigned NumParams =
endian::readNext<uint16_t, llvm::endianness::little>(Data);
while (NumParams > 0) {
ParamInfo pi;
ReadParamInfo(Data, pi);
Info.Params.push_back(pi);
--NumParams;
}
unsigned ResultTypeLen =
endian::readNext<uint16_t, llvm::endianness::little>(Data);
Info.ResultType = std::string(Data, Data + ResultTypeLen);
Data += ResultTypeLen;
unsigned SwiftReturnOwnershipLength =
endian::readNext<uint16_t, llvm::endianness::little>(Data);
Info.SwiftReturnOwnership = std::string(reinterpret_cast<const char *>(Data),
reinterpret_cast<const char *>(Data) +
SwiftReturnOwnershipLength);
Data += SwiftReturnOwnershipLength;
}
/// Used to deserialize the on-disk Objective-C method table.
class ObjCMethodTableInfo
: public VersionedTableInfo<ObjCMethodTableInfo,
std::tuple<uint32_t, uint32_t, uint8_t>,
ObjCMethodInfo> {
public:
static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) {
auto ClassID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
auto SelectorID =
endian::readNext<uint32_t, llvm::endianness::little>(Data);
auto IsInstance = endian::readNext<uint8_t, llvm::endianness::little>(Data);
return {ClassID, SelectorID, IsInstance};
}
hash_value_type ComputeHash(internal_key_type Key) {
return static_cast<size_t>(llvm::hash_value(Key));
}
static ObjCMethodInfo readUnversioned(internal_key_type Key,
const uint8_t *&Data) {
ObjCMethodInfo Info;
uint8_t Payload = *Data++;
bool HasSelf = Payload & 0x01;
Payload >>= 1;
Info.RequiredInit = Payload & 0x01;
Payload >>= 1;
Info.DesignatedInit = Payload & 0x01;
Payload >>= 1;
assert(Payload == 0 && "Unable to fully decode 'Payload'.");
ReadFunctionInfo(Data, Info);
if (HasSelf) {
Info.Self = ParamInfo{};
ReadParamInfo(Data, *Info.Self);
}
return Info;
}
};
/// Used to deserialize the on-disk Objective-C selector table.
class ObjCSelectorTableInfo {
public:
using internal_key_type = StoredObjCSelector;
using external_key_type = internal_key_type;
using data_type = SelectorID;
using hash_value_type = unsigned;
using offset_type = unsigned;
internal_key_type GetInternalKey(external_key_type Key) { return Key; }
external_key_type GetExternalKey(internal_key_type Key) { return Key; }
hash_value_type ComputeHash(internal_key_type Key) {
return llvm::DenseMapInfo<StoredObjCSelector>::getHashValue(Key);
}
static bool EqualKey(internal_key_type LHS, internal_key_type RHS) {
return llvm::DenseMapInfo<StoredObjCSelector>::isEqual(LHS, RHS);
}
static std::pair<unsigned, unsigned> ReadKeyDataLength(const uint8_t *&Data) {
unsigned KeyLength =
endian::readNext<uint16_t, llvm::endianness::little>(Data);
unsigned DataLength =
endian::readNext<uint16_t, llvm::endianness::little>(Data);
return {KeyLength, DataLength};
}
static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) {
internal_key_type Key;
Key.NumArgs = endian::readNext<uint16_t, llvm::endianness::little>(Data);
unsigned NumIdents = (Length - sizeof(uint16_t)) / sizeof(uint32_t);
for (unsigned i = 0; i != NumIdents; ++i) {
Key.Identifiers.push_back(
endian::readNext<uint32_t, llvm::endianness::little>(Data));
}
return Key;
}
static data_type ReadData(internal_key_type Key, const uint8_t *Data,
unsigned Length) {
return endian::readNext<uint32_t, llvm::endianness::little>(Data);
}
};
/// Used to deserialize the on-disk global variable table.
class GlobalVariableTableInfo
: public VersionedTableInfo<GlobalVariableTableInfo, SingleDeclTableKey,
GlobalVariableInfo> {
public:
static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) {
auto CtxID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
auto NameID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
return {CtxID, NameID};
}
hash_value_type ComputeHash(internal_key_type Key) {
return static_cast<size_t>(Key.hashValue());
}
static GlobalVariableInfo readUnversioned(internal_key_type Key,
const uint8_t *&Data) {
GlobalVariableInfo Info;
ReadVariableInfo(Data, Info);
return Info;
}
};
/// Used to deserialize the on-disk global function table.
class GlobalFunctionTableInfo
: public VersionedTableInfo<GlobalFunctionTableInfo, SingleDeclTableKey,
GlobalFunctionInfo> {
public:
static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) {
auto CtxID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
auto NameID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
return {CtxID, NameID};
}
hash_value_type ComputeHash(internal_key_type Key) {
return static_cast<size_t>(Key.hashValue());
}
static GlobalFunctionInfo readUnversioned(internal_key_type Key,
const uint8_t *&Data) {
GlobalFunctionInfo Info;
ReadFunctionInfo(Data, Info);
return Info;
}
};
/// Used to deserialize the on-disk C++ method table.
class CXXMethodTableInfo
: public VersionedTableInfo<CXXMethodTableInfo, SingleDeclTableKey,
CXXMethodInfo> {
public:
static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) {
auto CtxID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
auto NameID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
return {CtxID, NameID};
}
hash_value_type ComputeHash(internal_key_type Key) {
return static_cast<size_t>(Key.hashValue());
}
static CXXMethodInfo readUnversioned(internal_key_type Key,
const uint8_t *&Data) {
CXXMethodInfo Info;
uint8_t Payload = *Data++;
bool HasThis = Payload & 0x01;
Payload >>= 1;
assert(Payload == 0 && "Unable to fully decode 'Payload'.");
ReadFunctionInfo(Data, Info);
if (HasThis) {
Info.This = ParamInfo{};
ReadParamInfo(Data, *Info.This);
}
return Info;
}
};
/// Used to deserialize the on-disk enumerator table.
class EnumConstantTableInfo
: public VersionedTableInfo<EnumConstantTableInfo, uint32_t,
EnumConstantInfo> {
public:
static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) {
auto NameID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
return NameID;
}
hash_value_type ComputeHash(internal_key_type Key) {
return static_cast<size_t>(llvm::hash_value(Key));
}
static EnumConstantInfo readUnversioned(internal_key_type Key,
const uint8_t *&Data) {
EnumConstantInfo Info;
ReadCommonEntityInfo(Data, Info);
return Info;
}
};
/// Used to deserialize the on-disk tag table.
class TagTableInfo
: public VersionedTableInfo<TagTableInfo, SingleDeclTableKey, TagInfo> {
public:
static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) {
auto CtxID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
auto NameID =
endian::readNext<IdentifierID, llvm::endianness::little>(Data);
return {CtxID, NameID};
}
hash_value_type ComputeHash(internal_key_type Key) {
return static_cast<size_t>(Key.hashValue());
}
static TagInfo readUnversioned(internal_key_type Key, const uint8_t *&Data) {
TagInfo Info;
uint8_t Payload = *Data++;
if (Payload & 1)
Info.setFlagEnum(Payload & 2);
Payload >>= 2;
if (Payload > 0)
Info.EnumExtensibility =
static_cast<EnumExtensibilityKind>((Payload & 0x3) - 1);
uint8_t Copyable =
endian::readNext<uint8_t, llvm::endianness::little>(Data);
if (Copyable == kSwiftConforms || Copyable == kSwiftDoesNotConform)
Info.setSwiftCopyable(std::optional(Copyable == kSwiftConforms));
uint8_t Escapable =
endian::readNext<uint8_t, llvm::endianness::little>(Data);
if (Escapable == kSwiftConforms || Escapable == kSwiftDoesNotConform)
Info.setSwiftEscapable(std::optional(Escapable == kSwiftConforms));
unsigned ImportAsLength =
endian::readNext<uint16_t, llvm::endianness::little>(Data);
if (ImportAsLength > 0) {
Info.SwiftImportAs =
std::string(reinterpret_cast<const char *>(Data), ImportAsLength - 1);
Data += ImportAsLength - 1;
}
unsigned RetainOpLength =
endian::readNext<uint16_t, llvm::endianness::little>(Data);
if (RetainOpLength > 0) {
Info.SwiftRetainOp =
std::string(reinterpret_cast<const char *>(Data), RetainOpLength - 1);
Data += RetainOpLength - 1;
}
unsigned ReleaseOpLength =
endian::readNext<uint16_t, llvm::endianness::little>(Data);
if (ReleaseOpLength > 0) {
Info.SwiftReleaseOp = std::string(reinterpret_cast<const char *>(Data),
ReleaseOpLength - 1);
Data += ReleaseOpLength - 1;
}
if (unsigned ConformanceLength =
endian::readNext<uint16_t, llvm::endianness::little>(Data)) {
Info.SwiftConformance = std::string(reinterpret_cast<const char *>(Data),
ConformanceLength - 1);
Data += ConformanceLength - 1;
}
ReadCommonTypeInfo(Data, Info);
return Info;
}
};
/// Used to deserialize the on-disk typedef table.
class TypedefTableInfo
: public VersionedTableInfo<TypedefTableInfo, SingleDeclTableKey,
TypedefInfo> {
public:
static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) {
auto CtxID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
auto nameID =
endian::readNext<IdentifierID, llvm::endianness::little>(Data);
return {CtxID, nameID};
}
hash_value_type ComputeHash(internal_key_type Key) {
return static_cast<size_t>(Key.hashValue());
}
static TypedefInfo readUnversioned(internal_key_type Key,
const uint8_t *&Data) {
TypedefInfo Info;
uint8_t Payload = *Data++;
if (Payload > 0)
Info.SwiftWrapper = static_cast<SwiftNewTypeKind>((Payload & 0x3) - 1);
ReadCommonTypeInfo(Data, Info);
return Info;
}
};
} // end anonymous namespace
class APINotesReader::Implementation {
public:
/// The input buffer for the API notes data.
llvm::MemoryBuffer *InputBuffer;
/// The Swift version to use for filtering.
llvm::VersionTuple SwiftVersion;
/// 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.
std::optional<std::pair<off_t, time_t>> SourceFileSizeAndModTime;
using SerializedIdentifierTable =
llvm::OnDiskIterableChainedHashTable<IdentifierTableInfo>;
/// The identifier table.
std::unique_ptr<SerializedIdentifierTable> IdentifierTable;
using SerializedContextIDTable =
llvm::OnDiskIterableChainedHashTable<ContextIDTableInfo>;
/// The Objective-C / C++ context ID table.
std::unique_ptr<SerializedContextIDTable> ContextIDTable;
using SerializedContextInfoTable =
llvm::OnDiskIterableChainedHashTable<ContextInfoTableInfo>;
/// The Objective-C context info table.
std::unique_ptr<SerializedContextInfoTable> ContextInfoTable;
using SerializedObjCPropertyTable =
llvm::OnDiskIterableChainedHashTable<ObjCPropertyTableInfo>;
/// The Objective-C property table.
std::unique_ptr<SerializedObjCPropertyTable> ObjCPropertyTable;
using SerializedFieldTable =
llvm::OnDiskIterableChainedHashTable<FieldTableInfo>;
/// The C record field table.
std::unique_ptr<SerializedFieldTable> FieldTable;
using SerializedObjCMethodTable =
llvm::OnDiskIterableChainedHashTable<ObjCMethodTableInfo>;
/// The Objective-C method table.
std::unique_ptr<SerializedObjCMethodTable> ObjCMethodTable;
using SerializedCXXMethodTable =
llvm::OnDiskIterableChainedHashTable<CXXMethodTableInfo>;
/// The C++ method table.
std::unique_ptr<SerializedCXXMethodTable> CXXMethodTable;
using SerializedObjCSelectorTable =
llvm::OnDiskIterableChainedHashTable<ObjCSelectorTableInfo>;
/// The Objective-C selector table.
std::unique_ptr<SerializedObjCSelectorTable> ObjCSelectorTable;
using SerializedGlobalVariableTable =
llvm::OnDiskIterableChainedHashTable<GlobalVariableTableInfo>;
/// The global variable table.
std::unique_ptr<SerializedGlobalVariableTable> GlobalVariableTable;
using SerializedGlobalFunctionTable =
llvm::OnDiskIterableChainedHashTable<GlobalFunctionTableInfo>;
/// The global function table.
std::unique_ptr<SerializedGlobalFunctionTable> GlobalFunctionTable;
using SerializedEnumConstantTable =
llvm::OnDiskIterableChainedHashTable<EnumConstantTableInfo>;
/// The enumerator table.
std::unique_ptr<SerializedEnumConstantTable> EnumConstantTable;
using SerializedTagTable = llvm::OnDiskIterableChainedHashTable<TagTableInfo>;
/// The tag table.
std::unique_ptr<SerializedTagTable> TagTable;
using SerializedTypedefTable =
llvm::OnDiskIterableChainedHashTable<TypedefTableInfo>;
/// The typedef table.
std::unique_ptr<SerializedTypedefTable> TypedefTable;
/// Retrieve the identifier ID for the given string, or an empty
/// optional if the string is unknown.
std::optional<IdentifierID> getIdentifier(llvm::StringRef Str);
/// Retrieve the selector ID for the given selector, or an empty
/// optional if the string is unknown.
std::optional<SelectorID> getSelector(ObjCSelectorRef Selector);
bool readControlBlock(llvm::BitstreamCursor &Cursor,
llvm::SmallVectorImpl<uint64_t> &Scratch);
bool readIdentifierBlock(llvm::BitstreamCursor &Cursor,
llvm::SmallVectorImpl<uint64_t> &Scratch);
bool readContextBlock(llvm::BitstreamCursor &Cursor,
llvm::SmallVectorImpl<uint64_t> &Scratch);
bool readObjCPropertyBlock(llvm::BitstreamCursor &Cursor,
llvm::SmallVectorImpl<uint64_t> &Scratch);
bool readObjCMethodBlock(llvm::BitstreamCursor &Cursor,
llvm::SmallVectorImpl<uint64_t> &Scratch);
bool readCXXMethodBlock(llvm::BitstreamCursor &Cursor,
llvm::SmallVectorImpl<uint64_t> &Scratch);
bool readFieldBlock(llvm::BitstreamCursor &Cursor,
llvm::SmallVectorImpl<uint64_t> &Scratch);
bool readObjCSelectorBlock(llvm::BitstreamCursor &Cursor,
llvm::SmallVectorImpl<uint64_t> &Scratch);
bool readGlobalVariableBlock(llvm::BitstreamCursor &Cursor,
llvm::SmallVectorImpl<uint64_t> &Scratch);
bool readGlobalFunctionBlock(llvm::BitstreamCursor &Cursor,
llvm::SmallVectorImpl<uint64_t> &Scratch);
bool readEnumConstantBlock(llvm::BitstreamCursor &Cursor,
llvm::SmallVectorImpl<uint64_t> &Scratch);
bool readTagBlock(llvm::BitstreamCursor &Cursor,
llvm::SmallVectorImpl<uint64_t> &Scratch);
bool readTypedefBlock(llvm::BitstreamCursor &Cursor,
llvm::SmallVectorImpl<uint64_t> &Scratch);
};
std::optional<IdentifierID>
APINotesReader::Implementation::getIdentifier(llvm::StringRef Str) {
if (!IdentifierTable)
return std::nullopt;
if (Str.empty())
return IdentifierID(0);
auto Known = IdentifierTable->find(Str);
if (Known == IdentifierTable->end())
return std::nullopt;
return *Known;
}
std::optional<SelectorID>
APINotesReader::Implementation::getSelector(ObjCSelectorRef Selector) {
if (!ObjCSelectorTable || !IdentifierTable)
return std::nullopt;
// Translate the identifiers.
StoredObjCSelector Key;
Key.NumArgs = Selector.NumArgs;
for (auto Ident : Selector.Identifiers) {
if (auto IdentID = getIdentifier(Ident)) {
Key.Identifiers.push_back(*IdentID);
} else {
return std::nullopt;
}
}
auto Known = ObjCSelectorTable->find(Key);
if (Known == ObjCSelectorTable->end())
return std::nullopt;
return *Known;
}
bool APINotesReader::Implementation::readControlBlock(
llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
if (Cursor.EnterSubBlock(CONTROL_BLOCK_ID))
return true;
bool SawMetadata = false;
llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
if (!MaybeNext) {
// FIXME this drops the error on the floor.
consumeError(MaybeNext.takeError());
return false;
}
llvm::BitstreamEntry Next = MaybeNext.get();
while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
if (Next.Kind == llvm::BitstreamEntry::Error)
return true;
if (Next.Kind == llvm::BitstreamEntry::SubBlock) {
// Unknown metadata sub-block, possibly for use by a future version of the
// API notes format.
if (Cursor.SkipBlock())
return true;
MaybeNext = Cursor.advance();
if (!MaybeNext) {
// FIXME this drops the error on the floor.
consumeError(MaybeNext.takeError());
return false;
}
Next = MaybeNext.get();
continue;
}
Scratch.clear();
llvm::StringRef BlobData;
llvm::Expected<unsigned> MaybeKind =
Cursor.readRecord(Next.ID, Scratch, &BlobData);
if (!MaybeKind) {
// FIXME this drops the error on the floor.
consumeError(MaybeKind.takeError());
return false;
}
unsigned Kind = MaybeKind.get();
switch (Kind) {
case control_block::METADATA:
// Already saw metadata.
if (SawMetadata)
return true;
if (Scratch[0] != VERSION_MAJOR || Scratch[1] != VERSION_MINOR)
return true;
SawMetadata = true;
break;
case control_block::MODULE_NAME:
ModuleName = BlobData.str();
break;
case control_block::MODULE_OPTIONS:
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.
break;
}
MaybeNext = Cursor.advance();
if (!MaybeNext) {
// FIXME this drops the error on the floor.
consumeError(MaybeNext.takeError());
return false;
}
Next = MaybeNext.get();
}
return !SawMetadata;
}
bool APINotesReader::Implementation::readIdentifierBlock(
llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
if (Cursor.EnterSubBlock(IDENTIFIER_BLOCK_ID))
return true;
llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
if (!MaybeNext) {
// FIXME this drops the error on the floor.
consumeError(MaybeNext.takeError());
return false;
}
llvm::BitstreamEntry Next = MaybeNext.get();
while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
if (Next.Kind == llvm::BitstreamEntry::Error)
return true;
if (Next.Kind == llvm::BitstreamEntry::SubBlock) {
// Unknown sub-block, possibly for use by a future version of the
// API notes format.
if (Cursor.SkipBlock())
return true;
MaybeNext = Cursor.advance();
if (!MaybeNext) {
// FIXME this drops the error on the floor.
consumeError(MaybeNext.takeError());
return false;
}
Next = MaybeNext.get();
continue;
}
Scratch.clear();
llvm::StringRef BlobData;
llvm::Expected<unsigned> MaybeKind =
Cursor.readRecord(Next.ID, Scratch, &BlobData);
if (!MaybeKind) {
// FIXME this drops the error on the floor.
consumeError(MaybeKind.takeError());
return false;
}
unsigned Kind = MaybeKind.get();
switch (Kind) {
case identifier_block::IDENTIFIER_DATA: {
// Already saw identifier table.
if (IdentifierTable)
return true;
uint32_t tableOffset;
identifier_block::IdentifierDataLayout::readRecord(Scratch, tableOffset);
auto base = reinterpret_cast<const uint8_t *>(BlobData.data());
IdentifierTable.reset(SerializedIdentifierTable::Create(
base + tableOffset, base + sizeof(uint32_t), base));
break;
}
default:
// Unknown record, possibly for use by a future version of the
// module format.
break;
}
MaybeNext = Cursor.advance();
if (!MaybeNext) {
// FIXME this drops the error on the floor.
consumeError(MaybeNext.takeError());
return false;
}
Next = MaybeNext.get();
}
return false;
}
bool APINotesReader::Implementation::readContextBlock(
llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
if (Cursor.EnterSubBlock(OBJC_CONTEXT_BLOCK_ID))
return true;
llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
if (!MaybeNext) {
// FIXME this drops the error on the floor.
consumeError(MaybeNext.takeError());
return false;
}
llvm::BitstreamEntry Next = MaybeNext.get();
while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
if (Next.Kind == llvm::BitstreamEntry::Error)
return true;
if (Next.Kind == llvm::BitstreamEntry::SubBlock) {
// Unknown sub-block, possibly for use by a future version of the
// API notes format.
if (Cursor.SkipBlock())
return true;
MaybeNext = Cursor.advance();
if (!MaybeNext) {
// FIXME this drops the error on the floor.
consumeError(MaybeNext.takeError());
return false;
}
Next = MaybeNext.get();
continue;
}
Scratch.clear();
llvm::StringRef BlobData;
llvm::Expected<unsigned> MaybeKind =
Cursor.readRecord(Next.ID, Scratch, &BlobData);
if (!MaybeKind) {
// FIXME this drops the error on the floor.
consumeError(MaybeKind.takeError());
return false;
}
unsigned Kind = MaybeKind.get();
switch (Kind) {
case context_block::CONTEXT_ID_DATA: {
// Already saw Objective-C / C++ context ID table.
if (ContextIDTable)
return true;
uint32_t tableOffset;
context_block::ContextIDLayout::readRecord(Scratch, tableOffset);
auto base = reinterpret_cast<const uint8_t *>(BlobData.data());
ContextIDTable.reset(SerializedContextIDTable::Create(
base + tableOffset, base + sizeof(uint32_t), base));
break;
}
case context_block::CONTEXT_INFO_DATA: {
// Already saw Objective-C / C++ context info table.
if (ContextInfoTable)
return true;
uint32_t tableOffset;
context_block::ContextInfoLayout::readRecord(Scratch, tableOffset);
auto base = reinterpret_cast<const uint8_t *>(BlobData.data());
ContextInfoTable.reset(SerializedContextInfoTable::Create(
base + tableOffset, base + sizeof(uint32_t), base));
break;
}
default:
// Unknown record, possibly for use by a future version of the
// module format.
break;
}
MaybeNext = Cursor.advance();
if (!MaybeNext) {
// FIXME this drops the error on the floor.
consumeError(MaybeNext.takeError());
return false;
}
Next = MaybeNext.get();
}
return false;
}
bool APINotesReader::Implementation::readObjCPropertyBlock(
llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
if (Cursor.EnterSubBlock(OBJC_PROPERTY_BLOCK_ID))
return true;
llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
if (!MaybeNext) {
// FIXME this drops the error on the floor.
consumeError(MaybeNext.takeError());
return false;
}
llvm::BitstreamEntry Next = MaybeNext.get();
while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
if (Next.Kind == llvm::BitstreamEntry::Error)
return true;
if (Next.Kind == llvm::BitstreamEntry::SubBlock) {
// Unknown sub-block, possibly for use by a future version of the
// API notes format.
if (Cursor.SkipBlock())
return true;
MaybeNext = Cursor.advance();
if (!MaybeNext) {
// FIXME this drops the error on the floor.
consumeError(MaybeNext.takeError());
return false;
}
Next = MaybeNext.get();
continue;
}
Scratch.clear();
llvm::StringRef BlobData;
llvm::Expected<unsigned> MaybeKind =
Cursor.readRecord(Next.ID, Scratch, &BlobData);
if (!MaybeKind) {
// FIXME this drops the error on the floor.
consumeError(MaybeKind.takeError());
return false;
}
unsigned Kind = MaybeKind.get();
switch (Kind) {
case objc_property_block::OBJC_PROPERTY_DATA: {
// Already saw Objective-C property table.
if (ObjCPropertyTable)
return true;
uint32_t tableOffset;
objc_property_block::ObjCPropertyDataLayout::readRecord(Scratch,
tableOffset);
auto base = reinterpret_cast<const uint8_t *>(BlobData.data());
ObjCPropertyTable.reset(SerializedObjCPropertyTable::Create(
base + tableOffset, base + sizeof(uint32_t), base));
break;
}
default:
// Unknown record, possibly for use by a future version of the
// module format.
break;
}
MaybeNext = Cursor.advance();
if (!MaybeNext) {
// FIXME this drops the error on the floor.
consumeError(MaybeNext.takeError());
return false;
}
Next = MaybeNext.get();
}
return false;
}
bool APINotesReader::Implementation::readObjCMethodBlock(
llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
if (Cursor.EnterSubBlock(OBJC_METHOD_BLOCK_ID))
return true;
llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
if (!MaybeNext) {
// FIXME this drops the error on the floor.
consumeError(MaybeNext.takeError());
return false;
}
llvm::BitstreamEntry Next = MaybeNext.get();
while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
if (Next.Kind == llvm::BitstreamEntry::Error)
return true;
if (Next.Kind == llvm::BitstreamEntry::SubBlock) {
// Unknown sub-block, possibly for use by a future version of the
// API notes format.
if (Cursor.SkipBlock())
return true;
MaybeNext = Cursor.advance();
if (!MaybeNext) {
// FIXME this drops the error on the floor.
consumeError(MaybeNext.takeError());
return false;
}
Next = MaybeNext.get();
continue;
}
Scratch.clear();
llvm::StringRef BlobData;
llvm::Expected<unsigned> MaybeKind =
Cursor.readRecord(Next.ID, Scratch, &BlobData);
if (!MaybeKind) {
// FIXME this drops the error on the floor.
consumeError(MaybeKind.takeError());
return false;
}
unsigned Kind = MaybeKind.get();
switch (Kind) {
case objc_method_block::OBJC_METHOD_DATA: {
// Already saw Objective-C method table.
if (ObjCMethodTable)
return true;
uint32_t tableOffset;
objc_method_block::ObjCMethodDataLayout::readRecord(Scratch, tableOffset);
auto base = reinterpret_cast<const uint8_t *>(BlobData.data());
ObjCMethodTable.reset(SerializedObjCMethodTable::Create(
base + tableOffset, base + sizeof(uint32_t), base));
break;
}
default:
// Unknown record, possibly for use by a future version of the
// module format.
break;
}
MaybeNext = Cursor.advance();
if (!MaybeNext) {
// FIXME this drops the error on the floor.
consumeError(MaybeNext.takeError());
return false;
}
Next = MaybeNext.get();
}
return false;
}
bool APINotesReader::Implementation::readCXXMethodBlock(
llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
if (Cursor.EnterSubBlock(CXX_METHOD_BLOCK_ID))
return true;
llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
if (!MaybeNext) {
// FIXME this drops the error on the floor.
consumeError(MaybeNext.takeError());
return false;
}
llvm::BitstreamEntry Next = MaybeNext.get();
while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
if (Next.Kind == llvm::BitstreamEntry::Error)
return true;
if (Next.Kind == llvm::BitstreamEntry::SubBlock) {
// Unknown sub-block, possibly for use by a future version of the
// API notes format.
if (Cursor.SkipBlock())
return true;
MaybeNext = Cursor.advance();
if (!MaybeNext) {
// FIXME this drops the error on the floor.
consumeError(MaybeNext.takeError());
return false;
}
Next = MaybeNext.get();
continue;
}
Scratch.clear();
llvm::StringRef BlobData;
llvm::Expected<unsigned> MaybeKind =
Cursor.readRecord(Next.ID, Scratch, &BlobData);
if (!MaybeKind) {
// FIXME this drops the error on the floor.
consumeError(MaybeKind.takeError());
return false;
}
unsigned Kind = MaybeKind.get();
switch (Kind) {
case cxx_method_block::CXX_METHOD_DATA: {
// Already saw C++ method table.
if (CXXMethodTable)
return true;
uint32_t tableOffset;
cxx_method_block::CXXMethodDataLayout::readRecord(Scratch, tableOffset);
auto base = reinterpret_cast<const uint8_t *>(BlobData.data());
CXXMethodTable.reset(SerializedCXXMethodTable::Create(
base + tableOffset, base + sizeof(uint32_t), base));
break;
}
default:
// Unknown record, possibly for use by a future version of the
// module format.
break;
}
MaybeNext = Cursor.advance();
if (!MaybeNext) {
// FIXME this drops the error on the floor.
consumeError(MaybeNext.takeError());
return false;
}
Next = MaybeNext.get();
}
return false;
}
bool APINotesReader::Implementation::readFieldBlock(
llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
if (Cursor.EnterSubBlock(FIELD_BLOCK_ID))
return true;
llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
if (!MaybeNext) {
// FIXME this drops the error on the floor.
consumeError(MaybeNext.takeError());
return false;
}
llvm::BitstreamEntry Next = MaybeNext.get();
while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
if (Next.Kind == llvm::BitstreamEntry::Error)
return true;
if (Next.Kind == llvm::BitstreamEntry::SubBlock) {
// Unknown sub-block, possibly for use by a future version of the
// API notes format.
if (Cursor.SkipBlock())
return true;
MaybeNext = Cursor.advance();
if (!MaybeNext) {
// FIXME this drops the error on the floor.
consumeError(MaybeNext.takeError());
return false;
}
Next = MaybeNext.get();
continue;
}
Scratch.clear();
llvm::StringRef BlobData;
llvm::Expected<unsigned> MaybeKind =
Cursor.readRecord(Next.ID, Scratch, &BlobData);
if (!MaybeKind) {
// FIXME this drops the error on the floor.
consumeError(MaybeKind.takeError());
return false;
}
unsigned Kind = MaybeKind.get();
switch (Kind) {
case field_block::FIELD_DATA: {
// Already saw field table.
if (FieldTable)
return true;
uint32_t tableOffset;
field_block::FieldDataLayout::readRecord(Scratch, tableOffset);
auto base = reinterpret_cast<const uint8_t *>(BlobData.data());
FieldTable.reset(SerializedFieldTable::Create(
base + tableOffset, base + sizeof(uint32_t), base));
break;
}
default:
// Unknown record, possibly for use by a future version of the
// module format.
break;
}
MaybeNext = Cursor.advance();
if (!MaybeNext) {
// FIXME this drops the error on the floor.
consumeError(MaybeNext.takeError());
return false;
}
Next = MaybeNext.get();
}
return false;
}
bool APINotesReader::Implementation::readObjCSelectorBlock(
llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
if (Cursor.EnterSubBlock(OBJC_SELECTOR_BLOCK_ID))
return true;
llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
if (!MaybeNext) {
// FIXME this drops the error on the floor.
consumeError(MaybeNext.takeError());
return false;
}
llvm::BitstreamEntry Next = MaybeNext.get();
while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
if (Next.Kind == llvm::BitstreamEntry::Error)
return true;
if (Next.Kind == llvm::BitstreamEntry::SubBlock) {
// Unknown sub-block, possibly for use by a future version of the
// API notes format.
if (Cursor.SkipBlock())
return true;
MaybeNext = Cursor.advance();
if (!MaybeNext) {
// FIXME this drops the error on the floor.
consumeError(MaybeNext.takeError());
return false;
}
Next = MaybeNext.get();
continue;
}
Scratch.clear();
llvm::StringRef BlobData;
llvm::Expected<unsigned> MaybeKind =
Cursor.readRecord(Next.ID, Scratch, &BlobData);
if (!MaybeKind) {
// FIXME this drops the error on the floor.
consumeError(MaybeKind.takeError());
return false;
}
unsigned Kind = MaybeKind.get();
switch (Kind) {
case objc_selector_block::OBJC_SELECTOR_DATA: {
// Already saw Objective-C selector table.
if (ObjCSelectorTable)
return true;
uint32_t tableOffset;
objc_selector_block::ObjCSelectorDataLayout::readRecord(Scratch,
tableOffset);
auto base = reinterpret_cast<const uint8_t *>(BlobData.data());
ObjCSelectorTable.reset(SerializedObjCSelectorTable::Create(
base + tableOffset, base + sizeof(uint32_t), base));
break;
}
default:
// Unknown record, possibly for use by a future version of the
// module format.
break;
}
MaybeNext = Cursor.advance();
if (!MaybeNext) {
// FIXME this drops the error on the floor.
consumeError(MaybeNext.takeError());
return false;
}
Next = MaybeNext.get();
}
return false;
}
bool APINotesReader::Implementation::readGlobalVariableBlock(
llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
if (Cursor.EnterSubBlock(GLOBAL_VARIABLE_BLOCK_ID))
return true;
llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
if (!MaybeNext) {
// FIXME this drops the error on the floor.
consumeError(MaybeNext.takeError());
return false;
}
llvm::BitstreamEntry Next = MaybeNext.get();
while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
if (Next.Kind == llvm::BitstreamEntry::Error)
return true;
if (Next.Kind == llvm::BitstreamEntry::SubBlock) {
// Unknown sub-block, possibly for use by a future version of the
// API notes format.
if (Cursor.SkipBlock())
return true;
MaybeNext = Cursor.advance();
if (!MaybeNext) {
// FIXME this drops the error on the floor.
consumeError(MaybeNext.takeError());
return false;
}
Next = MaybeNext.get();
continue;
}
Scratch.clear();
llvm::StringRef BlobData;
llvm::Expected<unsigned> MaybeKind =
Cursor.readRecord(Next.ID, Scratch, &BlobData);
if (!MaybeKind) {
// FIXME this drops the error on the floor.
consumeError(MaybeKind.takeError());
return false;
}
unsigned Kind = MaybeKind.get();
switch (Kind) {
case global_variable_block::GLOBAL_VARIABLE_DATA: {
// Already saw global variable table.
if (GlobalVariableTable)
return true;
uint32_t tableOffset;
global_variable_block::GlobalVariableDataLayout::readRecord(Scratch,
tableOffset);
auto base = reinterpret_cast<const uint8_t *>(BlobData.data());
GlobalVariableTable.reset(SerializedGlobalVariableTable::Create(
base + tableOffset, base + sizeof(uint32_t), base));
break;
}
default:
// Unknown record, possibly for use by a future version of the
// module format.
break;
}
MaybeNext = Cursor.advance();
if (!MaybeNext) {
// FIXME this drops the error on the floor.
consumeError(MaybeNext.takeError());
return false;
}
Next = MaybeNext.get();
}
return false;
}
bool APINotesReader::Implementation::readGlobalFunctionBlock(
llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
if (Cursor.EnterSubBlock(GLOBAL_FUNCTION_BLOCK_ID))
return true;
llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
if (!MaybeNext) {
// FIXME this drops the error on the floor.
consumeError(MaybeNext.takeError());
return false;
}
llvm::BitstreamEntry Next = MaybeNext.get();
while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
if (Next.Kind == llvm::BitstreamEntry::Error)
return true;
if (Next.Kind == llvm::BitstreamEntry::SubBlock) {
// Unknown sub-block, possibly for use by a future version of the
// API notes format.
if (Cursor.SkipBlock())
return true;
MaybeNext = Cursor.advance();
if (!MaybeNext) {
// FIXME this drops the error on the floor.
consumeError(MaybeNext.takeError());
return false;
}
Next = MaybeNext.get();
continue;
}
Scratch.clear();
llvm::StringRef BlobData;
llvm::Expected<unsigned> MaybeKind =
Cursor.readRecord(Next.ID, Scratch, &BlobData);
if (!MaybeKind) {
// FIXME this drops the error on the floor.
consumeError(MaybeKind.takeError());
return false;
}
unsigned Kind = MaybeKind.get();
switch (Kind) {
case global_function_block::GLOBAL_FUNCTION_DATA: {
// Already saw global function table.
if (GlobalFunctionTable)
return true;
uint32_t tableOffset;
global_function_block::GlobalFunctionDataLayout::readRecord(Scratch,
tableOffset);
auto base = reinterpret_cast<const uint8_t *>(BlobData.data());
GlobalFunctionTable.reset(SerializedGlobalFunctionTable::Create(
base + tableOffset, base + sizeof(uint32_t), base));
break;
}
default:
// Unknown record, possibly for use by a future version of the
// module format.
break;
}
MaybeNext = Cursor.advance();
if (!MaybeNext) {
// FIXME this drops the error on the floor.
consumeError(MaybeNext.takeError());
return false;
}
Next = MaybeNext.get();
}
return false;
}
bool APINotesReader::Implementation::readEnumConstantBlock(
llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
if (Cursor.EnterSubBlock(ENUM_CONSTANT_BLOCK_ID))
return true;
llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
if (!MaybeNext) {
// FIXME this drops the error on the floor.
consumeError(MaybeNext.takeError());
return false;
}
llvm::BitstreamEntry Next = MaybeNext.get();
while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
if (Next.Kind == llvm::BitstreamEntry::Error)
return true;
if (Next.Kind == llvm::BitstreamEntry::SubBlock) {
// Unknown sub-block, possibly for use by a future version of the
// API notes format.
if (Cursor.SkipBlock())
return true;
MaybeNext = Cursor.advance();
if (!MaybeNext) {
// FIXME this drops the error on the floor.
consumeError(MaybeNext.takeError());
return false;
}
Next = MaybeNext.get();
continue;
}
Scratch.clear();
llvm::StringRef BlobData;
llvm::Expected<unsigned> MaybeKind =
Cursor.readRecord(Next.ID, Scratch, &BlobData);
if (!MaybeKind) {
// FIXME this drops the error on the floor.
consumeError(MaybeKind.takeError());
return false;
}
unsigned Kind = MaybeKind.get();
switch (Kind) {
case enum_constant_block::ENUM_CONSTANT_DATA: {
// Already saw enumerator table.
if (EnumConstantTable)
return true;
uint32_t tableOffset;
enum_constant_block::EnumConstantDataLayout::readRecord(Scratch,
tableOffset);
auto base = reinterpret_cast<const uint8_t *>(BlobData.data());
EnumConstantTable.reset(SerializedEnumConstantTable::Create(
base + tableOffset, base + sizeof(uint32_t), base));
break;
}
default:
// Unknown record, possibly for use by a future version of the
// module format.
break;
}
MaybeNext = Cursor.advance();
if (!MaybeNext) {
// FIXME this drops the error on the floor.
consumeError(MaybeNext.takeError());
return false;
}
Next = MaybeNext.get();
}
return false;
}
bool APINotesReader::Implementation::readTagBlock(
llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
if (Cursor.EnterSubBlock(TAG_BLOCK_ID))
return true;
llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
if (!MaybeNext) {
// FIXME this drops the error on the floor.
consumeError(MaybeNext.takeError());
return false;
}
llvm::BitstreamEntry Next = MaybeNext.get();
while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
if (Next.Kind == llvm::BitstreamEntry::Error)
return true;
if (Next.Kind == llvm::BitstreamEntry::SubBlock) {
// Unknown sub-block, possibly for use by a future version of the
// API notes format.
if (Cursor.SkipBlock())
return true;
MaybeNext = Cursor.advance();
if (!MaybeNext) {
// FIXME this drops the error on the floor.
consumeError(MaybeNext.takeError());
return false;
}
Next = MaybeNext.get();
continue;
}
Scratch.clear();
llvm::StringRef BlobData;
llvm::Expected<unsigned> MaybeKind =
Cursor.readRecord(Next.ID, Scratch, &BlobData);
if (!MaybeKind) {
// FIXME this drops the error on the floor.
consumeError(MaybeKind.takeError());
return false;
}
unsigned Kind = MaybeKind.get();
switch (Kind) {
case tag_block::TAG_DATA: {
// Already saw tag table.
if (TagTable)
return true;
uint32_t tableOffset;
tag_block::TagDataLayout::readRecord(Scratch, tableOffset);
auto base = reinterpret_cast<const uint8_t *>(BlobData.data());
TagTable.reset(SerializedTagTable::Create(base + tableOffset,
base + sizeof(uint32_t), base));
break;
}
default:
// Unknown record, possibly for use by a future version of the
// module format.
break;
}
MaybeNext = Cursor.advance();
if (!MaybeNext) {
// FIXME this drops the error on the floor.
consumeError(MaybeNext.takeError());
return false;
}
Next = MaybeNext.get();
}
return false;
}
bool APINotesReader::Implementation::readTypedefBlock(
llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
if (Cursor.EnterSubBlock(TYPEDEF_BLOCK_ID))
return true;
llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
if (!MaybeNext) {
// FIXME this drops the error on the floor.
consumeError(MaybeNext.takeError());
return false;
}
llvm::BitstreamEntry Next = MaybeNext.get();
while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
if (Next.Kind == llvm::BitstreamEntry::Error)
return true;
if (Next.Kind == llvm::BitstreamEntry::SubBlock) {
// Unknown sub-block, possibly for use by a future version of the
// API notes format.
if (Cursor.SkipBlock())
return true;
MaybeNext = Cursor.advance();
if (!MaybeNext) {
// FIXME this drops the error on the floor.
consumeError(MaybeNext.takeError());
return false;
}
Next = MaybeNext.get();
continue;
}
Scratch.clear();
llvm::StringRef BlobData;
llvm::Expected<unsigned> MaybeKind =
Cursor.readRecord(Next.ID, Scratch, &BlobData);
if (!MaybeKind) {
// FIXME this drops the error on the floor.
consumeError(MaybeKind.takeError());
return false;
}
unsigned Kind = MaybeKind.get();
switch (Kind) {
case typedef_block::TYPEDEF_DATA: {
// Already saw typedef table.
if (TypedefTable)
return true;
uint32_t tableOffset;
typedef_block::TypedefDataLayout::readRecord(Scratch, tableOffset);
auto base = reinterpret_cast<const uint8_t *>(BlobData.data());
TypedefTable.reset(SerializedTypedefTable::Create(
base + tableOffset, base + sizeof(uint32_t), base));
break;
}
default:
// Unknown record, possibly for use by a future version of the
// module format.
break;
}
MaybeNext = Cursor.advance();
if (!MaybeNext) {
// FIXME this drops the error on the floor.
consumeError(MaybeNext.takeError());
return false;
}
Next = MaybeNext.get();
}
return false;
}
APINotesReader::APINotesReader(llvm::MemoryBuffer *InputBuffer,
llvm::VersionTuple SwiftVersion, bool &Failed)
: Implementation(new class Implementation) {
Failed = false;
// Initialize the input buffer.
Implementation->InputBuffer = InputBuffer;
Implementation->SwiftVersion = SwiftVersion;
llvm::BitstreamCursor Cursor(*Implementation->InputBuffer);
// Validate signature.
for (auto byte : API_NOTES_SIGNATURE) {
if (Cursor.AtEndOfStream()) {
Failed = true;
return;
}
if (llvm::Expected<llvm::SimpleBitstreamCursor::word_t> maybeRead =
Cursor.Read(8)) {
if (maybeRead.get() != byte) {
Failed = true;
return;
}
} else {
// FIXME this drops the error on the floor.
consumeError(maybeRead.takeError());
Failed = true;
return;
}
}
// Look at all of the blocks.
bool HasValidControlBlock = false;
llvm::SmallVector<uint64_t, 64> Scratch;
while (!Cursor.AtEndOfStream()) {
llvm::Expected<llvm::BitstreamEntry> MaybeTopLevelEntry = Cursor.advance();
if (!MaybeTopLevelEntry) {
// FIXME this drops the error on the floor.
consumeError(MaybeTopLevelEntry.takeError());
Failed = true;
return;
}
llvm::BitstreamEntry TopLevelEntry = MaybeTopLevelEntry.get();
if (TopLevelEntry.Kind != llvm::BitstreamEntry::SubBlock)
break;
switch (TopLevelEntry.ID) {
case llvm::bitc::BLOCKINFO_BLOCK_ID:
if (!Cursor.ReadBlockInfoBlock()) {
Failed = true;
break;
}
break;
case CONTROL_BLOCK_ID:
// Only allow a single control block.
if (HasValidControlBlock ||
Implementation->readControlBlock(Cursor, Scratch)) {
Failed = true;
return;
}
HasValidControlBlock = true;
break;
case IDENTIFIER_BLOCK_ID:
if (!HasValidControlBlock ||
Implementation->readIdentifierBlock(Cursor, Scratch)) {
Failed = true;
return;
}
break;
case OBJC_CONTEXT_BLOCK_ID:
if (!HasValidControlBlock ||
Implementation->readContextBlock(Cursor, Scratch)) {
Failed = true;
return;
}
break;
case OBJC_PROPERTY_BLOCK_ID:
if (!HasValidControlBlock ||
Implementation->readObjCPropertyBlock(Cursor, Scratch)) {
Failed = true;
return;
}
break;
case OBJC_METHOD_BLOCK_ID:
if (!HasValidControlBlock ||
Implementation->readObjCMethodBlock(Cursor, Scratch)) {
Failed = true;
return;
}
break;
case CXX_METHOD_BLOCK_ID:
if (!HasValidControlBlock ||
Implementation->readCXXMethodBlock(Cursor, Scratch)) {
Failed = true;
return;
}
break;
case FIELD_BLOCK_ID:
if (!HasValidControlBlock ||
Implementation->readFieldBlock(Cursor, Scratch)) {
Failed = true;
return;
}
break;
case OBJC_SELECTOR_BLOCK_ID:
if (!HasValidControlBlock ||
Implementation->readObjCSelectorBlock(Cursor, Scratch)) {
Failed = true;
return;
}
break;
case GLOBAL_VARIABLE_BLOCK_ID:
if (!HasValidControlBlock ||
Implementation->readGlobalVariableBlock(Cursor, Scratch)) {
Failed = true;
return;
}
break;
case GLOBAL_FUNCTION_BLOCK_ID:
if (!HasValidControlBlock ||
Implementation->readGlobalFunctionBlock(Cursor, Scratch)) {
Failed = true;
return;
}
break;
case ENUM_CONSTANT_BLOCK_ID:
if (!HasValidControlBlock ||
Implementation->readEnumConstantBlock(Cursor, Scratch)) {
Failed = true;
return;
}
break;
case TAG_BLOCK_ID:
if (!HasValidControlBlock ||
Implementation->readTagBlock(Cursor, Scratch)) {
Failed = true;
return;
}
break;
case TYPEDEF_BLOCK_ID:
if (!HasValidControlBlock ||
Implementation->readTypedefBlock(Cursor, Scratch)) {
Failed = true;
return;
}
break;
default:
// Unknown top-level block, possibly for use by a future version of the
// module format.
if (Cursor.SkipBlock()) {
Failed = true;
return;
}
break;
}
}
if (!Cursor.AtEndOfStream()) {
Failed = true;
return;
}
}
APINotesReader::~APINotesReader() { delete Implementation->InputBuffer; }
std::unique_ptr<APINotesReader>
APINotesReader::Create(std::unique_ptr<llvm::MemoryBuffer> InputBuffer,
llvm::VersionTuple SwiftVersion) {
bool Failed = false;
std::unique_ptr<APINotesReader> Reader(
new APINotesReader(InputBuffer.release(), SwiftVersion, Failed));
if (Failed)
return nullptr;
return Reader;
}
template <typename T>
APINotesReader::VersionedInfo<T>::VersionedInfo(
llvm::VersionTuple Version,
llvm::SmallVector<std::pair<llvm::VersionTuple, T>, 1> R)
: Results(std::move(R)) {
assert(!Results.empty());
assert(std::is_sorted(
Results.begin(), Results.end(),
[](const std::pair<llvm::VersionTuple, T> &left,
const std::pair<llvm::VersionTuple, T> &right) -> bool {
// The comparison function should be reflective, and with expensive
// checks we can get callbacks basically checking that lambda(a,a) is
// false. We could still check that we do not find equal elements when
// left!=right.
assert((&left == &right || left.first != right.first) &&
"two entries for the same version");
return left.first < right.first;
}));
Selected = std::nullopt;
for (unsigned i = 0, n = Results.size(); i != n; ++i) {
if (!Version.empty() && Results[i].first >= Version) {
// If the current version is "4", then entries for 4 are better than
// entries for 5, but both are valid. Because entries are sorted, we get
// that behavior by picking the first match.
Selected = i;
break;
}
}
// If we didn't find a match but we have an unversioned result, use the
// unversioned result. This will always be the first entry because we encode
// it as version 0.
if (!Selected && Results[0].first.empty())
Selected = 0;
}
auto APINotesReader::lookupObjCClassID(llvm::StringRef Name)
-> std::optional<ContextID> {
if (!Implementation->ContextIDTable)
return std::nullopt;
std::optional<IdentifierID> ClassID = Implementation->getIdentifier(Name);
if (!ClassID)
return std::nullopt;
// ObjC classes can't be declared in C++ namespaces, so use -1 as the global
// context.
auto KnownID = Implementation->ContextIDTable->find(
ContextTableKey(-1, (uint8_t)ContextKind::ObjCClass, *ClassID));
if (KnownID == Implementation->ContextIDTable->end())
return std::nullopt;
return ContextID(*KnownID);
}
auto APINotesReader::lookupObjCClassInfo(llvm::StringRef Name)
-> VersionedInfo<ContextInfo> {
if (!Implementation->ContextInfoTable)
return std::nullopt;
std::optional<ContextID> CtxID = lookupObjCClassID(Name);
if (!CtxID)
return std::nullopt;
auto KnownInfo = Implementation->ContextInfoTable->find(CtxID->Value);
if (KnownInfo == Implementation->ContextInfoTable->end())
return std::nullopt;
return {Implementation->SwiftVersion, *KnownInfo};
}
auto APINotesReader::lookupObjCProtocolID(llvm::StringRef Name)
-> std::optional<ContextID> {
if (!Implementation->ContextIDTable)
return std::nullopt;
std::optional<IdentifierID> classID = Implementation->getIdentifier(Name);
if (!classID)
return std::nullopt;
// ObjC classes can't be declared in C++ namespaces, so use -1 as the global
// context.
auto KnownID = Implementation->ContextIDTable->find(
ContextTableKey(-1, (uint8_t)ContextKind::ObjCProtocol, *classID));
if (KnownID == Implementation->ContextIDTable->end())
return std::nullopt;
return ContextID(*KnownID);
}
auto APINotesReader::lookupObjCProtocolInfo(llvm::StringRef Name)
-> VersionedInfo<ContextInfo> {
if (!Implementation->ContextInfoTable)
return std::nullopt;
std::optional<ContextID> CtxID = lookupObjCProtocolID(Name);
if (!CtxID)
return std::nullopt;
auto KnownInfo = Implementation->ContextInfoTable->find(CtxID->Value);
if (KnownInfo == Implementation->ContextInfoTable->end())
return std::nullopt;
return {Implementation->SwiftVersion, *KnownInfo};
}
auto APINotesReader::lookupObjCProperty(ContextID CtxID, llvm::StringRef Name,
bool IsInstance)
-> VersionedInfo<ObjCPropertyInfo> {
if (!Implementation->ObjCPropertyTable)
return std::nullopt;
std::optional<IdentifierID> PropertyID = Implementation->getIdentifier(Name);
if (!PropertyID)
return std::nullopt;
auto Known = Implementation->ObjCPropertyTable->find(
std::make_tuple(CtxID.Value, *PropertyID, (char)IsInstance));
if (Known == Implementation->ObjCPropertyTable->end())
return std::nullopt;
return {Implementation->SwiftVersion, *Known};
}
auto APINotesReader::lookupObjCMethod(ContextID CtxID, ObjCSelectorRef Selector,
bool IsInstanceMethod)
-> VersionedInfo<ObjCMethodInfo> {
if (!Implementation->ObjCMethodTable)
return std::nullopt;
std::optional<SelectorID> SelID = Implementation->getSelector(Selector);
if (!SelID)
return std::nullopt;
auto Known = Implementation->ObjCMethodTable->find(
ObjCMethodTableInfo::internal_key_type{CtxID.Value, *SelID,
IsInstanceMethod});
if (Known == Implementation->ObjCMethodTable->end())
return std::nullopt;
return {Implementation->SwiftVersion, *Known};
}
auto APINotesReader::lookupField(ContextID CtxID, llvm::StringRef Name)
-> VersionedInfo<FieldInfo> {
if (!Implementation->FieldTable)
return std::nullopt;
std::optional<IdentifierID> NameID = Implementation->getIdentifier(Name);
if (!NameID)
return std::nullopt;
auto Known = Implementation->FieldTable->find(
SingleDeclTableKey(CtxID.Value, *NameID));
if (Known == Implementation->FieldTable->end())
return std::nullopt;
return {Implementation->SwiftVersion, *Known};
}
auto APINotesReader::lookupCXXMethod(ContextID CtxID, llvm::StringRef Name)
-> VersionedInfo<CXXMethodInfo> {
if (!Implementation->CXXMethodTable)
return std::nullopt;
std::optional<IdentifierID> NameID = Implementation->getIdentifier(Name);
if (!NameID)
return std::nullopt;
auto Known = Implementation->CXXMethodTable->find(
SingleDeclTableKey(CtxID.Value, *NameID));
if (Known == Implementation->CXXMethodTable->end())
return std::nullopt;
return {Implementation->SwiftVersion, *Known};
}
auto APINotesReader::lookupGlobalVariable(llvm::StringRef Name,
std::optional<Context> Ctx)
-> VersionedInfo<GlobalVariableInfo> {
if (!Implementation->GlobalVariableTable)
return std::nullopt;
std::optional<IdentifierID> NameID = Implementation->getIdentifier(Name);
if (!NameID)
return std::nullopt;
SingleDeclTableKey Key(Ctx, *NameID);
auto Known = Implementation->GlobalVariableTable->find(Key);
if (Known == Implementation->GlobalVariableTable->end())
return std::nullopt;
return {Implementation->SwiftVersion, *Known};
}
auto APINotesReader::lookupGlobalFunction(llvm::StringRef Name,
std::optional<Context> Ctx)
-> VersionedInfo<GlobalFunctionInfo> {
if (!Implementation->GlobalFunctionTable)
return std::nullopt;
std::optional<IdentifierID> NameID = Implementation->getIdentifier(Name);
if (!NameID)
return std::nullopt;
SingleDeclTableKey Key(Ctx, *NameID);
auto Known = Implementation->GlobalFunctionTable->find(Key);
if (Known == Implementation->GlobalFunctionTable->end())
return std::nullopt;
return {Implementation->SwiftVersion, *Known};
}
auto APINotesReader::lookupEnumConstant(llvm::StringRef Name)
-> VersionedInfo<EnumConstantInfo> {
if (!Implementation->EnumConstantTable)
return std::nullopt;
std::optional<IdentifierID> NameID = Implementation->getIdentifier(Name);
if (!NameID)
return std::nullopt;
auto Known = Implementation->EnumConstantTable->find(*NameID);
if (Known == Implementation->EnumConstantTable->end())
return std::nullopt;
return {Implementation->SwiftVersion, *Known};
}
auto APINotesReader::lookupTagID(llvm::StringRef Name,
std::optional<Context> ParentCtx)
-> std::optional<ContextID> {
if (!Implementation->ContextIDTable)
return std::nullopt;
std::optional<IdentifierID> TagID = Implementation->getIdentifier(Name);
if (!TagID)
return std::nullopt;
auto KnownID = Implementation->ContextIDTable->find(
ContextTableKey(ParentCtx, ContextKind::Tag, *TagID));
if (KnownID == Implementation->ContextIDTable->end())
return std::nullopt;
return ContextID(*KnownID);
}
auto APINotesReader::lookupTag(llvm::StringRef Name, std::optional<Context> Ctx)
-> VersionedInfo<TagInfo> {
if (!Implementation->TagTable)
return std::nullopt;
std::optional<IdentifierID> NameID = Implementation->getIdentifier(Name);
if (!NameID)
return std::nullopt;
SingleDeclTableKey Key(Ctx, *NameID);
auto Known = Implementation->TagTable->find(Key);
if (Known == Implementation->TagTable->end())
return std::nullopt;
return {Implementation->SwiftVersion, *Known};
}
auto APINotesReader::lookupTypedef(llvm::StringRef Name,
std::optional<Context> Ctx)
-> VersionedInfo<TypedefInfo> {
if (!Implementation->TypedefTable)
return std::nullopt;
std::optional<IdentifierID> NameID = Implementation->getIdentifier(Name);
if (!NameID)
return std::nullopt;
SingleDeclTableKey Key(Ctx, *NameID);
auto Known = Implementation->TypedefTable->find(Key);
if (Known == Implementation->TypedefTable->end())
return std::nullopt;
return {Implementation->SwiftVersion, *Known};
}
auto APINotesReader::lookupNamespaceID(
llvm::StringRef Name, std::optional<ContextID> ParentNamespaceID)
-> std::optional<ContextID> {
if (!Implementation->ContextIDTable)
return std::nullopt;
std::optional<IdentifierID> NamespaceID = Implementation->getIdentifier(Name);
if (!NamespaceID)
return std::nullopt;
uint32_t RawParentNamespaceID =
ParentNamespaceID ? ParentNamespaceID->Value : -1;
auto KnownID = Implementation->ContextIDTable->find(
{RawParentNamespaceID, (uint8_t)ContextKind::Namespace, *NamespaceID});
if (KnownID == Implementation->ContextIDTable->end())
return std::nullopt;
return ContextID(*KnownID);
}
} // namespace api_notes
} // namespace clang