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

Saleem Abdulrasool via cfe-commits cfe-commits at lists.llvm.org
Mon Nov 13 07:44:22 PST 2023


================
@@ -635,3 +638,478 @@ bool clang::api_notes::parseAndDumpAPINotes(StringRef YI,
 
   return false;
 }
+
+namespace {
+using namespace api_notes;
+
+class YAMLConverter {
+  const Module &M;
+  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)
+      : M(TheModule), Writer(TheModule.Name, SourceFile), OS(OS),
+        DiagHandler(DiagHandler), DiagHandlerCtxt(DiagHandlerCtxt),
+        ErrorOccured(false) {}
+
+  void convertAvailability(const AvailabilityItem &Availability,
+                           CommonEntityInfo &CEI, llvm::StringRef APIName) {
+    // Populate the unavailability information.
+    CEI.Unavailable = (Availability.Mode == APIAvailability::None);
+    CEI.UnavailableInSwift = (Availability.Mode == APIAvailability::NonSwift);
+    if (CEI.Unavailable || CEI.UnavailableInSwift) {
+      CEI.UnavailableMsg = std::string(Availability.Msg);
+    } else {
+      if (!Availability.Msg.empty()) {
+        emitError(llvm::Twine("availability message for available API '") +
+                  APIName + "' will not be used");
+      }
+    }
+  }
+
+  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);
+      if (OutInfo.Params.size() <= P.Position) {
+        OutInfo.Params.resize(P.Position + 1);
+      }
+      OutInfo.Params[P.Position] |= PI;
+    }
+  }
+
+  void convertNullability(const NullabilitySeq &Nullability,
+                          std::optional<NullabilityKind> ReturnNullability,
+                          FunctionInfo &OutInfo, llvm::StringRef APIName) {
+    if (Nullability.size() > FunctionInfo::getMaxNullabilityIndex()) {
+      emitError(llvm::Twine("nullability info for '") + APIName +
+                "' does not fit");
+      return;
+    }
+
+    bool audited = false;
+    unsigned int idx = 1;
+    for (const auto &N : Nullability)
+      OutInfo.addTypeInfo(idx++, N);
+    audited = Nullability.size() > 0 || ReturnNullability;
+    if (audited)
+      OutInfo.addTypeInfo(0, ReturnNullability ? *ReturnNullability
+                                               : NullabilityKind::NonNull);
+    if (!audited)
+      return;
+    OutInfo.NullabilityAudited = audited;
+    OutInfo.NumAdjustedNullable = idx;
+  }
+
+  /// Convert the common parts of an entity from YAML.
+  template <typename T>
+  void convertCommonEntity(const T &Common, CommonEntityInfo &Info,
+                           StringRef APIName) {
+    convertAvailability(Common.Availability, Info, APIName);
+    Info.setSwiftPrivate(Common.SwiftPrivate);
+    Info.SwiftName = std::string(Common.SwiftName);
+  }
+
+  /// Convert the common parts of a type entity from YAML.
+  template <typename T>
+  void convertCommonType(const T &Common, CommonTypeInfo &Info,
+                         StringRef APIName) {
+    convertCommonEntity(Common, Info, APIName);
+    if (Common.SwiftBridge)
+      Info.setSwiftBridge(std::string(*Common.SwiftBridge));
+    Info.setNSErrorDomain(Common.NSErrorDomain);
+  }
+
+  // Translate from Method into ObjCMethodInfo and write it out.
+  void convertMethod(const Method &M, ContextID ClassID, StringRef ClassName,
+                     VersionTuple SwiftVersion) {
+    ObjCMethodInfo MI;
+    convertCommonEntity(M, MI, M.Selector);
+
+    // Check if the selector ends with ':' to determine if it takes arguments.
+    bool takesArguments = M.Selector.endswith(":");
+
+    // Split the selector into pieces.
+    llvm::SmallVector<StringRef, 4> Args;
+    M.Selector.split(Args, ":", /*MaxSplit*/ -1, /*KeepEmpty*/ false);
+    if (!takesArguments && Args.size() > 1) {
+      emitError("selector '" + M.Selector + "' is missing a ':' at the end");
+      return;
+    }
+
+    // Construct ObjCSelectorRef.
+    api_notes::ObjCSelectorRef Selector;
+    Selector.NumArgs = !takesArguments ? 0 : Args.size();
+    Selector.Identifiers = Args;
+
+    // Translate the initializer info.
+    MI.DesignatedInit = M.DesignatedInit;
+    MI.RequiredInit = M.Required;
+    if (M.FactoryAsInit != FactoryAsInitKind::Infer)
+      emitError("'FactoryAsInit' is no longer valid; use 'SwiftName' instead");
+
+    MI.ResultType = std::string(M.ResultType);
+
+    // Translate parameter information.
+    convertParams(M.Params, MI);
+
+    // Translate nullability info.
+    convertNullability(M.Nullability, M.NullabilityOfRet, MI, M.Selector);
+
+    MI.setRetainCountConvention(M.RetainCountConvention);
+
+    // Write it.
+    Writer.addObjCMethod(ClassID, Selector, M.Kind == MethodKind::Instance, MI,
+                         SwiftVersion);
+  }
+
+  void convertContext(std::optional<ContextID> ParentContextID, const Class &C,
+                      ContextKind Kind, VersionTuple SwiftVersion) {
+    // Write the class.
+    ObjCContextInfo CI;
+    convertCommonType(C, CI, C.Name);
+
+    if (C.AuditedForNullability)
+      CI.setDefaultNullability(NullabilityKind::NonNull);
+    if (C.SwiftImportAsNonGeneric)
+      CI.setSwiftImportAsNonGeneric(*C.SwiftImportAsNonGeneric);
+    if (C.SwiftObjCMembers)
+      CI.setSwiftObjCMembers(*C.SwiftObjCMembers);
+
+    ContextID CtxID =
+        Writer.addObjCContext(ParentContextID, C.Name, Kind, CI, SwiftVersion);
+
+    // Write all methods.
+    llvm::StringMap<std::pair<bool, bool>> KnownMethods;
+    for (const auto &method : C.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 ? "-" : "+") + "[" + C.Name + " " +
+                  method.Selector + "]'");
+        continue;
+      }
+      Known = true;
+
+      convertMethod(method, CtxID, C.Name, SwiftVersion);
+    }
+
+    // Write all properties.
+    llvm::StringSet<> KnownInstanceProperties;
+    llvm::StringSet<> KnownClassProperties;
+    for (const auto &Property : C.Properties) {
+      // Check for duplicate property definitions.
+      if ((!Property.Kind || *Property.Kind == MethodKind::Instance) &&
+          !KnownInstanceProperties.insert(Property.Name).second) {
+        emitError(llvm::Twine("duplicate definition of instance property '") +
+                  C.Name + "." + Property.Name + "'");
+        continue;
+      }
+
+      if ((!Property.Kind || *Property.Kind == MethodKind::Class) &&
+          !KnownClassProperties.insert(Property.Name).second) {
+        emitError(llvm::Twine("duplicate definition of class property '") +
+                  C.Name + "." + Property.Name + "'");
+        continue;
+      }
+
+      // Translate from Property into ObjCPropertyInfo.
+      ObjCPropertyInfo PI;
+      convertAvailability(Property.Availability, PI, Property.Name);
+      PI.setSwiftPrivate(Property.SwiftPrivate);
+      PI.SwiftName = std::string(Property.SwiftName);
+      if (Property.Nullability)
+        PI.setNullabilityAudited(*Property.Nullability);
+      if (Property.SwiftImportAsAccessors)
+        PI.setSwiftImportAsAccessors(*Property.SwiftImportAsAccessors);
+      PI.setType(std::string(Property.Type));
+      if (Property.Kind) {
+        Writer.addObjCProperty(CtxID, Property.Name,
+                               *Property.Kind == MethodKind::Instance, PI,
+                               SwiftVersion);
+      } else {
+        // Add both instance and class properties with this name.
+        Writer.addObjCProperty(CtxID, Property.Name, true, PI, SwiftVersion);
+        Writer.addObjCProperty(CtxID, Property.Name, false, PI, SwiftVersion);
+      }
+    }
+  }
+
+  void convertNamespaceContext(std::optional<ContextID> ParentContextID,
+                               const Namespace &TheNamespace,
+                               VersionTuple SwiftVersion) {
+    // Write the namespace.
+    ObjCContextInfo CI;
+    convertCommonEntity(TheNamespace, CI, TheNamespace.Name);
+
+    ContextID CtxID =
+        Writer.addObjCContext(ParentContextID, TheNamespace.Name,
+                              ContextKind::Namespace, CI, 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(llvm::Twine("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(llvm::Twine("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(llvm::Twine("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(llvm::Twine("multiple definitions of global variable '") +
+                  Global.Name + "'");
+        continue;
+      }
+
+      GlobalVariableInfo GVI;
+      convertAvailability(Global.Availability, GVI, Global.Name);
+      GVI.setSwiftPrivate(Global.SwiftPrivate);
+      GVI.SwiftName = std::string(Global.SwiftName);
+      if (Global.Nullability)
+        GVI.setNullabilityAudited(*Global.Nullability);
+      GVI.setType(std::string(Global.Type));
+      Writer.addGlobalVariable(Ctx, Global.Name, GVI, 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(llvm::Twine("multiple definitions of global function '") +
+                  Function.Name + "'");
+        continue;
+      }
+
+      GlobalFunctionInfo GFI;
+      convertAvailability(Function.Availability, GFI, Function.Name);
+      GFI.setSwiftPrivate(Function.SwiftPrivate);
+      GFI.SwiftName = std::string(Function.SwiftName);
+      convertParams(Function.Params, GFI);
+      convertNullability(Function.Nullability, Function.NullabilityOfRet, GFI,
+                         Function.Name);
+      GFI.ResultType = std::string(Function.ResultType);
+      GFI.setRetainCountConvention(Function.RetainCountConvention);
+      Writer.addGlobalFunction(Ctx, Function.Name, GFI, SwiftVersion);
+    }
+
+    // Write all enumerators.
+    llvm::StringSet<> KnownEnumConstants;
+    for (const auto &EnumConstant : TLItems.EnumConstants) {
+      // Check for duplicate enumerators
+      if (!KnownEnumConstants.insert(EnumConstant.Name).second) {
+        emitError(llvm::Twine("multiple definitions of enumerator '") +
+                  EnumConstant.Name + "'");
+        continue;
+      }
+
+      EnumConstantInfo ECI;
+      convertAvailability(EnumConstant.Availability, ECI, EnumConstant.Name);
+      ECI.setSwiftPrivate(EnumConstant.SwiftPrivate);
+      ECI.SwiftName = std::string(EnumConstant.SwiftName);
+      Writer.addEnumConstant(EnumConstant.Name, ECI, 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(llvm::Twine("multiple definitions of tag '") + Tag.Name +
+                  "'");
+        continue;
+      }
+
+      TagInfo TI;
+      convertCommonType(Tag, TI, Tag.Name);
+
+      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)
+        TI.SwiftImportAs = Tag.SwiftImportAs;
+      if (Tag.SwiftRetainOp)
+        TI.SwiftRetainOp = Tag.SwiftRetainOp;
+      if (Tag.SwiftReleaseOp)
+        TI.SwiftReleaseOp = Tag.SwiftReleaseOp;
+
+      if (Tag.EnumConvenienceKind) {
+        if (Tag.EnumExtensibility) {
+          emitError(
+              llvm::Twine("cannot mix EnumKind and EnumExtensibility (for ") +
+              Tag.Name + ")");
+          continue;
+        }
+        if (Tag.FlagEnum) {
+          emitError(llvm::Twine("cannot mix EnumKind and FlagEnum (for ") +
+                    Tag.Name + ")");
+          continue;
+        }
+        switch (*Tag.EnumConvenienceKind) {
+        case EnumConvenienceAliasKind::None:
+          TI.EnumExtensibility = EnumExtensibilityKind::None;
+          TI.setFlagEnum(false);
+          break;
+        case EnumConvenienceAliasKind::CFEnum:
+          TI.EnumExtensibility = EnumExtensibilityKind::Open;
+          TI.setFlagEnum(false);
+          break;
+        case EnumConvenienceAliasKind::CFOptions:
+          TI.EnumExtensibility = EnumExtensibilityKind::Open;
+          TI.setFlagEnum(true);
+          break;
+        case EnumConvenienceAliasKind::CFClosedEnum:
+          TI.EnumExtensibility = EnumExtensibilityKind::Closed;
+          TI.setFlagEnum(false);
+          break;
+        }
+      } else {
+        TI.EnumExtensibility = Tag.EnumExtensibility;
+        TI.setFlagEnum(Tag.FlagEnum);
+      }
+
+      Writer.addTag(Ctx, Tag.Name, TI, SwiftVersion);
+    }
+
+    // Write all typedefs.
+    llvm::StringSet<> KnownTypedefs;
+    for (const auto &Typedef : TLItems.Typedefs) {
+      // Check for duplicate typedef definitions.
+      if (!KnownTypedefs.insert(Typedef.Name).second) {
+        emitError(llvm::Twine("multiple definitions of typedef '") +
+                  Typedef.Name + "'");
+        continue;
+      }
+
+      TypedefInfo TInfo;
+      convertCommonType(Typedef, TInfo, Typedef.Name);
+      TInfo.SwiftWrapper = Typedef.SwiftType;
+
+      Writer.addTypedef(Ctx, Typedef.Name, TInfo, SwiftVersion);
+    }
+  }
+
+  bool convertModule() {
+    // Write the top-level items.
+    convertTopLevelItems(/* context */ std::nullopt, M.TopLevel,
+                         VersionTuple());
+
+    // Convert the versioned information.
+    for (const auto &Versioned : M.SwiftVersions) {
+      convertTopLevelItems(/* context */ std::nullopt, Versioned.Items,
+                           Versioned.Version);
+    }
----------------
compnerd wrote:

```suggestion
    for (const auto &Versioned : M.SwiftVersions)
      convertTopLevelItems(/* context */ std::nullopt, Versioned.Items,
                           Versioned.Version);
```

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


More information about the cfe-commits mailing list