| //===-- APINotesYAMLCompiler.cpp - API Notes YAML Format 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 |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // The types defined locally are designed to represent the YAML state, which |
| // adds an additional bit of state: e.g. a tri-state boolean attribute (yes, no, |
| // not applied) becomes a tri-state boolean + present. As a result, while these |
| // enumerations appear to be redefining constants from the attributes table |
| // data, they are distinct. |
| // |
| |
| #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/YAMLTraits.h" |
| #include <optional> |
| #include <vector> |
| |
| using namespace clang; |
| using namespace api_notes; |
| |
| namespace { |
| enum class APIAvailability { |
| Available = 0, |
| None, |
| NonSwift, |
| }; |
| } // namespace |
| |
| namespace llvm { |
| namespace yaml { |
| template <> struct ScalarEnumerationTraits<APIAvailability> { |
| static void enumeration(IO &IO, APIAvailability &AA) { |
| IO.enumCase(AA, "none", APIAvailability::None); |
| IO.enumCase(AA, "nonswift", APIAvailability::NonSwift); |
| IO.enumCase(AA, "available", APIAvailability::Available); |
| } |
| }; |
| } // namespace yaml |
| } // namespace llvm |
| |
| namespace { |
| enum class MethodKind { |
| Class, |
| Instance, |
| }; |
| } // namespace |
| |
| namespace llvm { |
| namespace yaml { |
| template <> struct ScalarEnumerationTraits<MethodKind> { |
| static void enumeration(IO &IO, MethodKind &MK) { |
| IO.enumCase(MK, "Class", MethodKind::Class); |
| IO.enumCase(MK, "Instance", MethodKind::Instance); |
| } |
| }; |
| } // namespace yaml |
| } // namespace llvm |
| |
| namespace { |
| struct Param { |
| unsigned Position; |
| std::optional<bool> NoEscape = false; |
| std::optional<NullabilityKind> Nullability; |
| std::optional<RetainCountConventionKind> RetainCountConvention; |
| StringRef Type; |
| }; |
| |
| typedef std::vector<Param> ParamsSeq; |
| } // namespace |
| |
| LLVM_YAML_IS_SEQUENCE_VECTOR(Param) |
| LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(NullabilityKind) |
| |
| namespace llvm { |
| namespace yaml { |
| template <> struct ScalarEnumerationTraits<NullabilityKind> { |
| static void enumeration(IO &IO, NullabilityKind &NK) { |
| IO.enumCase(NK, "Nonnull", NullabilityKind::NonNull); |
| IO.enumCase(NK, "Optional", NullabilityKind::Nullable); |
| IO.enumCase(NK, "Unspecified", NullabilityKind::Unspecified); |
| IO.enumCase(NK, "NullableResult", NullabilityKind::NullableResult); |
| // TODO: Mapping this to it's own value would allow for better cross |
| // checking. Also the default should be Unknown. |
| IO.enumCase(NK, "Scalar", NullabilityKind::Unspecified); |
| |
| // Aliases for compatibility with existing APINotes. |
| IO.enumCase(NK, "N", NullabilityKind::NonNull); |
| IO.enumCase(NK, "O", NullabilityKind::Nullable); |
| IO.enumCase(NK, "U", NullabilityKind::Unspecified); |
| IO.enumCase(NK, "S", NullabilityKind::Unspecified); |
| } |
| }; |
| |
| template <> struct ScalarEnumerationTraits<RetainCountConventionKind> { |
| static void enumeration(IO &IO, RetainCountConventionKind &RCCK) { |
| IO.enumCase(RCCK, "none", RetainCountConventionKind::None); |
| IO.enumCase(RCCK, "CFReturnsRetained", |
| RetainCountConventionKind::CFReturnsRetained); |
| IO.enumCase(RCCK, "CFReturnsNotRetained", |
| RetainCountConventionKind::CFReturnsNotRetained); |
| IO.enumCase(RCCK, "NSReturnsRetained", |
| RetainCountConventionKind::NSReturnsRetained); |
| IO.enumCase(RCCK, "NSReturnsNotRetained", |
| RetainCountConventionKind::NSReturnsNotRetained); |
| } |
| }; |
| |
| template <> struct MappingTraits<Param> { |
| static void mapping(IO &IO, Param &P) { |
| IO.mapRequired("Position", P.Position); |
| IO.mapOptional("Nullability", P.Nullability, std::nullopt); |
| IO.mapOptional("RetainCountConvention", P.RetainCountConvention); |
| IO.mapOptional("NoEscape", P.NoEscape); |
| IO.mapOptional("Type", P.Type, StringRef("")); |
| } |
| }; |
| } // namespace yaml |
| } // namespace llvm |
| |
| namespace { |
| typedef std::vector<NullabilityKind> NullabilitySeq; |
| |
| struct AvailabilityItem { |
| APIAvailability Mode = APIAvailability::Available; |
| StringRef Msg; |
| }; |
| |
| /// Old attribute deprecated in favor of SwiftName. |
| enum class FactoryAsInitKind { |
| /// Infer based on name and type (the default). |
| Infer, |
| /// Treat as a class method. |
| AsClassMethod, |
| /// Treat as an initializer. |
| AsInitializer, |
| }; |
| |
| struct Method { |
| StringRef Selector; |
| MethodKind Kind; |
| ParamsSeq Params; |
| NullabilitySeq Nullability; |
| std::optional<NullabilityKind> NullabilityOfRet; |
| std::optional<RetainCountConventionKind> RetainCountConvention; |
| AvailabilityItem Availability; |
| std::optional<bool> SwiftPrivate; |
| StringRef SwiftName; |
| FactoryAsInitKind FactoryAsInit = FactoryAsInitKind::Infer; |
| bool DesignatedInit = false; |
| bool Required = false; |
| StringRef ResultType; |
| }; |
| |
| typedef std::vector<Method> MethodsSeq; |
| } // namespace |
| |
| LLVM_YAML_IS_SEQUENCE_VECTOR(Method) |
| |
| namespace llvm { |
| namespace yaml { |
| template <> struct ScalarEnumerationTraits<FactoryAsInitKind> { |
| static void enumeration(IO &IO, FactoryAsInitKind &FIK) { |
| IO.enumCase(FIK, "A", FactoryAsInitKind::Infer); |
| IO.enumCase(FIK, "C", FactoryAsInitKind::AsClassMethod); |
| IO.enumCase(FIK, "I", FactoryAsInitKind::AsInitializer); |
| } |
| }; |
| |
| template <> struct MappingTraits<Method> { |
| static void mapping(IO &IO, Method &M) { |
| IO.mapRequired("Selector", M.Selector); |
| IO.mapRequired("MethodKind", M.Kind); |
| IO.mapOptional("Parameters", M.Params); |
| IO.mapOptional("Nullability", M.Nullability); |
| IO.mapOptional("NullabilityOfRet", M.NullabilityOfRet, std::nullopt); |
| IO.mapOptional("RetainCountConvention", M.RetainCountConvention); |
| IO.mapOptional("Availability", M.Availability.Mode, |
| APIAvailability::Available); |
| IO.mapOptional("AvailabilityMsg", M.Availability.Msg, StringRef("")); |
| IO.mapOptional("SwiftPrivate", M.SwiftPrivate); |
| IO.mapOptional("SwiftName", M.SwiftName, StringRef("")); |
| IO.mapOptional("FactoryAsInit", M.FactoryAsInit, FactoryAsInitKind::Infer); |
| IO.mapOptional("DesignatedInit", M.DesignatedInit, false); |
| IO.mapOptional("Required", M.Required, false); |
| IO.mapOptional("ResultType", M.ResultType, StringRef("")); |
| } |
| }; |
| } // namespace yaml |
| } // namespace llvm |
| |
| namespace { |
| struct Property { |
| StringRef Name; |
| std::optional<MethodKind> Kind; |
| std::optional<NullabilityKind> Nullability; |
| AvailabilityItem Availability; |
| std::optional<bool> SwiftPrivate; |
| StringRef SwiftName; |
| std::optional<bool> SwiftImportAsAccessors; |
| StringRef Type; |
| }; |
| |
| typedef std::vector<Property> PropertiesSeq; |
| } // namespace |
| |
| LLVM_YAML_IS_SEQUENCE_VECTOR(Property) |
| |
| namespace llvm { |
| namespace yaml { |
| template <> struct MappingTraits<Property> { |
| static void mapping(IO &IO, Property &P) { |
| IO.mapRequired("Name", P.Name); |
| IO.mapOptional("PropertyKind", P.Kind); |
| IO.mapOptional("Nullability", P.Nullability, std::nullopt); |
| IO.mapOptional("Availability", P.Availability.Mode, |
| APIAvailability::Available); |
| IO.mapOptional("AvailabilityMsg", P.Availability.Msg, StringRef("")); |
| IO.mapOptional("SwiftPrivate", P.SwiftPrivate); |
| IO.mapOptional("SwiftName", P.SwiftName, StringRef("")); |
| IO.mapOptional("SwiftImportAsAccessors", P.SwiftImportAsAccessors); |
| IO.mapOptional("Type", P.Type, StringRef("")); |
| } |
| }; |
| } // namespace yaml |
| } // namespace llvm |
| |
| namespace { |
| struct Class { |
| StringRef Name; |
| bool AuditedForNullability = false; |
| AvailabilityItem Availability; |
| std::optional<bool> SwiftPrivate; |
| StringRef SwiftName; |
| std::optional<StringRef> SwiftBridge; |
| std::optional<StringRef> NSErrorDomain; |
| std::optional<bool> SwiftImportAsNonGeneric; |
| std::optional<bool> SwiftObjCMembers; |
| MethodsSeq Methods; |
| PropertiesSeq Properties; |
| }; |
| |
| typedef std::vector<Class> ClassesSeq; |
| } // namespace |
| |
| LLVM_YAML_IS_SEQUENCE_VECTOR(Class) |
| |
| namespace llvm { |
| namespace yaml { |
| template <> struct MappingTraits<Class> { |
| static void mapping(IO &IO, Class &C) { |
| IO.mapRequired("Name", C.Name); |
| IO.mapOptional("AuditedForNullability", C.AuditedForNullability, false); |
| IO.mapOptional("Availability", C.Availability.Mode, |
| APIAvailability::Available); |
| IO.mapOptional("AvailabilityMsg", C.Availability.Msg, StringRef("")); |
| IO.mapOptional("SwiftPrivate", C.SwiftPrivate); |
| IO.mapOptional("SwiftName", C.SwiftName, StringRef("")); |
| IO.mapOptional("SwiftBridge", C.SwiftBridge); |
| IO.mapOptional("NSErrorDomain", C.NSErrorDomain); |
| IO.mapOptional("SwiftImportAsNonGeneric", C.SwiftImportAsNonGeneric); |
| IO.mapOptional("SwiftObjCMembers", C.SwiftObjCMembers); |
| IO.mapOptional("Methods", C.Methods); |
| IO.mapOptional("Properties", C.Properties); |
| } |
| }; |
| } // namespace yaml |
| } // namespace llvm |
| |
| namespace { |
| struct Function { |
| StringRef Name; |
| ParamsSeq Params; |
| NullabilitySeq Nullability; |
| std::optional<NullabilityKind> NullabilityOfRet; |
| std::optional<api_notes::RetainCountConventionKind> RetainCountConvention; |
| AvailabilityItem Availability; |
| std::optional<bool> SwiftPrivate; |
| StringRef SwiftName; |
| StringRef Type; |
| StringRef ResultType; |
| }; |
| |
| typedef std::vector<Function> FunctionsSeq; |
| } // namespace |
| |
| LLVM_YAML_IS_SEQUENCE_VECTOR(Function) |
| |
| namespace llvm { |
| namespace yaml { |
| template <> struct MappingTraits<Function> { |
| static void mapping(IO &IO, Function &F) { |
| IO.mapRequired("Name", F.Name); |
| IO.mapOptional("Parameters", F.Params); |
| IO.mapOptional("Nullability", F.Nullability); |
| IO.mapOptional("NullabilityOfRet", F.NullabilityOfRet, std::nullopt); |
| IO.mapOptional("RetainCountConvention", F.RetainCountConvention); |
| IO.mapOptional("Availability", F.Availability.Mode, |
| APIAvailability::Available); |
| IO.mapOptional("AvailabilityMsg", F.Availability.Msg, StringRef("")); |
| IO.mapOptional("SwiftPrivate", F.SwiftPrivate); |
| IO.mapOptional("SwiftName", F.SwiftName, StringRef("")); |
| IO.mapOptional("ResultType", F.ResultType, StringRef("")); |
| } |
| }; |
| } // namespace yaml |
| } // namespace llvm |
| |
| namespace { |
| struct GlobalVariable { |
| StringRef Name; |
| std::optional<NullabilityKind> Nullability; |
| AvailabilityItem Availability; |
| std::optional<bool> SwiftPrivate; |
| StringRef SwiftName; |
| StringRef Type; |
| }; |
| |
| typedef std::vector<GlobalVariable> GlobalVariablesSeq; |
| } // namespace |
| |
| LLVM_YAML_IS_SEQUENCE_VECTOR(GlobalVariable) |
| |
| namespace llvm { |
| namespace yaml { |
| template <> struct MappingTraits<GlobalVariable> { |
| static void mapping(IO &IO, GlobalVariable &GV) { |
| IO.mapRequired("Name", GV.Name); |
| IO.mapOptional("Nullability", GV.Nullability, std::nullopt); |
| IO.mapOptional("Availability", GV.Availability.Mode, |
| APIAvailability::Available); |
| IO.mapOptional("AvailabilityMsg", GV.Availability.Msg, StringRef("")); |
| IO.mapOptional("SwiftPrivate", GV.SwiftPrivate); |
| IO.mapOptional("SwiftName", GV.SwiftName, StringRef("")); |
| IO.mapOptional("Type", GV.Type, StringRef("")); |
| } |
| }; |
| } // namespace yaml |
| } // namespace llvm |
| |
| namespace { |
| struct EnumConstant { |
| StringRef Name; |
| AvailabilityItem Availability; |
| std::optional<bool> SwiftPrivate; |
| StringRef SwiftName; |
| }; |
| |
| typedef std::vector<EnumConstant> EnumConstantsSeq; |
| } // namespace |
| |
| LLVM_YAML_IS_SEQUENCE_VECTOR(EnumConstant) |
| |
| namespace llvm { |
| namespace yaml { |
| template <> struct MappingTraits<EnumConstant> { |
| static void mapping(IO &IO, EnumConstant &EC) { |
| IO.mapRequired("Name", EC.Name); |
| IO.mapOptional("Availability", EC.Availability.Mode, |
| APIAvailability::Available); |
| IO.mapOptional("AvailabilityMsg", EC.Availability.Msg, StringRef("")); |
| IO.mapOptional("SwiftPrivate", EC.SwiftPrivate); |
| IO.mapOptional("SwiftName", EC.SwiftName, StringRef("")); |
| } |
| }; |
| } // namespace yaml |
| } // namespace llvm |
| |
| namespace { |
| /// Syntactic sugar for EnumExtensibility and FlagEnum |
| enum class EnumConvenienceAliasKind { |
| /// EnumExtensibility: none, FlagEnum: false |
| None, |
| /// EnumExtensibility: open, FlagEnum: false |
| CFEnum, |
| /// EnumExtensibility: open, FlagEnum: true |
| CFOptions, |
| /// EnumExtensibility: closed, FlagEnum: false |
| CFClosedEnum |
| }; |
| } // namespace |
| |
| namespace llvm { |
| namespace yaml { |
| template <> struct ScalarEnumerationTraits<EnumConvenienceAliasKind> { |
| static void enumeration(IO &IO, EnumConvenienceAliasKind &ECAK) { |
| IO.enumCase(ECAK, "none", EnumConvenienceAliasKind::None); |
| IO.enumCase(ECAK, "CFEnum", EnumConvenienceAliasKind::CFEnum); |
| IO.enumCase(ECAK, "NSEnum", EnumConvenienceAliasKind::CFEnum); |
| IO.enumCase(ECAK, "CFOptions", EnumConvenienceAliasKind::CFOptions); |
| IO.enumCase(ECAK, "NSOptions", EnumConvenienceAliasKind::CFOptions); |
| IO.enumCase(ECAK, "CFClosedEnum", EnumConvenienceAliasKind::CFClosedEnum); |
| IO.enumCase(ECAK, "NSClosedEnum", EnumConvenienceAliasKind::CFClosedEnum); |
| } |
| }; |
| } // namespace yaml |
| } // namespace llvm |
| |
| namespace { |
| struct Field { |
| StringRef Name; |
| std::optional<NullabilityKind> Nullability; |
| AvailabilityItem Availability; |
| std::optional<bool> SwiftPrivate; |
| StringRef SwiftName; |
| StringRef Type; |
| }; |
| |
| typedef std::vector<Field> FieldsSeq; |
| } // namespace |
| |
| LLVM_YAML_IS_SEQUENCE_VECTOR(Field) |
| |
| namespace llvm { |
| namespace yaml { |
| template <> struct MappingTraits<Field> { |
| static void mapping(IO &IO, Field &F) { |
| IO.mapRequired("Name", F.Name); |
| IO.mapOptional("Nullability", F.Nullability, std::nullopt); |
| IO.mapOptional("Availability", F.Availability.Mode, |
| APIAvailability::Available); |
| IO.mapOptional("AvailabilityMsg", F.Availability.Msg, StringRef("")); |
| IO.mapOptional("SwiftPrivate", F.SwiftPrivate); |
| IO.mapOptional("SwiftName", F.SwiftName, StringRef("")); |
| IO.mapOptional("Type", F.Type, StringRef("")); |
| } |
| }; |
| } // namespace yaml |
| } // namespace llvm |
| |
| namespace { |
| struct Tag; |
| typedef std::vector<Tag> TagsSeq; |
| |
| struct Tag { |
| StringRef Name; |
| AvailabilityItem Availability; |
| StringRef SwiftName; |
| std::optional<bool> SwiftPrivate; |
| std::optional<StringRef> SwiftBridge; |
| std::optional<StringRef> NSErrorDomain; |
| std::optional<std::string> SwiftImportAs; |
| std::optional<std::string> SwiftRetainOp; |
| std::optional<std::string> SwiftReleaseOp; |
| std::optional<std::string> SwiftConformance; |
| std::optional<EnumExtensibilityKind> EnumExtensibility; |
| std::optional<bool> FlagEnum; |
| std::optional<EnumConvenienceAliasKind> EnumConvenienceKind; |
| std::optional<bool> SwiftCopyable; |
| FunctionsSeq Methods; |
| FieldsSeq Fields; |
| |
| /// Tags that are declared within the current tag. Only the tags that have |
| /// corresponding API Notes will be listed. |
| TagsSeq Tags; |
| }; |
| } // namespace |
| |
| LLVM_YAML_IS_SEQUENCE_VECTOR(Tag) |
| |
| namespace llvm { |
| namespace yaml { |
| template <> struct ScalarEnumerationTraits<EnumExtensibilityKind> { |
| static void enumeration(IO &IO, EnumExtensibilityKind &EEK) { |
| IO.enumCase(EEK, "none", EnumExtensibilityKind::None); |
| IO.enumCase(EEK, "open", EnumExtensibilityKind::Open); |
| IO.enumCase(EEK, "closed", EnumExtensibilityKind::Closed); |
| } |
| }; |
| |
| template <> struct MappingTraits<Tag> { |
| static void mapping(IO &IO, Tag &T) { |
| IO.mapRequired("Name", T.Name); |
| IO.mapOptional("Availability", T.Availability.Mode, |
| APIAvailability::Available); |
| IO.mapOptional("AvailabilityMsg", T.Availability.Msg, StringRef("")); |
| IO.mapOptional("SwiftPrivate", T.SwiftPrivate); |
| IO.mapOptional("SwiftName", T.SwiftName, StringRef("")); |
| IO.mapOptional("SwiftBridge", T.SwiftBridge); |
| IO.mapOptional("NSErrorDomain", T.NSErrorDomain); |
| IO.mapOptional("SwiftImportAs", T.SwiftImportAs); |
| IO.mapOptional("SwiftReleaseOp", T.SwiftReleaseOp); |
| IO.mapOptional("SwiftRetainOp", T.SwiftRetainOp); |
| IO.mapOptional("SwiftConformsTo", T.SwiftConformance); |
| IO.mapOptional("EnumExtensibility", T.EnumExtensibility); |
| IO.mapOptional("FlagEnum", T.FlagEnum); |
| IO.mapOptional("EnumKind", T.EnumConvenienceKind); |
| IO.mapOptional("SwiftCopyable", T.SwiftCopyable); |
| IO.mapOptional("Methods", T.Methods); |
| IO.mapOptional("Fields", T.Fields); |
| IO.mapOptional("Tags", T.Tags); |
| } |
| }; |
| } // namespace yaml |
| } // namespace llvm |
| |
| namespace { |
| struct Typedef { |
| StringRef Name; |
| AvailabilityItem Availability; |
| StringRef SwiftName; |
| std::optional<bool> SwiftPrivate; |
| std::optional<StringRef> SwiftBridge; |
| std::optional<StringRef> NSErrorDomain; |
| std::optional<SwiftNewTypeKind> SwiftType; |
| }; |
| |
| typedef std::vector<Typedef> TypedefsSeq; |
| } // namespace |
| |
| LLVM_YAML_IS_SEQUENCE_VECTOR(Typedef) |
| |
| namespace llvm { |
| namespace yaml { |
| template <> struct ScalarEnumerationTraits<SwiftNewTypeKind> { |
| static void enumeration(IO &IO, SwiftNewTypeKind &SWK) { |
| IO.enumCase(SWK, "none", SwiftNewTypeKind::None); |
| IO.enumCase(SWK, "struct", SwiftNewTypeKind::Struct); |
| IO.enumCase(SWK, "enum", SwiftNewTypeKind::Enum); |
| } |
| }; |
| |
| template <> struct MappingTraits<Typedef> { |
| static void mapping(IO &IO, Typedef &T) { |
| IO.mapRequired("Name", T.Name); |
| IO.mapOptional("Availability", T.Availability.Mode, |
| APIAvailability::Available); |
| IO.mapOptional("AvailabilityMsg", T.Availability.Msg, StringRef("")); |
| IO.mapOptional("SwiftPrivate", T.SwiftPrivate); |
| IO.mapOptional("SwiftName", T.SwiftName, StringRef("")); |
| IO.mapOptional("SwiftBridge", T.SwiftBridge); |
| IO.mapOptional("NSErrorDomain", T.NSErrorDomain); |
| IO.mapOptional("SwiftWrapper", T.SwiftType); |
| } |
| }; |
| } // namespace yaml |
| } // namespace llvm |
| |
| namespace { |
| struct Namespace; |
| typedef std::vector<Namespace> NamespacesSeq; |
| |
| struct TopLevelItems { |
| ClassesSeq Classes; |
| ClassesSeq Protocols; |
| FunctionsSeq Functions; |
| GlobalVariablesSeq Globals; |
| EnumConstantsSeq EnumConstants; |
| TagsSeq Tags; |
| TypedefsSeq Typedefs; |
| NamespacesSeq Namespaces; |
| }; |
| } // namespace |
| |
| namespace llvm { |
| namespace yaml { |
| static void mapTopLevelItems(IO &IO, TopLevelItems &TLI) { |
| IO.mapOptional("Classes", TLI.Classes); |
| IO.mapOptional("Protocols", TLI.Protocols); |
| IO.mapOptional("Functions", TLI.Functions); |
| IO.mapOptional("Globals", TLI.Globals); |
| IO.mapOptional("Enumerators", TLI.EnumConstants); |
| IO.mapOptional("Tags", TLI.Tags); |
| IO.mapOptional("Typedefs", TLI.Typedefs); |
| IO.mapOptional("Namespaces", TLI.Namespaces); |
| } |
| } // namespace yaml |
| } // namespace llvm |
| |
| namespace { |
| struct Namespace { |
| StringRef Name; |
| AvailabilityItem Availability; |
| StringRef SwiftName; |
| std::optional<bool> SwiftPrivate; |
| TopLevelItems Items; |
| }; |
| } // namespace |
| |
| LLVM_YAML_IS_SEQUENCE_VECTOR(Namespace) |
| |
| namespace llvm { |
| namespace yaml { |
| template <> struct MappingTraits<Namespace> { |
| static void mapping(IO &IO, Namespace &T) { |
| IO.mapRequired("Name", T.Name); |
| IO.mapOptional("Availability", T.Availability.Mode, |
| APIAvailability::Available); |
| IO.mapOptional("AvailabilityMsg", T.Availability.Msg, StringRef("")); |
| IO.mapOptional("SwiftPrivate", T.SwiftPrivate); |
| IO.mapOptional("SwiftName", T.SwiftName, StringRef("")); |
| mapTopLevelItems(IO, T.Items); |
| } |
| }; |
| } // namespace yaml |
| } // namespace llvm |
| |
| namespace { |
| struct Versioned { |
| VersionTuple Version; |
| TopLevelItems Items; |
| }; |
| |
| typedef std::vector<Versioned> VersionedSeq; |
| } // namespace |
| |
| LLVM_YAML_IS_SEQUENCE_VECTOR(Versioned) |
| |
| namespace llvm { |
| namespace yaml { |
| template <> struct MappingTraits<Versioned> { |
| static void mapping(IO &IO, Versioned &V) { |
| IO.mapRequired("Version", V.Version); |
| mapTopLevelItems(IO, V.Items); |
| } |
| }; |
| } // namespace yaml |
| } // namespace llvm |
| |
| namespace { |
| struct Module { |
| StringRef Name; |
| AvailabilityItem Availability; |
| TopLevelItems TopLevel; |
| VersionedSeq SwiftVersions; |
| |
| std::optional<bool> SwiftInferImportAsMember; |
| |
| #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) |
| LLVM_DUMP_METHOD void dump() /*const*/; |
| #endif |
| }; |
| } // namespace |
| |
| namespace llvm { |
| namespace yaml { |
| template <> struct MappingTraits<Module> { |
| static void mapping(IO &IO, Module &M) { |
| IO.mapRequired("Name", M.Name); |
| IO.mapOptional("Availability", M.Availability.Mode, |
| APIAvailability::Available); |
| IO.mapOptional("AvailabilityMsg", M.Availability.Msg, StringRef("")); |
| IO.mapOptional("SwiftInferImportAsMember", M.SwiftInferImportAsMember); |
| mapTopLevelItems(IO, M.TopLevel); |
| IO.mapOptional("SwiftVersions", M.SwiftVersions); |
| } |
| }; |
| } // namespace yaml |
| } // namespace llvm |
| |
| #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) |
| LLVM_DUMP_METHOD void Module::dump() { |
| llvm::yaml::Output OS(llvm::errs()); |
| OS << *this; |
| } |
| #endif |
| |
| namespace { |
| bool parseAPINotes(StringRef YI, Module &M, llvm::SourceMgr::DiagHandlerTy Diag, |
| void *DiagContext) { |
| llvm::yaml::Input IS(YI, nullptr, Diag, DiagContext); |
| IS >> M; |
| return static_cast<bool>(IS.error()); |
| } |
| } // namespace |
| |
| bool clang::api_notes::parseAndDumpAPINotes(StringRef YI, |
| llvm::raw_ostream &OS) { |
| Module M; |
| if (parseAPINotes(YI, M, nullptr, nullptr)) |
| return true; |
| |
| llvm::yaml::Output YOS(OS); |
| YOS << M; |
| |
| 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.ends_with(":"); |
| |
| // 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); |
| } |
| |
| template <typename T> |
| void convertVariable(const T &Entity, VariableInfo &VI) { |
| convertAvailability(Entity.Availability, VI, Entity.Name); |
| VI.setSwiftPrivate(Entity.SwiftPrivate); |
| VI.SwiftName = std::string(Entity.SwiftName); |
| if (Entity.Nullability) |
| VI.setNullabilityAudited(*Entity.Nullability); |
| VI.setType(std::string(Entity.Type)); |
| } |
| |
| void convertContext(std::optional<ContextID> ParentContextID, const Class &C, |
| ContextKind Kind, VersionTuple SwiftVersion) { |
| // Write the class. |
| ContextInfo 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.addContext(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; |
| convertVariable(Property, PI); |
| if (Property.SwiftImportAsAccessors) |
| PI.setSwiftImportAsAccessors(*Property.SwiftImportAsAccessors); |
| |
| // Add both instance and class properties with this name. |
| if (Property.Kind) { |
| Writer.addObjCProperty(CtxID, Property.Name, |
| *Property.Kind == MethodKind::Instance, PI, |
| SwiftVersion); |
| } else { |
| 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. |
| ContextInfo CI; |
| convertCommonEntity(TheNamespace, CI, TheNamespace.Name); |
| |
| ContextID CtxID = |
| Writer.addContext(ParentContextID, TheNamespace.Name, |
| ContextKind::Namespace, CI, SwiftVersion); |
| |
| convertTopLevelItems(Context(CtxID, ContextKind::Namespace), |
| TheNamespace.Items, SwiftVersion); |
| } |
| |
| void convertFunction(const Function &Function, FunctionInfo &FI) { |
| convertAvailability(Function.Availability, FI, Function.Name); |
| FI.setSwiftPrivate(Function.SwiftPrivate); |
| FI.SwiftName = std::string(Function.SwiftName); |
| convertParams(Function.Params, FI); |
| convertNullability(Function.Nullability, Function.NullabilityOfRet, FI, |
| Function.Name); |
| FI.ResultType = std::string(Function.ResultType); |
| FI.setRetainCountConvention(Function.RetainCountConvention); |
| } |
| |
| void convertTagContext(std::optional<Context> ParentContext, const Tag &T, |
| VersionTuple SwiftVersion) { |
| TagInfo TI; |
| std::optional<ContextID> ParentContextID = |
| ParentContext ? std::optional<ContextID>(ParentContext->id) |
| : std::nullopt; |
| convertCommonType(T, TI, T.Name); |
| |
| if ((T.SwiftRetainOp || T.SwiftReleaseOp) && !T.SwiftImportAs) { |
| emitError(llvm::Twine("should declare SwiftImportAs to use " |
| "SwiftRetainOp and SwiftReleaseOp (for ") + |
| T.Name + ")"); |
| return; |
| } |
| if (T.SwiftReleaseOp.has_value() != T.SwiftRetainOp.has_value()) { |
| emitError(llvm::Twine("should declare both SwiftReleaseOp and " |
| "SwiftRetainOp (for ") + |
| T.Name + ")"); |
| return; |
| } |
| |
| if (T.SwiftImportAs) |
| TI.SwiftImportAs = T.SwiftImportAs; |
| if (T.SwiftRetainOp) |
| TI.SwiftRetainOp = T.SwiftRetainOp; |
| if (T.SwiftReleaseOp) |
| TI.SwiftReleaseOp = T.SwiftReleaseOp; |
| if (T.SwiftConformance) |
| TI.SwiftConformance = T.SwiftConformance; |
| |
| if (T.SwiftCopyable) |
| TI.setSwiftCopyable(T.SwiftCopyable); |
| |
| if (T.EnumConvenienceKind) { |
| if (T.EnumExtensibility) { |
| emitError( |
| llvm::Twine("cannot mix EnumKind and EnumExtensibility (for ") + |
| T.Name + ")"); |
| return; |
| } |
| if (T.FlagEnum) { |
| emitError(llvm::Twine("cannot mix EnumKind and FlagEnum (for ") + |
| T.Name + ")"); |
| return; |
| } |
| switch (*T.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 = T.EnumExtensibility; |
| TI.setFlagEnum(T.FlagEnum); |
| } |
| |
| Writer.addTag(ParentContext, T.Name, TI, SwiftVersion); |
| |
| ContextInfo CI; |
| auto TagCtxID = Writer.addContext(ParentContextID, T.Name, ContextKind::Tag, |
| CI, SwiftVersion); |
| Context TagCtx(TagCtxID, ContextKind::Tag); |
| |
| for (const auto &Field : T.Fields) { |
| FieldInfo FI; |
| convertVariable(Field, FI); |
| Writer.addField(TagCtxID, Field.Name, FI, SwiftVersion); |
| } |
| |
| for (const auto &CXXMethod : T.Methods) { |
| CXXMethodInfo MI; |
| convertFunction(CXXMethod, MI); |
| Writer.addCXXMethod(TagCtxID, CXXMethod.Name, MI, SwiftVersion); |
| } |
| |
| // Convert nested tags. |
| for (const auto &Tag : T.Tags) |
| convertTagContext(TagCtx, Tag, 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; |
| convertVariable(Global, GVI); |
| 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; |
| convertFunction(Function, GFI); |
| 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; |
| } |
| |
| convertTagContext(Ctx, Tag, 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); |
| |
| if (!ErrorOccured) |
| Writer.writeToStream(OS); |
| |
| return ErrorOccured; |
| } |
| }; |
| } // namespace |
| |
| static bool compile(const Module &M, const FileEntry *SourceFile, |
| llvm::raw_ostream &OS, |
| llvm::SourceMgr::DiagHandlerTy DiagHandler, |
| void *DiagHandlerCtxt) { |
| YAMLConverter C(M, SourceFile, OS, DiagHandler, DiagHandlerCtxt); |
| return C.convertModule(); |
| } |
| |
| /// Simple diagnostic handler that prints diagnostics to standard error. |
| static void printDiagnostic(const llvm::SMDiagnostic &Diag, void *Context) { |
| Diag.print(nullptr, llvm::errs()); |
| } |
| |
| bool api_notes::compileAPINotes(StringRef YAMLInput, |
| const FileEntry *SourceFile, |
| llvm::raw_ostream &OS, |
| llvm::SourceMgr::DiagHandlerTy DiagHandler, |
| void *DiagHandlerCtxt) { |
| Module TheModule; |
| |
| if (!DiagHandler) |
| DiagHandler = &printDiagnostic; |
| |
| if (parseAPINotes(YAMLInput, TheModule, DiagHandler, DiagHandlerCtxt)) |
| return true; |
| |
| return compile(TheModule, SourceFile, OS, DiagHandler, DiagHandlerCtxt); |
| } |