mirror-llvm-project/clang/lib/APINotes/APINotesReader.cpp
StoeckOverflow a6614ee07b
[APINotes] Diagnose invalid Where.Parameters selectors (#209408)
This PR adds diagnostics for exact `Where.Parameters` selectors on top
of the existing parsing, serialization, and Sema matching support.

It diagnoses duplicate exact selectors during API notes conversion,
including duplicate `Where.Parameters: []`, while still allowing broad
name-only entries and same-name entries with different selectors.

It also adds reader/Sema support to warn under `-Wapinotes` when an
exact selector in API notes does not match any visible overload. The
reader can now enumerate stored exact selectors for global functions and
C++ methods, and Sema compares those against the selector candidates
derived from the visible overload set.

The diagnostic path follows the same matching policy as Sema, including
the desugared alias fallback, so valid matched selectors do not produce
false warnings.

Tests cover duplicate selector errors, unmatched selector warnings for
globals and C++ methods, broad-plus-exact coexistence, exact notes not
silently applying on mismatch, and no false warnings for matched or
alias-fallback selectors.

Reviewers: @Xazax-hun @j-hui @egorzhdan

---------

Co-authored-by: John Hui <updog@j-hui.com>
2026-07-29 09:50:35 +01:00

2685 lines
89 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/DenseMap.h"
#include "llvm/ADT/Hashing.h"
#include "llvm/Bitstream/BitstreamReader.h"
#include "llvm/Support/DJB.h"
#include "llvm/Support/OnDiskHashTable.h"
#include <string>
#include <type_traits>
#include <utility>
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);
}
static FunctionTableKey readFunctionTableKey(const uint8_t *Data,
unsigned Length) {
assert(Length >= FunctionTableKeyBaseLength &&
"Unexpected function table key length");
auto CtxID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
auto NameID = endian::readNext<uint32_t, llvm::endianness::little>(Data);
uint8_t FunctionKeyFlags =
endian::readNext<uint8_t, llvm::endianness::little>(Data);
auto ParameterCount =
endian::readNext<uint16_t, llvm::endianness::little>(Data);
assert(Length ==
FunctionTableKeyBaseLength + ParameterCount * sizeof(uint32_t) &&
"Unexpected function table key length");
llvm::SmallVector<IdentifierID, 2> ParameterTypeIDs;
ParameterTypeIDs.reserve(ParameterCount);
for (unsigned I = 0; I != ParameterCount; ++I)
ParameterTypeIDs.push_back(
endian::readNext<uint32_t, llvm::endianness::little>(Data));
assert((FunctionKeyFlags & ~FunctionKeyHasParameterSelector) == 0 &&
"Unexpected function table key flags");
if (FunctionKeyFlags & FunctionKeyHasParameterSelector)
return {CtxID, NameID, ParameterTypeIDs};
assert(ParameterTypeIDs.empty() &&
"Broad function table key should not store parameters");
return {CtxID, NameID};
}
/// 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 EncodedBits = *Data++;
Info.Unavailable = (EncodedBits >> 1) & 0x01;
Info.UnavailableInSwift = EncodedBits & 0x01;
if ((EncodedBits >> 2) & 0x01)
Info.setSwiftPrivate(static_cast<bool>((EncodedBits >> 3) & 0x01));
if ((EncodedBits >> 4) & 0x01)
Info.setSwiftSafety(
static_cast<SwiftSafetyKind>((EncodedBits >> 5) & 0x03));
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;
}
if (unsigned ConformanceLength =
endian::readNext<uint16_t, llvm::endianness::little>(Data)) {
Info.setSwiftConformance(std::string(reinterpret_cast<const char *>(Data),
ConformanceLength - 1));
Data += ConformanceLength - 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 BoundsSafetyInfo.
void ReadBoundsSafetyInfo(const uint8_t *&Data, BoundsSafetyInfo &Info) {
uint8_t Payload = endian::readNext<uint8_t, llvm::endianness::little>(Data);
if (Payload & 0x01) {
uint8_t Level = (Payload >> 1) & 0x7;
Info.setLevelAudited(Level);
}
Payload >>= 4;
if (Payload & 0x01) {
uint8_t Kind = (Payload >> 1) & 0x7;
assert(Kind <=
static_cast<uint8_t>(BoundsSafetyInfo::BoundsSafetyKind::EndedBy));
Info.setKindAudited(static_cast<BoundsSafetyInfo::BoundsSafetyKind>(Kind));
}
uint16_t ExternalBoundsLen =
endian::readNext<uint16_t, llvm::endianness::little>(Data);
Info.ExternalBounds = std::string(Data, Data + ExternalBoundsLen);
Data += ExternalBoundsLen;
}
/// 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;
if (Payload & 0x01)
ReadBoundsSafetyInfo(Data, Info.BoundsSafety.emplace());
}
/// 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 (Payload & 0x1)
Info.UnsafeBufferUsage = 1;
Payload >>= 0x1;
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, FunctionTableKey,
GlobalFunctionInfo> {
public:
static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) {
return readFunctionTableKey(Data, Length);
}
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, FunctionTableKey,
CXXMethodInfo> {
public:
static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) {
return readFunctionTableKey(Data, Length);
}
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;
}
unsigned DefaultOwnershipLength =
endian::readNext<uint16_t, llvm::endianness::little>(Data);
if (DefaultOwnershipLength > 0) {
Info.SwiftDefaultOwnership = std::string(
reinterpret_cast<const char *>(Data), DefaultOwnershipLength - 1);
Data += DefaultOwnershipLength - 1;
}
unsigned DestroyOpLength =
endian::readNext<uint16_t, llvm::endianness::little>(Data);
if (DestroyOpLength > 0) {
Info.SwiftDestroyOp = std::string(reinterpret_cast<const char *>(Data),
DestroyOpLength - 1);
Data += DestroyOpLength - 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;
/// Lazy reverse lookup cache from identifier ID to string.
std::optional<llvm::DenseMap<uint32_t, llvm::StringRef>> IdentifierStrings;
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 identifier string for the given ID, or an empty optional if
/// the ID is unknown.
std::optional<llvm::StringRef> getIdentifierString(IdentifierID ID);
/// Collect exact parameter selector keys stored in the given function-like
/// table.
template <typename TableT>
void collectExactFunctionParameterSelectors(
TableT &Table,
llvm::SmallVectorImpl<APINotesFunctionSelectorKey> &Selectors);
/// Retrieve the selector ID for the given selector, or an empty
/// optional if the string is unknown.
std::optional<SelectorID> getSelector(ObjCSelectorRef Selector);
llvm::Error readControlBlock(llvm::BitstreamCursor &Cursor,
llvm::SmallVectorImpl<uint64_t> &Scratch);
llvm::Error readIdentifierBlock(llvm::BitstreamCursor &Cursor,
llvm::SmallVectorImpl<uint64_t> &Scratch);
llvm::Error readContextBlock(llvm::BitstreamCursor &Cursor,
llvm::SmallVectorImpl<uint64_t> &Scratch);
llvm::Error readObjCPropertyBlock(llvm::BitstreamCursor &Cursor,
llvm::SmallVectorImpl<uint64_t> &Scratch);
llvm::Error readObjCMethodBlock(llvm::BitstreamCursor &Cursor,
llvm::SmallVectorImpl<uint64_t> &Scratch);
llvm::Error readCXXMethodBlock(llvm::BitstreamCursor &Cursor,
llvm::SmallVectorImpl<uint64_t> &Scratch);
llvm::Error readFieldBlock(llvm::BitstreamCursor &Cursor,
llvm::SmallVectorImpl<uint64_t> &Scratch);
llvm::Error readObjCSelectorBlock(llvm::BitstreamCursor &Cursor,
llvm::SmallVectorImpl<uint64_t> &Scratch);
llvm::Error readGlobalVariableBlock(llvm::BitstreamCursor &Cursor,
llvm::SmallVectorImpl<uint64_t> &Scratch);
std::optional<FunctionTableKey> getFunctionKey(uint32_t ParentContextID,
llvm::StringRef Name);
template <typename ParameterT>
std::optional<FunctionTableKey>
getFunctionKey(uint32_t ParentContextID, llvm::StringRef Name,
llvm::ArrayRef<ParameterT> Parameters);
std::optional<FunctionTableKey>
getFunctionKey(std::optional<Context> ParentContext, llvm::StringRef Name);
template <typename ParameterT>
std::optional<FunctionTableKey>
getFunctionKey(std::optional<Context> ParentContext, llvm::StringRef Name,
llvm::ArrayRef<ParameterT> Parameters);
llvm::Error readGlobalFunctionBlock(llvm::BitstreamCursor &Cursor,
llvm::SmallVectorImpl<uint64_t> &Scratch);
llvm::Error readEnumConstantBlock(llvm::BitstreamCursor &Cursor,
llvm::SmallVectorImpl<uint64_t> &Scratch);
llvm::Error readTagBlock(llvm::BitstreamCursor &Cursor,
llvm::SmallVectorImpl<uint64_t> &Scratch);
llvm::Error 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<llvm::StringRef>
APINotesReader::Implementation::getIdentifierString(IdentifierID ID) {
if (!IdentifierTable)
return std::nullopt;
if (ID == IdentifierID(0))
return llvm::StringRef();
if (!IdentifierStrings) {
IdentifierStrings.emplace();
// keys() and data() iterate over the same serialized entries in lockstep.
// The serialized hash-table order is not guaranteed to be identifier-ID
// order, so keep an explicit ID-to-string map rather than indexing a vector
// by ID.
auto Identifiers = IdentifierTable->keys();
auto IDs = IdentifierTable->data();
auto Identifier = Identifiers.begin();
auto KnownID = IDs.begin();
auto IdentifierEnd = Identifiers.end();
auto KnownIDEnd = IDs.end();
for (; Identifier != IdentifierEnd && KnownID != KnownIDEnd;
++Identifier, ++KnownID)
IdentifierStrings->try_emplace(static_cast<uint32_t>(*KnownID),
*Identifier);
}
auto Known = IdentifierStrings->find(static_cast<uint32_t>(ID));
if (Known == IdentifierStrings->end())
return std::nullopt;
return Known->second;
}
template <typename TableT>
void APINotesReader::Implementation::collectExactFunctionParameterSelectors(
TableT &Table,
llvm::SmallVectorImpl<APINotesFunctionSelectorKey> &Selectors) {
static_assert(std::is_same_v<TableT, SerializedGlobalFunctionTable> ||
std::is_same_v<TableT, SerializedCXXMethodTable>);
constexpr bool IsCXXMethod = std::is_same_v<TableT, SerializedCXXMethodTable>;
for (const FunctionTableKey &Key : Table.keys()) {
if (!Key.parameterTypeIDs)
continue;
Selectors.push_back(APINotesFunctionSelectorKey{Key, IsCXXMethod});
}
}
std::optional<FunctionTableKey>
APINotesReader::Implementation::getFunctionKey(uint32_t ParentContextID,
llvm::StringRef Name) {
return getFunctionKeyImpl(ParentContextID, Name, [this](llvm::StringRef S) {
return getIdentifier(S);
});
}
template <typename ParameterT>
std::optional<FunctionTableKey> APINotesReader::Implementation::getFunctionKey(
uint32_t ParentContextID, llvm::StringRef Name,
llvm::ArrayRef<ParameterT> Parameters) {
return getFunctionKeyImpl(
ParentContextID, Name, Parameters,
[this](llvm::StringRef S) { return getIdentifier(S); });
}
std::optional<FunctionTableKey> APINotesReader::Implementation::getFunctionKey(
std::optional<Context> ParentContext, llvm::StringRef Name) {
uint32_t ParentContextID =
ParentContext ? ParentContext->id.Value : static_cast<uint32_t>(-1);
return getFunctionKey(ParentContextID, Name);
}
template <typename ParameterT>
std::optional<FunctionTableKey> APINotesReader::Implementation::getFunctionKey(
std::optional<Context> ParentContext, llvm::StringRef Name,
llvm::ArrayRef<ParameterT> Parameters) {
uint32_t ParentContextID =
ParentContext ? ParentContext->id.Value : static_cast<uint32_t>(-1);
return getFunctionKey(ParentContextID, Name, Parameters);
}
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;
}
llvm::Error APINotesReader::Implementation::readControlBlock(
llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
if (Cursor.EnterSubBlock(CONTROL_BLOCK_ID))
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Failed to enter control block");
bool SawMetadata = false;
llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
if (!MaybeNext)
return MaybeNext.takeError();
llvm::BitstreamEntry Next = MaybeNext.get();
while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
if (Next.Kind == llvm::BitstreamEntry::Error)
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Malformed bitstream entry");
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 llvm::createStringError(llvm::inconvertibleErrorCode(),
"Failed to skip sub-block");
MaybeNext = Cursor.advance();
if (!MaybeNext)
return MaybeNext.takeError();
Next = MaybeNext.get();
continue;
}
Scratch.clear();
llvm::StringRef BlobData;
llvm::Expected<unsigned> MaybeKind =
Cursor.readRecord(Next.ID, Scratch, &BlobData);
if (!MaybeKind) {
return MaybeKind.takeError();
}
unsigned Kind = MaybeKind.get();
switch (Kind) {
case control_block::METADATA:
// Already saw metadata.
if (SawMetadata)
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Multiple metadata records found");
if (Scratch[0] != VERSION_MAJOR || Scratch[1] != VERSION_MINOR)
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Version mismatch in API Notes");
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)
return MaybeNext.takeError();
Next = MaybeNext.get();
}
if (!SawMetadata)
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Missing metadata record");
return llvm::Error::success();
}
llvm::Error APINotesReader::Implementation::readIdentifierBlock(
llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
if (Cursor.EnterSubBlock(IDENTIFIER_BLOCK_ID))
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Failed to enter identifier block");
llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
if (!MaybeNext)
return MaybeNext.takeError();
llvm::BitstreamEntry Next = MaybeNext.get();
while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
if (Next.Kind == llvm::BitstreamEntry::Error)
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Malformed bitstream entry");
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 llvm::createStringError(llvm::inconvertibleErrorCode(),
"Failed to skip sub-block");
MaybeNext = Cursor.advance();
if (!MaybeNext)
return MaybeNext.takeError();
Next = MaybeNext.get();
continue;
}
Scratch.clear();
llvm::StringRef BlobData;
llvm::Expected<unsigned> MaybeKind =
Cursor.readRecord(Next.ID, Scratch, &BlobData);
if (!MaybeKind) {
return MaybeKind.takeError();
}
unsigned Kind = MaybeKind.get();
switch (Kind) {
case identifier_block::IDENTIFIER_DATA: {
// Already saw identifier table.
if (IdentifierTable)
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Multiple identifier records found");
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)
return MaybeNext.takeError();
Next = MaybeNext.get();
}
return llvm::Error::success();
}
llvm::Error APINotesReader::Implementation::readContextBlock(
llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
if (Cursor.EnterSubBlock(OBJC_CONTEXT_BLOCK_ID))
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Failed to enter Objective-C context block");
llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
if (!MaybeNext)
return MaybeNext.takeError();
llvm::BitstreamEntry Next = MaybeNext.get();
while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
if (Next.Kind == llvm::BitstreamEntry::Error)
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Malformed bitstream entry");
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 llvm::createStringError(llvm::inconvertibleErrorCode(),
"Failed to skip sub-block");
MaybeNext = Cursor.advance();
if (!MaybeNext)
return MaybeNext.takeError();
Next = MaybeNext.get();
continue;
}
Scratch.clear();
llvm::StringRef BlobData;
llvm::Expected<unsigned> MaybeKind =
Cursor.readRecord(Next.ID, Scratch, &BlobData);
if (!MaybeKind) {
return MaybeKind.takeError();
}
unsigned Kind = MaybeKind.get();
switch (Kind) {
case context_block::CONTEXT_ID_DATA: {
// Already saw Objective-C / C++ context ID table.
if (ContextIDTable)
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Multiple context ID records found");
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 llvm::createStringError(llvm::inconvertibleErrorCode(),
"Multiple context info records found");
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)
return MaybeNext.takeError();
Next = MaybeNext.get();
}
return llvm::Error::success();
}
llvm::Error APINotesReader::Implementation::readObjCPropertyBlock(
llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
if (Cursor.EnterSubBlock(OBJC_PROPERTY_BLOCK_ID))
return llvm::createStringError(
llvm::inconvertibleErrorCode(),
"Failed to enter Objective-C property block");
llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
if (!MaybeNext)
return MaybeNext.takeError();
llvm::BitstreamEntry Next = MaybeNext.get();
while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
if (Next.Kind == llvm::BitstreamEntry::Error)
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Malformed bitstream entry");
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 llvm::createStringError(llvm::inconvertibleErrorCode(),
"Failed to skip sub-block");
MaybeNext = Cursor.advance();
if (!MaybeNext)
return MaybeNext.takeError();
Next = MaybeNext.get();
continue;
}
Scratch.clear();
llvm::StringRef BlobData;
llvm::Expected<unsigned> MaybeKind =
Cursor.readRecord(Next.ID, Scratch, &BlobData);
if (!MaybeKind) {
return MaybeKind.takeError();
}
unsigned Kind = MaybeKind.get();
switch (Kind) {
case objc_property_block::OBJC_PROPERTY_DATA: {
// Already saw Objective-C property table.
if (ObjCPropertyTable)
return llvm::createStringError(
llvm::inconvertibleErrorCode(),
"Multiple Objective-C property records found");
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)
return MaybeNext.takeError();
Next = MaybeNext.get();
}
return llvm::Error::success();
}
llvm::Error APINotesReader::Implementation::readObjCMethodBlock(
llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
if (Cursor.EnterSubBlock(OBJC_METHOD_BLOCK_ID))
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Failed to enter Objective-C method block");
llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
if (!MaybeNext)
return MaybeNext.takeError();
llvm::BitstreamEntry Next = MaybeNext.get();
while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
if (Next.Kind == llvm::BitstreamEntry::Error)
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Malformed bitstream entry");
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 llvm::createStringError(llvm::inconvertibleErrorCode(),
"Failed to skip sub-block");
MaybeNext = Cursor.advance();
if (!MaybeNext)
return MaybeNext.takeError();
Next = MaybeNext.get();
continue;
}
Scratch.clear();
llvm::StringRef BlobData;
llvm::Expected<unsigned> MaybeKind =
Cursor.readRecord(Next.ID, Scratch, &BlobData);
if (!MaybeKind) {
return MaybeKind.takeError();
}
unsigned Kind = MaybeKind.get();
switch (Kind) {
case objc_method_block::OBJC_METHOD_DATA: {
// Already saw Objective-C method table.
if (ObjCMethodTable)
return llvm::createStringError(
llvm::inconvertibleErrorCode(),
"Multiple Objective-C method records found");
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)
return MaybeNext.takeError();
Next = MaybeNext.get();
}
return llvm::Error::success();
}
llvm::Error APINotesReader::Implementation::readCXXMethodBlock(
llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
if (Cursor.EnterSubBlock(CXX_METHOD_BLOCK_ID))
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Failed to enter C++ method block");
llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
if (!MaybeNext)
return MaybeNext.takeError();
llvm::BitstreamEntry Next = MaybeNext.get();
while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
if (Next.Kind == llvm::BitstreamEntry::Error)
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Malformed bitstream entry");
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 llvm::createStringError(llvm::inconvertibleErrorCode(),
"Failed to skip sub-block");
MaybeNext = Cursor.advance();
if (!MaybeNext)
return MaybeNext.takeError();
Next = MaybeNext.get();
continue;
}
Scratch.clear();
llvm::StringRef BlobData;
llvm::Expected<unsigned> MaybeKind =
Cursor.readRecord(Next.ID, Scratch, &BlobData);
if (!MaybeKind) {
return MaybeKind.takeError();
}
unsigned Kind = MaybeKind.get();
switch (Kind) {
case cxx_method_block::CXX_METHOD_DATA: {
// Already saw C++ method table.
if (CXXMethodTable)
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Multiple C++ method records found");
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)
return MaybeNext.takeError();
Next = MaybeNext.get();
}
return llvm::Error::success();
}
llvm::Error APINotesReader::Implementation::readFieldBlock(
llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
if (Cursor.EnterSubBlock(FIELD_BLOCK_ID))
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Failed to enter field block");
llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
if (!MaybeNext)
return MaybeNext.takeError();
llvm::BitstreamEntry Next = MaybeNext.get();
while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
if (Next.Kind == llvm::BitstreamEntry::Error)
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Malformed bitstream entry");
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 llvm::createStringError(llvm::inconvertibleErrorCode(),
"Failed to skip sub-block");
MaybeNext = Cursor.advance();
if (!MaybeNext)
return MaybeNext.takeError();
Next = MaybeNext.get();
continue;
}
Scratch.clear();
llvm::StringRef BlobData;
llvm::Expected<unsigned> MaybeKind =
Cursor.readRecord(Next.ID, Scratch, &BlobData);
if (!MaybeKind) {
return MaybeKind.takeError();
}
unsigned Kind = MaybeKind.get();
switch (Kind) {
case field_block::FIELD_DATA: {
// Already saw field table.
if (FieldTable)
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Multiple field records found");
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)
return MaybeNext.takeError();
Next = MaybeNext.get();
}
return llvm::Error::success();
}
llvm::Error APINotesReader::Implementation::readObjCSelectorBlock(
llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
if (Cursor.EnterSubBlock(OBJC_SELECTOR_BLOCK_ID))
return llvm::createStringError(
llvm::inconvertibleErrorCode(),
"Failed to enter Objective-C selector block");
llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
if (!MaybeNext)
return MaybeNext.takeError();
llvm::BitstreamEntry Next = MaybeNext.get();
while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
if (Next.Kind == llvm::BitstreamEntry::Error)
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Malformed bitstream entry");
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 llvm::createStringError(llvm::inconvertibleErrorCode(),
"Failed to skip sub-block");
MaybeNext = Cursor.advance();
if (!MaybeNext)
return MaybeNext.takeError();
Next = MaybeNext.get();
continue;
}
Scratch.clear();
llvm::StringRef BlobData;
llvm::Expected<unsigned> MaybeKind =
Cursor.readRecord(Next.ID, Scratch, &BlobData);
if (!MaybeKind) {
return MaybeKind.takeError();
}
unsigned Kind = MaybeKind.get();
switch (Kind) {
case objc_selector_block::OBJC_SELECTOR_DATA: {
// Already saw Objective-C selector table.
if (ObjCSelectorTable)
return llvm::createStringError(
llvm::inconvertibleErrorCode(),
"Multiple Objective-C selector records found");
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)
return MaybeNext.takeError();
Next = MaybeNext.get();
}
return llvm::Error::success();
}
llvm::Error APINotesReader::Implementation::readGlobalVariableBlock(
llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
if (Cursor.EnterSubBlock(GLOBAL_VARIABLE_BLOCK_ID))
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Failed to enter global variable block");
llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
if (!MaybeNext)
return MaybeNext.takeError();
llvm::BitstreamEntry Next = MaybeNext.get();
while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
if (Next.Kind == llvm::BitstreamEntry::Error)
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Malformed bitstream entry");
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 llvm::createStringError(llvm::inconvertibleErrorCode(),
"Failed to skip sub-block");
MaybeNext = Cursor.advance();
if (!MaybeNext)
return MaybeNext.takeError();
Next = MaybeNext.get();
continue;
}
Scratch.clear();
llvm::StringRef BlobData;
llvm::Expected<unsigned> MaybeKind =
Cursor.readRecord(Next.ID, Scratch, &BlobData);
if (!MaybeKind) {
return MaybeKind.takeError();
}
unsigned Kind = MaybeKind.get();
switch (Kind) {
case global_variable_block::GLOBAL_VARIABLE_DATA: {
// Already saw global variable table.
if (GlobalVariableTable)
return llvm::createStringError(
llvm::inconvertibleErrorCode(),
"Multiple global variable records found");
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)
return MaybeNext.takeError();
Next = MaybeNext.get();
}
return llvm::Error::success();
}
llvm::Error APINotesReader::Implementation::readGlobalFunctionBlock(
llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
if (Cursor.EnterSubBlock(GLOBAL_FUNCTION_BLOCK_ID))
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Failed to enter global function block");
llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
if (!MaybeNext)
return MaybeNext.takeError();
llvm::BitstreamEntry Next = MaybeNext.get();
while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
if (Next.Kind == llvm::BitstreamEntry::Error)
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Malformed bitstream entry");
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 llvm::createStringError(llvm::inconvertibleErrorCode(),
"Failed to skip sub-block");
MaybeNext = Cursor.advance();
if (!MaybeNext)
return MaybeNext.takeError();
Next = MaybeNext.get();
continue;
}
Scratch.clear();
llvm::StringRef BlobData;
llvm::Expected<unsigned> MaybeKind =
Cursor.readRecord(Next.ID, Scratch, &BlobData);
if (!MaybeKind) {
return MaybeKind.takeError();
}
unsigned Kind = MaybeKind.get();
switch (Kind) {
case global_function_block::GLOBAL_FUNCTION_DATA: {
// Already saw global function table.
if (GlobalFunctionTable)
return llvm::createStringError(
llvm::inconvertibleErrorCode(),
"Multiple global function records found");
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)
return MaybeNext.takeError();
Next = MaybeNext.get();
}
return llvm::Error::success();
}
llvm::Error APINotesReader::Implementation::readEnumConstantBlock(
llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
if (Cursor.EnterSubBlock(ENUM_CONSTANT_BLOCK_ID))
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Failed to enter enum constant block");
llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
if (!MaybeNext)
return MaybeNext.takeError();
llvm::BitstreamEntry Next = MaybeNext.get();
while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
if (Next.Kind == llvm::BitstreamEntry::Error)
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Malformed bitstream entry");
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 llvm::createStringError(llvm::inconvertibleErrorCode(),
"Failed to skip sub-block");
MaybeNext = Cursor.advance();
if (!MaybeNext)
return MaybeNext.takeError();
Next = MaybeNext.get();
continue;
}
Scratch.clear();
llvm::StringRef BlobData;
llvm::Expected<unsigned> MaybeKind =
Cursor.readRecord(Next.ID, Scratch, &BlobData);
if (!MaybeKind) {
return MaybeKind.takeError();
}
unsigned Kind = MaybeKind.get();
switch (Kind) {
case enum_constant_block::ENUM_CONSTANT_DATA: {
// Already saw enumerator table.
if (EnumConstantTable)
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Multiple enum constant records found");
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)
return MaybeNext.takeError();
Next = MaybeNext.get();
}
return llvm::Error::success();
}
llvm::Error APINotesReader::Implementation::readTagBlock(
llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
if (Cursor.EnterSubBlock(TAG_BLOCK_ID))
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Failed to enter tag block");
llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
if (!MaybeNext)
return MaybeNext.takeError();
llvm::BitstreamEntry Next = MaybeNext.get();
while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
if (Next.Kind == llvm::BitstreamEntry::Error)
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Malformed bitstream entry");
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 llvm::createStringError(llvm::inconvertibleErrorCode(),
"Failed to skip sub-block");
MaybeNext = Cursor.advance();
if (!MaybeNext)
return MaybeNext.takeError();
Next = MaybeNext.get();
continue;
}
Scratch.clear();
llvm::StringRef BlobData;
llvm::Expected<unsigned> MaybeKind =
Cursor.readRecord(Next.ID, Scratch, &BlobData);
if (!MaybeKind) {
return MaybeKind.takeError();
}
unsigned Kind = MaybeKind.get();
switch (Kind) {
case tag_block::TAG_DATA: {
// Already saw tag table.
if (TagTable)
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Multiple tag records found");
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)
return MaybeNext.takeError();
Next = MaybeNext.get();
}
return llvm::Error::success();
}
llvm::Error APINotesReader::Implementation::readTypedefBlock(
llvm::BitstreamCursor &Cursor, llvm::SmallVectorImpl<uint64_t> &Scratch) {
if (Cursor.EnterSubBlock(TYPEDEF_BLOCK_ID))
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Failed to enter typedef block");
llvm::Expected<llvm::BitstreamEntry> MaybeNext = Cursor.advance();
if (!MaybeNext)
return MaybeNext.takeError();
llvm::BitstreamEntry Next = MaybeNext.get();
while (Next.Kind != llvm::BitstreamEntry::EndBlock) {
if (Next.Kind == llvm::BitstreamEntry::Error)
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Malformed bitstream entry");
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 llvm::createStringError(llvm::inconvertibleErrorCode(),
"Failed to skip sub-block");
MaybeNext = Cursor.advance();
if (!MaybeNext)
return MaybeNext.takeError();
Next = MaybeNext.get();
continue;
}
Scratch.clear();
llvm::StringRef BlobData;
llvm::Expected<unsigned> MaybeKind =
Cursor.readRecord(Next.ID, Scratch, &BlobData);
if (!MaybeKind) {
return MaybeKind.takeError();
}
unsigned Kind = MaybeKind.get();
switch (Kind) {
case typedef_block::TYPEDEF_DATA: {
// Already saw typedef table.
if (TypedefTable)
return llvm::createStringError(llvm::inconvertibleErrorCode(),
"Multiple typedef records found");
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)
return MaybeNext.takeError();
Next = MaybeNext.get();
}
return llvm::Error::success();
}
APINotesReader::APINotesReader(llvm::MemoryBuffer *InputBuffer,
llvm::VersionTuple SwiftVersion,
llvm::Error &Err)
: Implementation(new class Implementation) {
// 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()) {
Err = llvm::createStringError(
llvm::inconvertibleErrorCode(),
"Unexpected end of stream while reading signature");
return;
}
llvm::Expected<llvm::SimpleBitstreamCursor::word_t> maybeRead =
Cursor.Read(8);
if (!maybeRead) {
Err = maybeRead.takeError();
return;
}
if (maybeRead.get() != byte) {
Err = llvm::createStringError(llvm::inconvertibleErrorCode(),
"Invalid signature in API notes file");
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) {
Err = MaybeTopLevelEntry.takeError();
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()) {
Err = llvm::createStringError(llvm::inconvertibleErrorCode(),
"Failed to read block info");
return;
}
break;
case CONTROL_BLOCK_ID:
// Only allow a single control block.
if (HasValidControlBlock) {
Err = llvm::createStringError(llvm::inconvertibleErrorCode(),
"Multiple control blocks found");
return;
}
if (llvm::Error BlockErr =
Implementation->readControlBlock(Cursor, Scratch)) {
Err = std::move(BlockErr);
return;
}
HasValidControlBlock = true;
break;
case IDENTIFIER_BLOCK_ID:
if (!HasValidControlBlock) {
Err = llvm::createStringError(llvm::inconvertibleErrorCode(),
"Missing control block");
return;
}
if (llvm::Error BlockErr =
Implementation->readIdentifierBlock(Cursor, Scratch)) {
Err = std::move(BlockErr);
return;
}
break;
case OBJC_CONTEXT_BLOCK_ID:
if (!HasValidControlBlock) {
Err = llvm::createStringError(llvm::inconvertibleErrorCode(),
"Missing control block");
return;
}
if (llvm::Error BlockErr =
Implementation->readContextBlock(Cursor, Scratch)) {
Err = std::move(BlockErr);
return;
}
break;
case OBJC_PROPERTY_BLOCK_ID:
if (!HasValidControlBlock) {
Err = llvm::createStringError(llvm::inconvertibleErrorCode(),
"Missing control block");
return;
}
if (llvm::Error BlockErr =
Implementation->readObjCPropertyBlock(Cursor, Scratch)) {
Err = std::move(BlockErr);
return;
}
break;
case OBJC_METHOD_BLOCK_ID:
if (!HasValidControlBlock) {
Err = llvm::createStringError(llvm::inconvertibleErrorCode(),
"Missing control block");
return;
}
if (llvm::Error BlockErr =
Implementation->readObjCMethodBlock(Cursor, Scratch)) {
Err = std::move(BlockErr);
return;
}
break;
case CXX_METHOD_BLOCK_ID:
if (!HasValidControlBlock) {
Err = llvm::createStringError(llvm::inconvertibleErrorCode(),
"Missing control block");
return;
}
if (llvm::Error BlockErr =
Implementation->readCXXMethodBlock(Cursor, Scratch)) {
Err = std::move(BlockErr);
return;
}
break;
case FIELD_BLOCK_ID:
if (!HasValidControlBlock) {
Err = llvm::createStringError(llvm::inconvertibleErrorCode(),
"Missing control block");
return;
}
if (llvm::Error BlockErr =
Implementation->readFieldBlock(Cursor, Scratch)) {
Err = std::move(BlockErr);
return;
}
break;
case OBJC_SELECTOR_BLOCK_ID:
if (!HasValidControlBlock) {
Err = llvm::createStringError(llvm::inconvertibleErrorCode(),
"Missing control block");
return;
}
if (llvm::Error BlockErr =
Implementation->readObjCSelectorBlock(Cursor, Scratch)) {
Err = std::move(BlockErr);
return;
}
break;
case GLOBAL_VARIABLE_BLOCK_ID:
if (!HasValidControlBlock) {
Err = llvm::createStringError(llvm::inconvertibleErrorCode(),
"Missing control block");
return;
}
if (llvm::Error BlockErr =
Implementation->readGlobalVariableBlock(Cursor, Scratch)) {
Err = std::move(BlockErr);
return;
}
break;
case GLOBAL_FUNCTION_BLOCK_ID:
if (!HasValidControlBlock) {
Err = llvm::createStringError(llvm::inconvertibleErrorCode(),
"Missing control block");
return;
}
if (llvm::Error BlockErr =
Implementation->readGlobalFunctionBlock(Cursor, Scratch)) {
Err = std::move(BlockErr);
return;
}
break;
case ENUM_CONSTANT_BLOCK_ID:
if (!HasValidControlBlock) {
Err = llvm::createStringError(llvm::inconvertibleErrorCode(),
"Missing control block");
return;
}
if (llvm::Error BlockErr =
Implementation->readEnumConstantBlock(Cursor, Scratch)) {
Err = std::move(BlockErr);
return;
}
break;
case TAG_BLOCK_ID:
if (!HasValidControlBlock) {
Err = llvm::createStringError(llvm::inconvertibleErrorCode(),
"Missing control block");
return;
}
if (llvm::Error BlockErr =
Implementation->readTagBlock(Cursor, Scratch)) {
Err = std::move(BlockErr);
return;
}
break;
case TYPEDEF_BLOCK_ID:
if (!HasValidControlBlock) {
Err = llvm::createStringError(llvm::inconvertibleErrorCode(),
"Missing control block");
return;
}
if (llvm::Error BlockErr =
Implementation->readTypedefBlock(Cursor, Scratch)) {
Err = std::move(BlockErr);
return;
}
break;
default:
// Unknown top-level block, possibly for use by a future version of the
// module format.
if (Cursor.SkipBlock()) {
Err = llvm::createStringError(llvm::inconvertibleErrorCode(),
"Failed to skip unknown top-level block");
return;
}
break;
}
}
if (!Cursor.AtEndOfStream()) {
Err = llvm::createStringError(llvm::inconvertibleErrorCode(),
"Bitstream has unread data after all blocks");
return;
}
}
APINotesReader::~APINotesReader() { delete Implementation->InputBuffer; }
llvm::Expected<std::unique_ptr<APINotesReader>>
APINotesReader::Create(std::unique_ptr<llvm::MemoryBuffer> InputBuffer,
llvm::VersionTuple SwiftVersion) {
llvm::Error Err = llvm::Error::success();
std::unique_ptr<APINotesReader> Reader(
new APINotesReader(InputBuffer.release(), SwiftVersion, Err));
if (Err)
return Err;
return std::move(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(llvm::is_sorted(
Results,
[](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> {
return lookupCXXMethodImpl(CtxID, Name);
}
auto APINotesReader::lookupCXXMethod(ContextID CtxID, llvm::StringRef Name,
llvm::ArrayRef<std::string> Parameters)
-> VersionedInfo<CXXMethodInfo> {
return lookupCXXMethodImpl(CtxID, Name, Parameters);
}
std::optional<APINotesFunctionSelectorKey>
APINotesReader::getCXXMethodSelectorKey(ContextID CtxID, llvm::StringRef Name) {
std::optional<FunctionTableKey> Key =
Implementation->getFunctionKey(CtxID.Value, Name);
if (!Key)
return std::nullopt;
return APINotesFunctionSelectorKey{*Key, /*IsCXXMethod=*/true};
}
std::optional<APINotesFunctionSelectorKey>
APINotesReader::getCXXMethodSelectorKey(
ContextID CtxID, llvm::StringRef Name,
llvm::ArrayRef<std::string> Parameters) {
std::optional<FunctionTableKey> Key =
Implementation->getFunctionKey(CtxID.Value, Name, Parameters);
if (!Key)
return std::nullopt;
return APINotesFunctionSelectorKey{*Key, /*IsCXXMethod=*/true};
}
auto APINotesReader::lookupCXXMethodImpl(ContextID CtxID, llvm::StringRef Name)
-> VersionedInfo<CXXMethodInfo> {
if (!Implementation->CXXMethodTable)
return std::nullopt;
std::optional<FunctionTableKey> Key =
Implementation->getFunctionKey(CtxID.Value, Name);
if (!Key)
return std::nullopt;
auto Known = Implementation->CXXMethodTable->find(*Key);
if (Known == Implementation->CXXMethodTable->end())
return std::nullopt;
return {Implementation->SwiftVersion, *Known};
}
template <typename ParameterT>
auto APINotesReader::lookupCXXMethodImpl(ContextID CtxID, llvm::StringRef Name,
llvm::ArrayRef<ParameterT> Parameters)
-> VersionedInfo<CXXMethodInfo> {
if (!Implementation->CXXMethodTable)
return std::nullopt;
std::optional<FunctionTableKey> Key =
Implementation->getFunctionKey(CtxID.Value, Name, Parameters);
if (!Key)
return std::nullopt;
auto Known = Implementation->CXXMethodTable->find(*Key);
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> {
return lookupGlobalFunctionImpl(Name, Ctx);
}
auto APINotesReader::lookupGlobalFunction(
llvm::StringRef Name, llvm::ArrayRef<std::string> Parameters,
std::optional<Context> Ctx) -> VersionedInfo<GlobalFunctionInfo> {
return lookupGlobalFunctionImpl(Name, Parameters, Ctx);
}
std::optional<APINotesFunctionSelectorKey>
APINotesReader::getGlobalFunctionSelectorKey(llvm::StringRef Name,
std::optional<Context> Ctx) {
std::optional<FunctionTableKey> Key =
Implementation->getFunctionKey(Ctx, Name);
if (!Key)
return std::nullopt;
return APINotesFunctionSelectorKey{*Key, /*IsCXXMethod=*/false};
}
std::optional<APINotesFunctionSelectorKey>
APINotesReader::getGlobalFunctionSelectorKey(
llvm::StringRef Name, llvm::ArrayRef<std::string> Parameters,
std::optional<Context> Ctx) {
std::optional<FunctionTableKey> Key =
Implementation->getFunctionKey(Ctx, Name, Parameters);
if (!Key)
return std::nullopt;
return APINotesFunctionSelectorKey{*Key, /*IsCXXMethod=*/false};
}
void APINotesReader::collectExactFunctionParameterSelectors(
llvm::SmallVectorImpl<APINotesFunctionSelectorKey> &Selectors) {
if (Implementation->GlobalFunctionTable)
Implementation->collectExactFunctionParameterSelectors(
*Implementation->GlobalFunctionTable, Selectors);
if (Implementation->CXXMethodTable)
Implementation->collectExactFunctionParameterSelectors(
*Implementation->CXXMethodTable, Selectors);
}
std::optional<llvm::SmallVector<std::string, 4>>
APINotesReader::getParameterSelectorSpellingsForDiagnostics(
const APINotesFunctionSelectorKey &Key) {
if (!Key.Key.parameterTypeIDs)
return std::nullopt;
llvm::SmallVector<std::string, 4> ParameterSpellings;
ParameterSpellings.reserve(Key.Key.parameterTypeIDs->size());
for (IdentifierID TypeID : *Key.Key.parameterTypeIDs) {
std::optional<llvm::StringRef> TypeName =
Implementation->getIdentifierString(TypeID);
if (!TypeName)
return std::nullopt;
ParameterSpellings.push_back(TypeName->str());
}
return ParameterSpellings;
}
auto APINotesReader::lookupGlobalFunctionImpl(llvm::StringRef Name,
std::optional<Context> Ctx)
-> VersionedInfo<GlobalFunctionInfo> {
if (!Implementation->GlobalFunctionTable)
return std::nullopt;
std::optional<FunctionTableKey> Key =
Implementation->getFunctionKey(Ctx, Name);
if (!Key)
return std::nullopt;
auto Known = Implementation->GlobalFunctionTable->find(*Key);
if (Known == Implementation->GlobalFunctionTable->end())
return std::nullopt;
return {Implementation->SwiftVersion, *Known};
}
template <typename ParameterT>
auto APINotesReader::lookupGlobalFunctionImpl(
llvm::StringRef Name, llvm::ArrayRef<ParameterT> Parameters,
std::optional<Context> Ctx) -> VersionedInfo<GlobalFunctionInfo> {
if (!Implementation->GlobalFunctionTable)
return std::nullopt;
std::optional<FunctionTableKey> Key =
Implementation->getFunctionKey(Ctx, Name, Parameters);
if (!Key)
return std::nullopt;
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