https://github.com/egorzhdan updated https://github.com/llvm/llvm-project/pull/66769
>From c62e0c4f263cfb46b74931d9ae5962116faf427f Mon Sep 17 00:00:00 2001 From: Egor Zhdan <e_zh...@apple.com> Date: Tue, 19 Sep 2023 13:42:16 +0100 Subject: [PATCH] [APINotes] Upstream APINotesReader This upstreams more of the Clang API Notes functionality that is currently implemented in the Apple fork: https://github.com/apple/llvm-project/tree/next/clang/lib/APINotes --- clang/include/clang/APINotes/APINotesReader.h | 202 ++ clang/include/clang/APINotes/Types.h | 8 +- clang/lib/APINotes/APINotesReader.cpp | 1997 +++++++++++++++++ clang/lib/APINotes/CMakeLists.txt | 3 + 4 files changed, 2203 insertions(+), 7 deletions(-) create mode 100644 clang/include/clang/APINotes/APINotesReader.h create mode 100644 clang/lib/APINotes/APINotesReader.cpp diff --git a/clang/include/clang/APINotes/APINotesReader.h b/clang/include/clang/APINotes/APINotesReader.h new file mode 100644 index 000000000000000..0bb3b3c5c22517d --- /dev/null +++ b/clang/include/clang/APINotes/APINotesReader.h @@ -0,0 +1,202 @@ +//===--- APINotesReader.h - 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 defines 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. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_APINOTES_READER_H +#define LLVM_CLANG_APINOTES_READER_H + +#include "clang/APINotes/Types.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/VersionTuple.h" +#include <memory> + +namespace clang { +namespace api_notes { + +/// A class that reads API notes data from a binary file that was written by +/// the \c APINotesWriter. +class APINotesReader { + class Implementation; + std::unique_ptr<Implementation> Implementation; + + APINotesReader(llvm::MemoryBuffer *InputBuffer, + llvm::VersionTuple SwiftVersion, bool &Failed); + +public: + /// Create a new API notes reader from the given member buffer, which + /// contains the contents of a binary API notes file. + /// + /// \returns the new API notes reader, or null if an error occurred. + static std::unique_ptr<APINotesReader> + Create(std::unique_ptr<llvm::MemoryBuffer> InputBuffer, + llvm::VersionTuple SwiftVersion); + + ~APINotesReader(); + + APINotesReader(const APINotesReader &) = delete; + APINotesReader &operator=(const APINotesReader &) = delete; + + /// Captures the completed versioned information for a particular part of + /// API notes, including both unversioned API notes and each versioned API + /// note for that particular entity. + template <typename T> class VersionedInfo { + /// The complete set of results. + llvm::SmallVector<std::pair<llvm::VersionTuple, T>, 1> Results; + + /// The index of the result that is the "selected" set based on the desired + /// Swift version, or null if nothing matched. + std::optional<unsigned> Selected; + + public: + /// Form an empty set of versioned information. + VersionedInfo(std::nullopt_t) : Selected(std::nullopt) {} + + /// Form a versioned info set given the desired version and a set of + /// results. + VersionedInfo( + llvm::VersionTuple Version, + llvm::SmallVector<std::pair<llvm::VersionTuple, T>, 1> Results); + + /// Retrieve the selected index in the result set. + std::optional<unsigned> getSelected() const { + return Selected; + } + + /// Return the number of versioned results we know about. + unsigned size() const { return Results.size(); } + + /// Access all versioned results. + const std::pair<llvm::VersionTuple, T> *begin() const { + assert(!Results.empty()); + return Results.begin(); + } + const std::pair<llvm::VersionTuple, T> *end() const { + return Results.end(); + } + + /// Access a specific versioned result. + const std::pair<llvm::VersionTuple, T> &operator[](unsigned index) const { + assert(index < Results.size()); + return Results[index]; + } + }; + + /// Look for the context ID of the given Objective-C class. + /// + /// \param Name The name of the class we're looking for. + /// + /// \returns The ID, if known. + std::optional<ContextID> lookupObjCClassID(llvm::StringRef Name); + + /// Look for information regarding the given Objective-C class. + /// + /// \param Name The name of the class we're looking for. + /// + /// \returns The information about the class, if known. + VersionedInfo<ObjCContextInfo> lookupObjCClassInfo(llvm::StringRef Name); + + /// Look for the context ID of the given Objective-C protocol. + /// + /// \param Name The name of the protocol we're looking for. + /// + /// \returns The ID of the protocol, if known. + std::optional<ContextID> lookupObjCProtocolID(llvm::StringRef Name); + + /// Look for information regarding the given Objective-C protocol. + /// + /// \param Name The name of the protocol we're looking for. + /// + /// \returns The information about the protocol, if known. + VersionedInfo<ObjCContextInfo> lookupObjCProtocolInfo(llvm::StringRef Name); + + /// Look for information regarding the given Objective-C property in + /// the given context. + /// + /// \param CtxID The ID that references the context we are looking for. + /// \param Name The name of the property we're looking for. + /// \param IsInstance Whether we are looking for an instance property (vs. + /// a class property). + /// + /// \returns Information about the property, if known. + VersionedInfo<ObjCPropertyInfo> + lookupObjCProperty(ContextID CtxID, llvm::StringRef Name, bool IsInstance); + + /// Look for information regarding the given Objective-C method in + /// the given context. + /// + /// \param CtxID The ID that references the context we are looking for. + /// \param Selector The selector naming the method we're looking for. + /// \param IsInstanceMethod Whether we are looking for an instance method. + /// + /// \returns Information about the method, if known. + VersionedInfo<ObjCMethodInfo> lookupObjCMethod(ContextID CtxID, + ObjCSelectorRef Selector, + bool IsInstanceMethod); + + /// Look for information regarding the given global variable. + /// + /// \param Name The name of the global variable. + /// + /// \returns information about the global variable, if known. + VersionedInfo<GlobalVariableInfo> + lookupGlobalVariable(llvm::StringRef Name, + std::optional<Context> Ctx = std::nullopt); + + /// Look for information regarding the given global function. + /// + /// \param Name The name of the global function. + /// + /// \returns information about the global function, if known. + VersionedInfo<GlobalFunctionInfo> + lookupGlobalFunction(llvm::StringRef Name, + std::optional<Context> Ctx = std::nullopt); + + /// Look for information regarding the given enumerator. + /// + /// \param Name The name of the enumerator. + /// + /// \returns information about the enumerator, if known. + VersionedInfo<EnumConstantInfo> lookupEnumConstant(llvm::StringRef Name); + + /// Look for information regarding the given tag + /// (struct/union/enum/C++ class). + /// + /// \param Name The name of the tag. + /// + /// \returns information about the tag, if known. + VersionedInfo<TagInfo> lookupTag(llvm::StringRef Name, + std::optional<Context> Ctx = std::nullopt); + + /// Look for information regarding the given typedef. + /// + /// \param Name The name of the typedef. + /// + /// \returns information about the typedef, if known. + VersionedInfo<TypedefInfo> + lookupTypedef(llvm::StringRef Name, + std::optional<Context> Ctx = std::nullopt); + + /// Look for the context ID of the given C++ namespace. + /// + /// \param Name The name of the class we're looking for. + /// + /// \returns The ID, if known. + std::optional<ContextID> + lookupNamespaceID(llvm::StringRef Name, + std::optional<ContextID> ParentNamespaceID = std::nullopt); +}; + +} // end namespace api_notes +} // end namespace clang + +#endif // LLVM_CLANG_APINOTES_READER_H diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h index 354458588e30932..b74244bc8f1cbd3 100644 --- a/clang/include/clang/APINotes/Types.h +++ b/clang/include/clang/APINotes/Types.h @@ -144,16 +144,10 @@ class CommonTypeInfo : public CommonEntityInfo { return SwiftBridge; } - void setSwiftBridge(const std::optional<std::string> &SwiftType) { + void setSwiftBridge(std::optional<std::string> SwiftType) { SwiftBridge = SwiftType; } - void setSwiftBridge(const std::optional<llvm::StringRef> &SwiftType) { - SwiftBridge = SwiftType - ? std::optional<std::string>(std::string(*SwiftType)) - : std::nullopt; - } - const std::optional<std::string> &getNSErrorDomain() const { return NSErrorDomain; } diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp new file mode 100644 index 000000000000000..43288a6d3e24d49 --- /dev/null +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -0,0 +1,1997 @@ +//===--- 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 +// +//===----------------------------------------------------------------------===// + +#include "clang/APINotes/APINotesReader.h" +#include "APINotesFormat.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, little, unaligned>(Data); + if (NumVersions == 0) + return llvm::VersionTuple(Major); + + unsigned Minor = endian::readNext<uint32_t, little, unaligned>(Data); + if (NumVersions == 1) + return llvm::VersionTuple(Major, Minor); + + unsigned Subminor = endian::readNext<uint32_t, little, unaligned>(Data); + if (NumVersions == 2) + return llvm::VersionTuple(Major, Minor, Subminor); + + unsigned Build = endian::readNext<uint32_t, little, unaligned>(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, little, unaligned>(Data); + unsigned DataLength = endian::readNext<uint16_t, little, unaligned>(Data); + return {KeyLength, DataLength}; + } + + static data_type ReadData(internal_key_type Key, const uint8_t *Data, + unsigned Length) { + unsigned NumElements = endian::readNext<uint16_t, little, unaligned>(Data); + data_type Result; + Result.reserve(NumElements); + for (unsigned i = 0; i != NumElements; ++i) { + auto version = ReadVersionTuple(Data); + const auto *DataBefore = Data; + (void)DataBefore; + assert(Data != DataBefore && + "Unversioned data reader didn't move pointer"); + auto UnversionedData = Derived::readUnversioned(Key, Data); + 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, little, unaligned>(Data); + Info.UnavailableMsg = + std::string(reinterpret_cast<const char *>(Data), + reinterpret_cast<const char *>(Data) + MsgLength); + Data += MsgLength; + + unsigned SwiftNameLength = + endian::readNext<uint16_t, little, unaligned>(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, little, unaligned>(Data); + if (SwiftBridgeLength > 0) { + Info.setSwiftBridge(std::string(reinterpret_cast<const char *>(Data), + SwiftBridgeLength - 1)); + Data += SwiftBridgeLength - 1; + } + + unsigned ErrorDomainLength = + endian::readNext<uint16_t, little, unaligned>(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::hash_value(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, little, unaligned>(Data); + unsigned DataLength = endian::readNext<uint16_t, little, unaligned>(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, little, unaligned>(Data); + } +}; + +/// Used to deserialize the on-disk Objective-C class table. +class ObjCContextIDTableInfo { +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, little, unaligned>(Data); + unsigned DataLength = endian::readNext<uint16_t, little, unaligned>(Data); + return {KeyLength, DataLength}; + } + + static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) { + auto ParentCtxID = endian::readNext<uint32_t, little, unaligned>(Data); + auto ContextKind = endian::readNext<uint8_t, little, unaligned>(Data); + auto NameID = endian::readNext<uint32_t, little, unaligned>(Data); + return {ParentCtxID, ContextKind, NameID}; + } + + static data_type ReadData(internal_key_type Key, const uint8_t *Data, + unsigned Length) { + return endian::readNext<uint32_t, little, unaligned>(Data); + } +}; + +/// Used to deserialize the on-disk Objective-C property table. +class ObjCContextInfoTableInfo + : public VersionedTableInfo<ObjCContextInfoTableInfo, unsigned, + ObjCContextInfo> { +public: + static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) { + return endian::readNext<uint32_t, little, unaligned>(Data); + } + + hash_value_type ComputeHash(internal_key_type Key) { + return static_cast<size_t>(llvm::hash_value(Key)); + } + + static ObjCContextInfo readUnversioned(internal_key_type Key, + const uint8_t *&Data) { + ObjCContextInfo 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, little, unaligned>(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, little, unaligned>(Data); + auto NameID = endian::readNext<uint32_t, little, unaligned>(Data); + char IsInstance = endian::readNext<uint8_t, little, unaligned>(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; + } +}; + +/// Read serialized ParamInfo. +void ReadParamInfo(const uint8_t *&Data, ParamInfo &Info) { + ReadVariableInfo(Data, Info); + + uint8_t Payload = endian::readNext<uint8_t, little, unaligned>(Data); + if (auto RawConvention = Payload & 0x7) { + auto Convention = static_cast<RetainCountConventionKind>(RawConvention - 1); + Info.setRetainCountConvention(Convention); + } + Payload >>= 3; + 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, little, unaligned>(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, little, unaligned>(Data); + Info.NullabilityPayload = endian::readNext<uint64_t, little, unaligned>(Data); + + unsigned NumParams = endian::readNext<uint16_t, little, unaligned>(Data); + while (NumParams > 0) { + ParamInfo pi; + ReadParamInfo(Data, pi); + Info.Params.push_back(pi); + --NumParams; + } + + unsigned ResultTypeLen = endian::readNext<uint16_t, little, unaligned>(Data); + Info.ResultType = std::string(Data, Data + ResultTypeLen); + Data += ResultTypeLen; +} + +/// 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, little, unaligned>(Data); + auto SelectorID = endian::readNext<uint32_t, little, unaligned>(Data); + auto IsInstance = endian::readNext<uint8_t, little, unaligned>(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++; + Info.RequiredInit = Payload & 0x01; + Payload >>= 1; + Info.DesignatedInit = Payload & 0x01; + Payload >>= 1; + + ReadFunctionInfo(Data, Info); + 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, little, unaligned>(Data); + unsigned DataLength = endian::readNext<uint16_t, little, unaligned>(Data); + return {KeyLength, DataLength}; + } + + static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) { + internal_key_type Key; + Key.NumPieces = endian::readNext<uint16_t, little, unaligned>(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, little, unaligned>(Data)); + } + return Key; + } + + static data_type ReadData(internal_key_type Key, const uint8_t *Data, + unsigned Length) { + return endian::readNext<uint32_t, little, unaligned>(Data); + } +}; + +/// Used to deserialize the on-disk global variable table. +class GlobalVariableTableInfo + : public VersionedTableInfo<GlobalVariableTableInfo, ContextTableKey, + GlobalVariableInfo> { +public: + static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) { + auto CtxID = endian::readNext<uint32_t, little, unaligned>(Data); + auto ContextKind = endian::readNext<uint8_t, little, unaligned>(Data); + auto NameID = endian::readNext<uint32_t, little, unaligned>(Data); + return {CtxID, ContextKind, 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, ContextTableKey, + GlobalFunctionInfo> { +public: + static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) { + auto CtxID = endian::readNext<uint32_t, little, unaligned>(Data); + auto ContextKind = endian::readNext<uint8_t, little, unaligned>(Data); + auto NameID = endian::readNext<uint32_t, little, unaligned>(Data); + return {CtxID, ContextKind, 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 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, little, unaligned>(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, ContextTableKey, TagInfo> { +public: + static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) { + auto CtxID = endian::readNext<uint32_t, little, unaligned>(Data); + auto ContextKind = endian::readNext<uint8_t, little, unaligned>(Data); + auto NameID = endian::readNext<IdentifierID, little, unaligned>(Data); + return {CtxID, ContextKind, 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); + + unsigned ImportAsLength = + endian::readNext<uint16_t, little, unaligned>(Data); + if (ImportAsLength > 0) { + Info.SwiftImportAs = + std::string(reinterpret_cast<const char *>(Data), ImportAsLength - 1); + Data += ImportAsLength - 1; + } + unsigned RetainOpLength = + endian::readNext<uint16_t, little, unaligned>(Data); + if (RetainOpLength > 0) { + Info.SwiftRetainOp = + std::string(reinterpret_cast<const char *>(Data), RetainOpLength - 1); + Data += RetainOpLength - 1; + } + unsigned ReleaseOpLength = + endian::readNext<uint16_t, little, unaligned>(Data); + if (ReleaseOpLength > 0) { + Info.SwiftReleaseOp = std::string(reinterpret_cast<const char *>(Data), + ReleaseOpLength - 1); + Data += ReleaseOpLength - 1; + } + + ReadCommonTypeInfo(Data, Info); + return Info; + } +}; + +/// Used to deserialize the on-disk typedef table. +class TypedefTableInfo + : public VersionedTableInfo<TypedefTableInfo, ContextTableKey, + TypedefInfo> { +public: + static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) { + auto CtxID = endian::readNext<uint32_t, little, unaligned>(Data); + auto ContextKind = endian::readNext<uint8_t, little, unaligned>(Data); + auto nameID = endian::readNext<IdentifierID, little, unaligned>(Data); + return {CtxID, ContextKind, 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 SerializedObjCContextIDTable = + llvm::OnDiskIterableChainedHashTable<ObjCContextIDTableInfo>; + + /// The Objective-C context ID table. + std::unique_ptr<SerializedObjCContextIDTable> ObjCContextIDTable; + + using SerializedObjCContextInfoTable = + llvm::OnDiskIterableChainedHashTable<ObjCContextInfoTableInfo>; + + /// The Objective-C context info table. + std::unique_ptr<SerializedObjCContextInfoTable> ObjCContextInfoTable; + + using SerializedObjCPropertyTable = + llvm::OnDiskIterableChainedHashTable<ObjCPropertyTableInfo>; + + /// The Objective-C property table. + std::unique_ptr<SerializedObjCPropertyTable> ObjCPropertyTable; + + using SerializedObjCMethodTable = + llvm::OnDiskIterableChainedHashTable<ObjCMethodTableInfo>; + + /// The Objective-C method table. + std::unique_ptr<SerializedObjCMethodTable> ObjCMethodTable; + + 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 readObjCContextBlock(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 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; + 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::readObjCContextBlock( + 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 objc_context_block::OBJC_CONTEXT_ID_DATA: { + // Already saw Objective-C context ID table. + if (ObjCContextIDTable) + return true; + + uint32_t tableOffset; + objc_context_block::ObjCContextIDLayout::readRecord(Scratch, tableOffset); + auto base = reinterpret_cast<const uint8_t *>(BlobData.data()); + + ObjCContextIDTable.reset(SerializedObjCContextIDTable::Create( + base + tableOffset, base + sizeof(uint32_t), base)); + break; + } + + case objc_context_block::OBJC_CONTEXT_INFO_DATA: { + // Already saw Objective-C context info table. + if (ObjCContextInfoTable) + return true; + + uint32_t tableOffset; + objc_context_block::ObjCContextInfoLayout::readRecord(Scratch, + tableOffset); + auto base = reinterpret_cast<const uint8_t *>(BlobData.data()); + + ObjCContextInfoTable.reset(SerializedObjCContextInfoTable::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::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->readObjCContextBlock(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 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> Results) + : Results(std::move(Results)) { + + 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 { + assert(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->ObjCContextIDTable) + 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->ObjCContextIDTable->find( + ContextTableKey(-1, (uint8_t)ContextKind::ObjCClass, *ClassID)); + if (KnownID == Implementation->ObjCContextIDTable->end()) + return std::nullopt; + + return ContextID(*KnownID); +} + +auto APINotesReader::lookupObjCClassInfo(llvm::StringRef Name) + -> VersionedInfo<ObjCContextInfo> { + if (!Implementation->ObjCContextInfoTable) + return std::nullopt; + + std::optional<ContextID> CtxID = lookupObjCClassID(Name); + if (!CtxID) + return std::nullopt; + + auto KnownInfo = Implementation->ObjCContextInfoTable->find(CtxID->Value); + if (KnownInfo == Implementation->ObjCContextInfoTable->end()) + return std::nullopt; + + return {Implementation->SwiftVersion, *KnownInfo}; +} + +auto APINotesReader::lookupObjCProtocolID(llvm::StringRef Name) + -> std::optional<ContextID> { + if (!Implementation->ObjCContextIDTable) + 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->ObjCContextIDTable->find( + ContextTableKey(-1, (uint8_t)ContextKind::ObjCProtocol, *classID)); + if (KnownID == Implementation->ObjCContextIDTable->end()) + return std::nullopt; + + return ContextID(*KnownID); +} + +auto APINotesReader::lookupObjCProtocolInfo(llvm::StringRef Name) + -> VersionedInfo<ObjCContextInfo> { + if (!Implementation->ObjCContextInfoTable) + return std::nullopt; + + std::optional<ContextID> CtxID = lookupObjCProtocolID(Name); + if (!CtxID) + return std::nullopt; + + auto KnownInfo = Implementation->ObjCContextInfoTable->find(CtxID->Value); + if (KnownInfo == Implementation->ObjCContextInfoTable->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::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; + + ContextTableKey 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; + + ContextTableKey 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::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; + + ContextTableKey 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; + + ContextTableKey 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->ObjCContextIDTable) + 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->ObjCContextIDTable->find( + {RawParentNamespaceID, (uint8_t)ContextKind::Namespace, *NamespaceID}); + if (KnownID == Implementation->ObjCContextIDTable->end()) + return std::nullopt; + + return ContextID(*KnownID); +} + +} // namespace api_notes +} // namespace clang diff --git a/clang/lib/APINotes/CMakeLists.txt b/clang/lib/APINotes/CMakeLists.txt index c34168876a42e25..dec596ea160c68f 100644 --- a/clang/lib/APINotes/CMakeLists.txt +++ b/clang/lib/APINotes/CMakeLists.txt @@ -1,6 +1,9 @@ set(LLVM_LINK_COMPONENTS + BitReader + BitstreamReader Support) add_clang_library(clangAPINotes + APINotesReader.cpp APINotesTypes.cpp APINotesWriter.cpp APINotesYAMLCompiler.cpp _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits