[clang] [APINotes] Upstream APINotes YAML compiler (PR #71413)

via cfe-commits cfe-commits at lists.llvm.org
Mon Nov 6 08:19:19 PST 2023


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Egor Zhdan (egorzhdan)

<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 23.21 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/71413.diff


6 Files Affected:

- (modified) clang/include/clang/APINotes/APINotesYAMLCompiler.h (+11) 
- (modified) clang/include/clang/APINotes/Types.h (+1) 
- (modified) clang/lib/APINotes/APINotesFormat.h (+3-3) 
- (modified) clang/lib/APINotes/APINotesReader.cpp (+2-1) 
- (modified) clang/lib/APINotes/APINotesWriter.cpp (+1-1) 
- (modified) clang/lib/APINotes/APINotesYAMLCompiler.cpp (+497-1) 


``````````diff
diff --git a/clang/include/clang/APINotes/APINotesYAMLCompiler.h b/clang/include/clang/APINotes/APINotesYAMLCompiler.h
index 6098d0ee36fc477..9c24ed85b6a124a 100644
--- a/clang/include/clang/APINotes/APINotesYAMLCompiler.h
+++ b/clang/include/clang/APINotes/APINotesYAMLCompiler.h
@@ -10,14 +10,25 @@
 #define LLVM_CLANG_APINOTES_APINOTESYAMLCOMPILER_H
 
 #include "llvm/ADT/StringRef.h"
+#include "llvm/Support/SourceMgr.h"
 #include "llvm/Support/raw_ostream.h"
 
+namespace clang {
+class FileEntry;
+} // namespace clang
+
 namespace clang {
 namespace api_notes {
 /// Parses the APINotes YAML content and writes the representation back to the
 /// specified stream.  This provides a means of testing the YAML processing of
 /// the APINotes format.
 bool parseAndDumpAPINotes(llvm::StringRef YI, llvm::raw_ostream &OS);
+
+/// Converts API notes from YAML format to binary format.
+bool compileAPINotes(llvm::StringRef YAMLInput, const FileEntry *SourceFile,
+                     llvm::raw_ostream &OS,
+                     llvm::SourceMgr::DiagHandlerTy DiagHandler = nullptr,
+                     void *DiagHandlerCtxt = nullptr);
 } // namespace api_notes
 } // namespace clang
 
diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h
index b74244bc8f1cbd3..79595abcf7d02d9 100644
--- a/clang/include/clang/APINotes/Types.h
+++ b/clang/include/clang/APINotes/Types.h
@@ -766,6 +766,7 @@ struct Context {
 /// data they contain; it is up to the user to ensure that the data
 /// referenced by the identifier list persists.
 struct ObjCSelectorRef {
+  unsigned NumArgs;
   llvm::ArrayRef<llvm::StringRef> Identifiers;
 };
 } // namespace api_notes
diff --git a/clang/lib/APINotes/APINotesFormat.h b/clang/lib/APINotes/APINotesFormat.h
index 5897b45d3796d0e..615314c46f09cac 100644
--- a/clang/lib/APINotes/APINotesFormat.h
+++ b/clang/lib/APINotes/APINotesFormat.h
@@ -247,7 +247,7 @@ using EnumConstantDataLayout =
 
 /// A stored Objective-C selector.
 struct StoredObjCSelector {
-  unsigned NumPieces;
+  unsigned NumArgs;
   llvm::SmallVector<IdentifierID, 2> Identifiers;
 };
 
@@ -302,7 +302,7 @@ template <> struct DenseMapInfo<clang::api_notes::StoredObjCSelector> {
 
   static unsigned
   getHashValue(const clang::api_notes::StoredObjCSelector &Selector) {
-    auto hash = llvm::hash_value(Selector.NumPieces);
+    auto hash = llvm::hash_value(Selector.NumArgs);
     hash = hash_combine(hash, Selector.Identifiers.size());
     for (auto piece : Selector.Identifiers)
       hash = hash_combine(hash, static_cast<unsigned>(piece));
@@ -313,7 +313,7 @@ template <> struct DenseMapInfo<clang::api_notes::StoredObjCSelector> {
 
   static bool isEqual(const clang::api_notes::StoredObjCSelector &LHS,
                       const clang::api_notes::StoredObjCSelector &RHS) {
-    return LHS.NumPieces == RHS.NumPieces && LHS.Identifiers == RHS.Identifiers;
+    return LHS.NumArgs == RHS.NumArgs && LHS.Identifiers == RHS.Identifiers;
   }
 };
 
diff --git a/clang/lib/APINotes/APINotesReader.cpp b/clang/lib/APINotes/APINotesReader.cpp
index 2cbf5fd3bf50301..ff9b95d9bf75e3d 100644
--- a/clang/lib/APINotes/APINotesReader.cpp
+++ b/clang/lib/APINotes/APINotesReader.cpp
@@ -421,7 +421,7 @@ class ObjCSelectorTableInfo {
 
   static internal_key_type ReadKey(const uint8_t *Data, unsigned Length) {
     internal_key_type Key;
-    Key.NumPieces =
+    Key.NumArgs =
         endian::readNext<uint16_t, llvm::endianness::little, unaligned>(Data);
     unsigned NumIdents = (Length - sizeof(uint16_t)) / sizeof(uint32_t);
     for (unsigned i = 0; i != NumIdents; ++i) {
@@ -741,6 +741,7 @@ APINotesReader::Implementation::getSelector(ObjCSelectorRef Selector) {
 
   // Translate the identifiers.
   StoredObjCSelector Key;
+  Key.NumArgs = Selector.NumArgs;
   for (auto Ident : Selector.Identifiers) {
     if (auto IdentID = getIdentifier(Ident)) {
       Key.Identifiers.push_back(*IdentID);
diff --git a/clang/lib/APINotes/APINotesWriter.cpp b/clang/lib/APINotes/APINotesWriter.cpp
index 770d78e22050c01..62a2ab1799913a1 100644
--- a/clang/lib/APINotes/APINotesWriter.cpp
+++ b/clang/lib/APINotes/APINotesWriter.cpp
@@ -823,7 +823,7 @@ class ObjCSelectorTableInfo {
 
   void EmitKey(raw_ostream &OS, key_type_ref Key, unsigned) {
     llvm::support::endian::Writer writer(OS, llvm::endianness::little);
-    writer.write<uint16_t>(Key.NumPieces);
+    writer.write<uint16_t>(Key.NumArgs);
     for (auto Identifier : Key.Identifiers)
       writer.write<uint32_t>(Identifier);
   }
diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp
index 647455111214c59..7e43cc981f35968 100644
--- a/clang/lib/APINotes/APINotesYAMLCompiler.cpp
+++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp
@@ -14,14 +14,17 @@
 //
 
 #include "clang/APINotes/APINotesYAMLCompiler.h"
+#include "clang/APINotes/APINotesWriter.h"
 #include "clang/APINotes/Types.h"
 #include "clang/Basic/LLVM.h"
 #include "clang/Basic/Specifiers.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/Support/SourceMgr.h"
 #include "llvm/Support/VersionTuple.h"
-#include "llvm/Support/YAMLParser.h"
 #include "llvm/Support/YAMLTraits.h"
 #include <optional>
 #include <vector>
+
 using namespace clang;
 using namespace api_notes;
 
@@ -635,3 +638,496 @@ bool clang::api_notes::parseAndDumpAPINotes(StringRef YI,
 
   return false;
 }
+
+namespace {
+using namespace api_notes;
+
+class YAMLConverter {
+  const Module &TheModule;
+  APINotesWriter Writer;
+  llvm::raw_ostream &OS;
+  llvm::SourceMgr::DiagHandlerTy DiagHandler;
+  void *DiagHandlerCtxt;
+  bool ErrorOccured;
+
+  /// Emit a diagnostic
+  bool emitError(llvm::Twine Message) {
+    DiagHandler(
+        llvm::SMDiagnostic("", llvm::SourceMgr::DK_Error, Message.str()),
+        DiagHandlerCtxt);
+    ErrorOccured = true;
+    return true;
+  }
+
+public:
+  YAMLConverter(const Module &TheModule, const FileEntry *SourceFile,
+                llvm::raw_ostream &OS,
+                llvm::SourceMgr::DiagHandlerTy DiagHandler,
+                void *DiagHandlerCtxt)
+      : TheModule(TheModule), Writer(TheModule.Name, SourceFile), OS(OS),
+        DiagHandler(DiagHandler), DiagHandlerCtxt(DiagHandlerCtxt),
+        ErrorOccured(false) {}
+
+  bool convertAvailability(const AvailabilityItem &In,
+                           CommonEntityInfo &OutInfo, llvm::StringRef APIName) {
+    // Populate the unavailability information.
+    OutInfo.Unavailable = (In.Mode == APIAvailability::None);
+    OutInfo.UnavailableInSwift = (In.Mode == APIAvailability::NonSwift);
+    if (OutInfo.Unavailable || OutInfo.UnavailableInSwift) {
+      OutInfo.UnavailableMsg = std::string(In.Msg);
+    } else {
+      if (!In.Msg.empty()) {
+        emitError("availability message for available API '" + APIName +
+                  "' will not be used");
+      }
+    }
+    return false;
+  }
+
+  void convertParams(const ParamsSeq &Params, FunctionInfo &OutInfo) {
+    for (const auto &P : Params) {
+      ParamInfo PI;
+      if (P.Nullability)
+        PI.setNullabilityAudited(*P.Nullability);
+      PI.setNoEscape(P.NoEscape);
+      PI.setType(std::string(P.Type));
+      PI.setRetainCountConvention(P.RetainCountConvention);
+      while (OutInfo.Params.size() <= P.Position) {
+        OutInfo.Params.push_back(ParamInfo());
+      }
+      OutInfo.Params[P.Position] |= PI;
+    }
+  }
+
+  void convertNullability(const NullabilitySeq &Nullability,
+                          std::optional<NullabilityKind> NullabilityOfRet,
+                          FunctionInfo &OutInfo, llvm::StringRef APIName) {
+    if (Nullability.size() > FunctionInfo::getMaxNullabilityIndex()) {
+      emitError("nullability info for " + APIName + " does not fit");
+      return;
+    }
+
+    bool audited = false;
+    unsigned int idx = 1;
+    for (auto i = Nullability.begin(), e = Nullability.end(); i != e;
+         ++i, ++idx) {
+      OutInfo.addTypeInfo(idx, *i);
+      audited = true;
+    }
+    if (NullabilityOfRet) {
+      OutInfo.addTypeInfo(0, *NullabilityOfRet);
+      audited = true;
+    } else if (audited) {
+      OutInfo.addTypeInfo(0, NullabilityKind::NonNull);
+    }
+    if (audited) {
+      OutInfo.NullabilityAudited = audited;
+      OutInfo.NumAdjustedNullable = idx;
+    }
+  }
+
+  /// Convert the common parts of an entity from YAML.
+  template <typename T>
+  bool convertCommon(const T &Common, CommonEntityInfo &Info,
+                     StringRef APIName) {
+    convertAvailability(Common.Availability, Info, APIName);
+    Info.setSwiftPrivate(Common.SwiftPrivate);
+    Info.SwiftName = std::string(Common.SwiftName);
+    return false;
+  }
+
+  /// Convert the common parts of a type entity from YAML.
+  template <typename T>
+  bool convertCommonType(const T &Common, CommonTypeInfo &Info,
+                         StringRef APIName) {
+    if (convertCommon(Common, Info, APIName))
+      return true;
+
+    if (Common.SwiftBridge)
+      Info.setSwiftBridge(std::string(*Common.SwiftBridge));
+    Info.setNSErrorDomain(Common.NSErrorDomain);
+    return false;
+  }
+
+  // Translate from Method into ObjCMethodInfo and write it out.
+  void convertMethod(const Method &TheMethod, ContextID ClassID,
+                     StringRef ClassName, VersionTuple SwiftVersion) {
+    ObjCMethodInfo MInfo;
+
+    if (convertCommon(TheMethod, MInfo, TheMethod.Selector))
+      return;
+
+    // Check if the selector ends with ':' to determine if it takes arguments.
+    bool takesArguments = TheMethod.Selector.endswith(":");
+
+    // Split the selector into pieces.
+    llvm::SmallVector<StringRef, 4> a;
+    TheMethod.Selector.split(a, ":", /*MaxSplit*/ -1, /*KeepEmpty*/ false);
+    if (!takesArguments && a.size() > 1) {
+      emitError("selector " + TheMethod.Selector +
+                "is missing a ':' at the end");
+      return;
+    }
+
+    // Construct ObjCSelectorRef.
+    api_notes::ObjCSelectorRef selectorRef;
+    selectorRef.NumArgs = !takesArguments ? 0 : a.size();
+    selectorRef.Identifiers = a;
+
+    // Translate the initializer info.
+    MInfo.DesignatedInit = TheMethod.DesignatedInit;
+    MInfo.RequiredInit = TheMethod.Required;
+    if (TheMethod.FactoryAsInit != FactoryAsInitKind::Infer) {
+      emitError("'FactoryAsInit' is no longer valid; "
+                "use 'SwiftName' instead");
+    }
+    MInfo.ResultType = std::string(TheMethod.ResultType);
+
+    // Translate parameter information.
+    convertParams(TheMethod.Params, MInfo);
+
+    // Translate nullability info.
+    convertNullability(TheMethod.Nullability, TheMethod.NullabilityOfRet, MInfo,
+                       TheMethod.Selector);
+
+    MInfo.setRetainCountConvention(TheMethod.RetainCountConvention);
+
+    // Write it.
+    Writer.addObjCMethod(ClassID, selectorRef,
+                         TheMethod.Kind == MethodKind::Instance, MInfo,
+                         SwiftVersion);
+  }
+
+  void convertContext(std::optional<ContextID> ParentContextID,
+                      const Class &TheClass, ContextKind Kind,
+                      VersionTuple SwiftVersion) {
+    // Write the class.
+    ObjCContextInfo CInfo;
+
+    if (convertCommonType(TheClass, CInfo, TheClass.Name))
+      return;
+
+    if (TheClass.AuditedForNullability)
+      CInfo.setDefaultNullability(NullabilityKind::NonNull);
+    if (TheClass.SwiftImportAsNonGeneric)
+      CInfo.setSwiftImportAsNonGeneric(*TheClass.SwiftImportAsNonGeneric);
+    if (TheClass.SwiftObjCMembers)
+      CInfo.setSwiftObjCMembers(*TheClass.SwiftObjCMembers);
+
+    ContextID CtxID = Writer.addObjCContext(ParentContextID, TheClass.Name,
+                                            Kind, CInfo, SwiftVersion);
+
+    // Write all methods.
+    llvm::StringMap<std::pair<bool, bool>> KnownMethods;
+    for (const auto &method : TheClass.Methods) {
+      // Check for duplicate method definitions.
+      bool IsInstanceMethod = method.Kind == MethodKind::Instance;
+      bool &Known = IsInstanceMethod ? KnownMethods[method.Selector].first
+                                     : KnownMethods[method.Selector].second;
+      if (Known) {
+        emitError(llvm::Twine("duplicate definition of method '") +
+                  (IsInstanceMethod ? "-" : "+") + "[" + TheClass.Name + " " +
+                  method.Selector + "]'");
+        continue;
+      }
+      Known = true;
+
+      convertMethod(method, CtxID, TheClass.Name, SwiftVersion);
+    }
+
+    // Write all properties.
+    llvm::StringSet<> KnownInstanceProperties;
+    llvm::StringSet<> KnownClassProperties;
+    for (const auto &Property : TheClass.Properties) {
+      // Check for duplicate property definitions.
+      if ((!Property.Kind || *Property.Kind == MethodKind::Instance) &&
+          !KnownInstanceProperties.insert(Property.Name).second) {
+        emitError("duplicate definition of instance property '" +
+                  TheClass.Name + "." + Property.Name + "'");
+        continue;
+      }
+
+      if ((!Property.Kind || *Property.Kind == MethodKind::Class) &&
+          !KnownClassProperties.insert(Property.Name).second) {
+        emitError("duplicate definition of class property '" + TheClass.Name +
+                  "." + Property.Name + "'");
+        continue;
+      }
+
+      // Translate from Property into ObjCPropertyInfo.
+      ObjCPropertyInfo PInfo;
+      convertAvailability(Property.Availability, PInfo, Property.Name);
+      PInfo.setSwiftPrivate(Property.SwiftPrivate);
+      PInfo.SwiftName = std::string(Property.SwiftName);
+      if (Property.Nullability)
+        PInfo.setNullabilityAudited(*Property.Nullability);
+      if (Property.SwiftImportAsAccessors)
+        PInfo.setSwiftImportAsAccessors(*Property.SwiftImportAsAccessors);
+      PInfo.setType(std::string(Property.Type));
+      if (Property.Kind) {
+        Writer.addObjCProperty(CtxID, Property.Name,
+                               *Property.Kind == MethodKind::Instance, PInfo,
+                               SwiftVersion);
+      } else {
+        // Add both instance and class properties with this name.
+        Writer.addObjCProperty(CtxID, Property.Name, true, PInfo, SwiftVersion);
+        Writer.addObjCProperty(CtxID, Property.Name, false, PInfo,
+                               SwiftVersion);
+      }
+    }
+  }
+
+  void convertNamespaceContext(std::optional<ContextID> ParentContextID,
+                               const Namespace &TheNamespace,
+                               VersionTuple SwiftVersion) {
+    // Write the namespace.
+    ObjCContextInfo CInfo;
+
+    if (convertCommon(TheNamespace, CInfo, TheNamespace.Name))
+      return;
+
+    ContextID CtxID =
+        Writer.addObjCContext(ParentContextID, TheNamespace.Name,
+                              ContextKind::Namespace, CInfo, SwiftVersion);
+
+    convertTopLevelItems(Context(CtxID, ContextKind::Namespace),
+                         TheNamespace.Items, SwiftVersion);
+  }
+
+  void convertTopLevelItems(std::optional<Context> Ctx,
+                            const TopLevelItems &TLItems,
+                            VersionTuple SwiftVersion) {
+    std::optional<ContextID> CtxID =
+        Ctx ? std::optional(Ctx->id) : std::nullopt;
+
+    // Write all classes.
+    llvm::StringSet<> KnownClasses;
+    for (const auto &Class : TLItems.Classes) {
+      // Check for duplicate class definitions.
+      if (!KnownClasses.insert(Class.Name).second) {
+        emitError("multiple definitions of class '" + Class.Name + "'");
+        continue;
+      }
+
+      convertContext(CtxID, Class, ContextKind::ObjCClass, SwiftVersion);
+    }
+
+    // Write all protocols.
+    llvm::StringSet<> KnownProtocols;
+    for (const auto &Protocol : TLItems.Protocols) {
+      // Check for duplicate protocol definitions.
+      if (!KnownProtocols.insert(Protocol.Name).second) {
+        emitError("multiple definitions of protocol '" + Protocol.Name + "'");
+        continue;
+      }
+
+      convertContext(CtxID, Protocol, ContextKind::ObjCProtocol, SwiftVersion);
+    }
+
+    // Write all namespaces.
+    llvm::StringSet<> KnownNamespaces;
+    for (const auto &Namespace : TLItems.Namespaces) {
+      // Check for duplicate namespace definitions.
+      if (!KnownNamespaces.insert(Namespace.Name).second) {
+        emitError("multiple definitions of namespace '" + Namespace.Name + "'");
+        continue;
+      }
+
+      convertNamespaceContext(CtxID, Namespace, SwiftVersion);
+    }
+
+    // Write all global variables.
+    llvm::StringSet<> KnownGlobals;
+    for (const auto &Global : TLItems.Globals) {
+      // Check for duplicate global variables.
+      if (!KnownGlobals.insert(Global.Name).second) {
+        emitError("multiple definitions of global variable '" + Global.Name +
+                  "'");
+        continue;
+      }
+
+      GlobalVariableInfo GVInfo;
+      convertAvailability(Global.Availability, GVInfo, Global.Name);
+      GVInfo.setSwiftPrivate(Global.SwiftPrivate);
+      GVInfo.SwiftName = std::string(Global.SwiftName);
+      if (Global.Nullability)
+        GVInfo.setNullabilityAudited(*Global.Nullability);
+      GVInfo.setType(std::string(Global.Type));
+      Writer.addGlobalVariable(Ctx, Global.Name, GVInfo, SwiftVersion);
+    }
+
+    // Write all global functions.
+    llvm::StringSet<> KnownFunctions;
+    for (const auto &Function : TLItems.Functions) {
+      // Check for duplicate global functions.
+      if (!KnownFunctions.insert(Function.Name).second) {
+        emitError("multiple definitions of global function '" + Function.Name +
+                  "'");
+        continue;
+      }
+
+      GlobalFunctionInfo GFInfo;
+      convertAvailability(Function.Availability, GFInfo, Function.Name);
+      GFInfo.setSwiftPrivate(Function.SwiftPrivate);
+      GFInfo.SwiftName = std::string(Function.SwiftName);
+      convertParams(Function.Params, GFInfo);
+      convertNullability(Function.Nullability, Function.NullabilityOfRet,
+                         GFInfo, Function.Name);
+      GFInfo.ResultType = std::string(Function.ResultType);
+      GFInfo.setRetainCountConvention(Function.RetainCountConvention);
+      Writer.addGlobalFunction(Ctx, Function.Name, GFInfo, SwiftVersion);
+    }
+
+    // Write all enumerators.
+    llvm::StringSet<> KnownEnumConstants;
+    for (const auto &EnumConstant : TLItems.EnumConstants) {
+      // Check for duplicate enumerators
+      if (!KnownEnumConstants.insert(EnumConstant.Name).second) {
+        emitError("multiple definitions of enumerator '" + EnumConstant.Name +
+                  "'");
+        continue;
+      }
+
+      EnumConstantInfo ECInfo;
+      convertAvailability(EnumConstant.Availability, ECInfo, EnumConstant.Name);
+      ECInfo.setSwiftPrivate(EnumConstant.SwiftPrivate);
+      ECInfo.SwiftName = std::string(EnumConstant.SwiftName);
+      Writer.addEnumConstant(EnumConstant.Name, ECInfo, SwiftVersion);
+    }
+
+    // Write all tags.
+    llvm::StringSet<> KnownTags;
+    for (const auto &Tag : TLItems.Tags) {
+      // Check for duplicate tag definitions.
+      if (!KnownTags.insert(Tag.Name).second) {
+        emitError("multiple definitions of tag '" + Tag.Name + "'");
+        continue;
+      }
+
+      TagInfo TInfo;
+      if (convertCommonType(Tag, TInfo, Tag.Name))
+        continue;
+
+      if ((Tag.SwiftRetainOp || Tag.SwiftReleaseOp) && !Tag.SwiftImportAs) {
+        emitError(llvm::Twine("should declare SwiftImportAs to use "
+                              "SwiftRetainOp and SwiftReleaseOp (for ") +
+                  Tag.Name + ")");
+        continue;
+      }
+      if (Tag.SwiftReleaseOp.has_value() != Tag.SwiftRetainOp.has_value()) {
+        emitError(llvm::Twine("should declare both SwiftReleaseOp and "
+                              "SwiftRetainOp (for ") +
+                  Tag.Name + ")");
+        continue;
+      }
+
+      if (Tag.SwiftImportAs)
+        TInfo.SwiftImportAs = Tag.SwiftImportAs;
+      if (Tag.SwiftRetainOp)
+        TInfo.SwiftRetainOp = Tag.SwiftRetainOp;
+      if (Tag.SwiftReleaseOp)
+        TInfo.SwiftReleaseOp = Tag.SwiftReleaseOp;
+
+      if (Tag.EnumConvenienceKind) {
+        if (Tag.EnumExtensibility) {
+          emitError(
+              llvm::Twine("cannot mix EnumKind and EnumExtensibility (for ") +
+              Tag.Name + ")");
+          continue;
+        }...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/71413


More information about the cfe-commits mailing list