[clang] [APINotes] Upstream APINotesReader (PR #66769)

via cfe-commits cfe-commits at lists.llvm.org
Tue Sep 19 05:45:52 PDT 2023


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


More information about the cfe-commits mailing list