================ @@ -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 + " " + ---------------- compnerd wrote:
I wonder if it is useful to extract the string conversion for the selector, though I suppose that we couldn't take advantage of the `Twine` if we did that. https://github.com/llvm/llvm-project/pull/71413 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits