llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang <details> <summary>Changes</summary> 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 --- Patch is 71.63 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/66769.diff 3 Files Affected: - (added) clang/include/clang/APINotes/APINotesReader.h (+202) - (added) clang/lib/APINotes/APINotesReader.cpp (+2001) - (modified) clang/lib/APINotes/CMakeLists.txt (+3) ``````````diff diff --git a/clang/include/clang/APINotes/APINotesReader.h b/clang/include/clang/APINotes/APINotesReader.h new file mode 100644 index 000000000000000..35e2ecaf0a55522 --- /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> + get(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 \c Results.size() if nothing matched. + unsigned Selected; + + public: + /// Form an empty set of versioned information. + VersionedInfo(std::nullopt_t) : Selected(0) {} + + /// 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 { + if (Selected == Results.size()) + return std::nullopt; + 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 { + 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 { + 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/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp new file mode 100644 index 000000000000000..cfdb90290834e5d --- /dev/null +++ b/clang/lib/APINotes/APINotesReader.cpp @@ -0,0 +1,2001 @@ +//===--- 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; + auto UnversionedData = Derived::readUnversioned(Key, Data); + assert(Data != DataBefore && + "Unversioned data reader didn't move pointer"); + Result.push_back({version, UnversionedData}); + } + return Result; + } +}; + +/// Read serialized CommonEntityInfo. +void ReadCommonEntityInfo(const uint8_t *&Data, CommonEntityInfo &Info) { + uint8_t UnavailableBits = *Data++; + Info.Unavailable = (UnavailableBits >> 1) & 0x01; + Info.UnavailableInSwift = UnavailableBits & 0x01; + if ((UnavailableBits >> 2) & 0x01) + Info.setSwiftPrivate(static_cast<bool>((UnavailableBits >> 3) & 0x01)); + + unsigned MsgLength = endian::readNext<uint16_t, 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::optional<std::string>(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::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, 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:... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/66769 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits