[clang] 82f86ae - APINotes: add APINotesYAMLCompiler
Saleem Abdulrasool via cfe-commits
cfe-commits at lists.llvm.org
Thu Nov 5 10:55:35 PST 2020
Author: Saleem Abdulrasool
Date: 2020-11-05T18:55:13Z
New Revision: 82f86ae01a54ff8e3a5aaefd24745ef2b7b917ba
URL: https://github.com/llvm/llvm-project/commit/82f86ae01a54ff8e3a5aaefd24745ef2b7b917ba
DIFF: https://github.com/llvm/llvm-project/commit/82f86ae01a54ff8e3a5aaefd24745ef2b7b917ba.diff
LOG: APINotes: add APINotesYAMLCompiler
This adds the skeleton of the YAML Compiler for APINotes. This change
only adds the YAML IO model for the API Notes along with a new testing
tool `apinotes-test` which can be used to verify that can round trip the
YAML content properly. It provides the basis for the future work which
will add a binary serialization and deserialization format to the data
model.
This is based on the code contributed by Apple at
https://github.com/llvm/llvm-project-staging/tree/staging/swift/apinotes.
Differential Revision: https://reviews.llvm.org/D88859
Reviewed By: Gabor Marton
Added:
clang/include/clang/APINotes/APINotesYAMLCompiler.h
clang/include/clang/APINotes/Types.h
clang/lib/APINotes/APINotesYAMLCompiler.cpp
clang/lib/APINotes/CMakeLists.txt
clang/test/APINotes/Inputs/Frameworks/Simple.framework/Headers/Simple.apinotes
clang/test/APINotes/Inputs/Frameworks/Simple.framework/Headers/Simple.h
clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.apinotes
clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.h
clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/module.modulemap
clang/test/APINotes/yaml-roundtrip-2.test
clang/test/APINotes/yaml-roundtrip.test
clang/tools/apinotes-test/APINotesTest.cpp
clang/tools/apinotes-test/CMakeLists.txt
Modified:
clang/lib/CMakeLists.txt
clang/test/CMakeLists.txt
clang/test/lit.cfg.py
clang/tools/CMakeLists.txt
Removed:
################################################################################
diff --git a/clang/include/clang/APINotes/APINotesYAMLCompiler.h b/clang/include/clang/APINotes/APINotesYAMLCompiler.h
new file mode 100644
index 000000000000..6098d0ee36fc
--- /dev/null
+++ b/clang/include/clang/APINotes/APINotesYAMLCompiler.h
@@ -0,0 +1,24 @@
+//===-- APINotesYAMLCompiler.h - 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_APINOTES_APINOTESYAMLCOMPILER_H
+#define LLVM_CLANG_APINOTES_APINOTESYAMLCOMPILER_H
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/raw_ostream.h"
+
+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);
+} // namespace api_notes
+} // namespace clang
+
+#endif
diff --git a/clang/include/clang/APINotes/Types.h b/clang/include/clang/APINotes/Types.h
new file mode 100644
index 000000000000..be2a99ad6fd0
--- /dev/null
+++ b/clang/include/clang/APINotes/Types.h
@@ -0,0 +1,40 @@
+//===-- Types.h - API Notes Data Types --------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_APINOTES_TYPES_H
+#define LLVM_CLANG_APINOTES_TYPES_H
+
+namespace clang {
+namespace api_notes {
+enum class RetainCountConventionKind {
+ None,
+ CFReturnsRetained,
+ CFReturnsNotRetained,
+ NSReturnsRetained,
+ NSReturnsNotRetained,
+};
+
+/// The payload for an enum_extensibility attribute. This is a tri-state rather
+/// than just a boolean because the presence of the attribute indicates
+/// auditing.
+enum class EnumExtensibilityKind {
+ None,
+ Open,
+ Closed,
+};
+
+/// The kind of a swift_wrapper/swift_newtype.
+enum class SwiftNewTypeKind {
+ None,
+ Struct,
+ Enum,
+};
+} // namespace api_notes
+} // namespace clang
+
+#endif
diff --git a/clang/lib/APINotes/APINotesYAMLCompiler.cpp b/clang/lib/APINotes/APINotesYAMLCompiler.cpp
new file mode 100644
index 000000000000..997929a9bd22
--- /dev/null
+++ b/clang/lib/APINotes/APINotesYAMLCompiler.cpp
@@ -0,0 +1,597 @@
+//===-- 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/Types.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/Specifiers.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/Support/VersionTuple.h"
+#include "llvm/Support/YAMLParser.h"
+#include "llvm/Support/YAMLTraits.h"
+#include <vector>
+using namespace clang;
+using namespace api_notes;
+
+namespace {
+enum class APIAvailability {
+ Available = 0,
+ OSX,
+ IOS,
+ None,
+ NonSwift,
+};
+} // namespace
+
+namespace llvm {
+namespace yaml {
+template <> struct ScalarEnumerationTraits<APIAvailability> {
+ static void enumeration(IO &IO, APIAvailability &AA) {
+ IO.enumCase(AA, "OSX", APIAvailability::OSX);
+ IO.enumCase(AA, "iOS", APIAvailability::IOS);
+ 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;
+ Optional<bool> NoEscape = false;
+ Optional<NullabilityKind> Nullability;
+ 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);
+ // 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, llvm::None);
+ 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;
+ Optional<NullabilityKind> NullabilityOfRet;
+ Optional<RetainCountConventionKind> RetainCountConvention;
+ AvailabilityItem Availability;
+ 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, llvm::None);
+ 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;
+ llvm::Optional<MethodKind> Kind;
+ llvm::Optional<NullabilityKind> Nullability;
+ AvailabilityItem Availability;
+ Optional<bool> SwiftPrivate;
+ StringRef SwiftName;
+ 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, llvm::None);
+ 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;
+ Optional<bool> SwiftPrivate;
+ StringRef SwiftName;
+ Optional<StringRef> SwiftBridge;
+ Optional<StringRef> NSErrorDomain;
+ Optional<bool> SwiftImportAsNonGeneric;
+ 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;
+ Optional<NullabilityKind> NullabilityOfRet;
+ Optional<api_notes::RetainCountConventionKind> RetainCountConvention;
+ AvailabilityItem Availability;
+ 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, llvm::None);
+ 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;
+ llvm::Optional<NullabilityKind> Nullability;
+ AvailabilityItem Availability;
+ 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, llvm::None);
+ 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;
+ 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 Tag {
+ StringRef Name;
+ AvailabilityItem Availability;
+ StringRef SwiftName;
+ Optional<bool> SwiftPrivate;
+ Optional<StringRef> SwiftBridge;
+ Optional<StringRef> NSErrorDomain;
+ Optional<EnumExtensibilityKind> EnumExtensibility;
+ Optional<bool> FlagEnum;
+ Optional<EnumConvenienceAliasKind> EnumConvenienceKind;
+};
+
+typedef std::vector<Tag> TagsSeq;
+} // 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("EnumExtensibility", T.EnumExtensibility);
+ IO.mapOptional("FlagEnum", T.FlagEnum);
+ IO.mapOptional("EnumKind", T.EnumConvenienceKind);
+ }
+};
+} // namespace yaml
+} // namespace llvm
+
+namespace {
+struct Typedef {
+ StringRef Name;
+ AvailabilityItem Availability;
+ StringRef SwiftName;
+ Optional<bool> SwiftPrivate;
+ Optional<StringRef> SwiftBridge;
+ Optional<StringRef> NSErrorDomain;
+ 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 TopLevelItems {
+ ClassesSeq Classes;
+ ClassesSeq Protocols;
+ FunctionsSeq Functions;
+ GlobalVariablesSeq Globals;
+ EnumConstantsSeq EnumConstants;
+ TagsSeq Tags;
+ TypedefsSeq Typedefs;
+};
+} // 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);
+}
+} // 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;
+
+ llvm::Optional<bool> SwiftInferImportAsMember = {llvm::None};
+
+ LLVM_DUMP_METHOD void dump() /*const*/;
+};
+} // 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
+
+void Module::dump() {
+ llvm::yaml::Output OS(llvm::errs());
+ OS << *this;
+}
+
+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;
+}
diff --git a/clang/lib/APINotes/CMakeLists.txt b/clang/lib/APINotes/CMakeLists.txt
new file mode 100644
index 000000000000..3ce511a0de22
--- /dev/null
+++ b/clang/lib/APINotes/CMakeLists.txt
@@ -0,0 +1,6 @@
+set(LLVM_LINK_COMPONENTS
+ Support)
+add_clang_library(clangAPINotes
+ APINotesYAMLCompiler.cpp
+ LINK_LIBS
+ clangBasic)
diff --git a/clang/lib/CMakeLists.txt b/clang/lib/CMakeLists.txt
index 1068288100fd..be09c0c351f3 100644
--- a/clang/lib/CMakeLists.txt
+++ b/clang/lib/CMakeLists.txt
@@ -1,5 +1,6 @@
add_subdirectory(Headers)
add_subdirectory(Basic)
+add_subdirectory(APINotes)
add_subdirectory(Lex)
add_subdirectory(Parse)
add_subdirectory(AST)
diff --git a/clang/test/APINotes/Inputs/Frameworks/Simple.framework/Headers/Simple.apinotes b/clang/test/APINotes/Inputs/Frameworks/Simple.framework/Headers/Simple.apinotes
new file mode 100644
index 000000000000..8c915bd8b591
--- /dev/null
+++ b/clang/test/APINotes/Inputs/Frameworks/Simple.framework/Headers/Simple.apinotes
@@ -0,0 +1,28 @@
+Name: SimpleKit
+Classes:
+ - Name: I
+ Properties:
+ - Name: nonnullProperty
+ PropertyKind: Class
+ Nullability: N
+ - Name: nonnullNewProperty
+ PropertyKind: Class
+ Nullability: Nonnull
+ - Name: optionalProperty
+ PropertyKind: Class
+ Nullability: O
+ - Name: optionalNewProperty
+ PropertyKind: Class
+ Nullability: Optional
+ - Name: unspecifiedProperty
+ PropertyKind: Instance
+ Nullability: U
+ - Name: unspecifiedNewProperty
+ PropertyKind: Instance
+ Nullability: Unspecified
+ - Name: scalarProperty
+ PropertyKind: Instance
+ Nullability: S
+ - Name: scalarNewProperty
+ PropertyKind: Instance
+ Nullability: Scalar
diff --git a/clang/test/APINotes/Inputs/Frameworks/Simple.framework/Headers/Simple.h b/clang/test/APINotes/Inputs/Frameworks/Simple.framework/Headers/Simple.h
new file mode 100644
index 000000000000..d206a7a36887
--- /dev/null
+++ b/clang/test/APINotes/Inputs/Frameworks/Simple.framework/Headers/Simple.h
@@ -0,0 +1,19 @@
+#ifndef SIMPLE_H
+#define SIMPLE_H
+
+__attribute__((__objc_root__))
+ at interface I
+ at property(class, nonatomic, readonly) id nonnullProperty;
+ at property(class, nonatomic, readonly) id nonnullNewProperty;
+
+ at property(class, nonatomic, readonly) id optionalProperty;
+ at property(class, nonatomic, readonly) id optionalNewProperty;
+
+ at property(nonatomic, readonly) id unspecifiedProperty;
+ at property(nonatomic, readonly) id unspecifiedNewProperty;
+
+ at property(nonatomic, readonly) id scalarProperty;
+ at property(nonatomic, readonly) id scalarNewProperty;
+ at end
+
+#endif
diff --git a/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.apinotes b/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.apinotes
new file mode 100644
index 000000000000..ef6e44c51c21
--- /dev/null
+++ b/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.apinotes
@@ -0,0 +1,48 @@
+Name: SimpleKit
+Classes:
+ - Name: MethodTest
+ Methods:
+ - Selector: getOwnedToUnowned
+ MethodKind: Instance
+ RetainCountConvention: NSReturnsNotRetained
+ - Selector: getUnownedToOwned
+ MethodKind: Instance
+ RetainCountConvention: NSReturnsRetained
+Functions:
+ - Name: getCFOwnedToUnowned
+ RetainCountConvention: CFReturnsNotRetained
+ - Name: getCFUnownedToOwned
+ RetainCountConvention: CFReturnsRetained
+ - Name: getCFOwnedToNone
+ RetainCountConvention: none
+ - Name: getObjCOwnedToUnowned
+ RetainCountConvention: NSReturnsNotRetained
+ - Name: getObjCUnownedToOwned
+ RetainCountConvention: NSReturnsRetained
+ - Name: indirectGetCFOwnedToUnowned
+ Parameters:
+ - Position: 0
+ RetainCountConvention: CFReturnsNotRetained
+ - Name: indirectGetCFUnownedToOwned
+ Parameters:
+ - Position: 0
+ RetainCountConvention: CFReturnsRetained
+ - Name: indirectGetCFOwnedToNone
+ Parameters:
+ - Position: 0
+ RetainCountConvention: none
+ - Name: indirectGetCFNoneToOwned
+ Parameters:
+ - Position: 0
+ RetainCountConvention: CFReturnsNotRetained
+ - Name: getCFAuditedToUnowned_DUMP
+ RetainCountConvention: CFReturnsNotRetained
+ - Name: getCFAuditedToOwned_DUMP
+ RetainCountConvention: CFReturnsRetained
+ - Name: getCFAuditedToNone_DUMP
+ RetainCountConvention: none
+Tags:
+ - Name: RenamedAgainInAPINotesA
+ SwiftName: SuccessfullyRenamedA
+ - Name: RenamedAgainInAPINotesB
+ SwiftName: SuccessfullyRenamedB
diff --git a/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.h b/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.h
new file mode 100644
index 000000000000..bd73926e9d6a
--- /dev/null
+++ b/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.h
@@ -0,0 +1,29 @@
+struct RenamedAgainInAPINotesA {
+ int field;
+} __attribute__((__swift_name__("bad")));
+
+struct __attribute__((__swift_name__("bad"))) RenamedAgainInAPINotesB {
+ int field;
+};
+
+void *getCFOwnedToUnowned(void) __attribute__((__cf_returns_retained__));
+void *getCFUnownedToOwned(void) __attribute__((__cf_returns_not_retained__));
+void *getCFOwnedToNone(void) __attribute__((__cf_returns_retained__));
+id getObjCOwnedToUnowned(void) __attribute__((__ns_returns_retained__));
+id getObjCUnownedToOwned(void) __attribute__((__ns_returns_not_retained__));
+
+int indirectGetCFOwnedToUnowned(void **out __attribute__((__cf_returns_retained__)));
+int indirectGetCFUnownedToOwned(void **out __attribute__((__cf_returns_not_retained__)));
+int indirectGetCFOwnedToNone(void **out __attribute__((__cf_returns_retained__)));
+int indirectGetCFNoneToOwned(void **out);
+
+#pragma clang arc_cf_code_audited begin
+void *getCFAuditedToUnowned_DUMP(void);
+void *getCFAuditedToOwned_DUMP(void);
+void *getCFAuditedToNone_DUMP(void);
+#pragma clang arc_cf_code_audited end
+
+ at interface MethodTest
+- (id)getOwnedToUnowned __attribute__((__ns_returns_retained__));
+- (id)getUnownedToOwned __attribute__((__ns_returns_not_retained__));
+ at end
diff --git a/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/module.modulemap b/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/module.modulemap
new file mode 100644
index 000000000000..2d07e76c0a14
--- /dev/null
+++ b/clang/test/APINotes/Inputs/Frameworks/SimpleKit.framework/Headers/module.modulemap
@@ -0,0 +1,5 @@
+framework module SimpleKit {
+ umbrella header "SimpleKit.h"
+ export *
+ module * { export * }
+}
diff --git a/clang/test/APINotes/yaml-roundtrip-2.test b/clang/test/APINotes/yaml-roundtrip-2.test
new file mode 100644
index 000000000000..02455302fec1
--- /dev/null
+++ b/clang/test/APINotes/yaml-roundtrip-2.test
@@ -0,0 +1,11 @@
+RUN: apinotes-test %S/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.apinotes > %t.result
+RUN: not
diff --strip-trailing-cr --ed %t.result %S/Inputs/Frameworks/SimpleKit.framework/Headers/SimpleKit.apinotes | FileCheck %s
+
+The `--ed` parameter to `
diff ` is not implemented in the builtin
diff , assume
+that we have a GNU compatible
diff when we have a shell.
+REQUIRES: shell
+
+We expect only the document markers to be emitted
+
+CHECK: 50d
+CHECK: 1d
diff --git a/clang/test/APINotes/yaml-roundtrip.test b/clang/test/APINotes/yaml-roundtrip.test
new file mode 100644
index 000000000000..3379cbf3b6db
--- /dev/null
+++ b/clang/test/APINotes/yaml-roundtrip.test
@@ -0,0 +1,26 @@
+RUN: apinotes-test %S/Inputs/Frameworks/Simple.framework/Headers/Simple.apinotes > %t.result
+RUN: not
diff --strip-trailing-cr %S/Inputs/Frameworks/Simple.framework/Headers/Simple.apinotes %t.result | FileCheck %s
+
+We expect only the nullability to be
diff erent as it is canonicalized during the
+roudtrip.
+
+CHECK: 7c8
+CHECK-NEXT: < Nullability: N
+CHECK-NEXT: ---
+CHECK-NEXT: > Nullability: Nonnull
+CHECK-NEXT: 13c14
+CHECK-NEXT: < Nullability: O
+CHECK-NEXT: ---
+CHECK-NEXT: > Nullability: Optional
+CHECK-NEXT: 19c20
+CHECK-NEXT: < Nullability: U
+CHECK-NEXT: ---
+CHECK-NEXT: > Nullability: Unspecified
+CHECK-NEXT: 25c26
+CHECK-NEXT: < Nullability: S
+CHECK-NEXT: ---
+CHECK-NEXT: > Nullability: Unspecified
+CHECK-NEXT: 28c29,30
+CHECK-NEXT: < Nullability: Scalar
+CHECK-NEXT: ---
+CHECK-NEXT: > Nullability: Unspecified
diff --git a/clang/test/CMakeLists.txt b/clang/test/CMakeLists.txt
index 334a90498d0d..2207607f5c6a 100644
--- a/clang/test/CMakeLists.txt
+++ b/clang/test/CMakeLists.txt
@@ -58,6 +58,7 @@ if(CLANG_TEST_USE_VG)
endif ()
list(APPEND CLANG_TEST_DEPS
+ apinotes-test
c-index-test
clang
clang-resource-headers
diff --git a/clang/test/lit.cfg.py b/clang/test/lit.cfg.py
index 91161b8244f2..1a6e73ed9783 100644
--- a/clang/test/lit.cfg.py
+++ b/clang/test/lit.cfg.py
@@ -63,7 +63,8 @@
tool_dirs = [config.clang_tools_dir, config.llvm_tools_dir]
tools = [
- 'c-index-test', 'clang-
diff ', 'clang-format', 'clang-tblgen', 'opt', 'llvm-ifs',
+ 'apinotes-test', 'c-index-test', 'clang-
diff ', 'clang-format',
+ 'clang-tblgen', 'opt', 'llvm-ifs',
ToolSubst('%clang_extdef_map', command=FindTool(
'clang-extdef-mapping'), unresolved='ignore'),
]
diff --git a/clang/tools/CMakeLists.txt b/clang/tools/CMakeLists.txt
index 84e3fb156f1a..52fd02529b46 100644
--- a/clang/tools/CMakeLists.txt
+++ b/clang/tools/CMakeLists.txt
@@ -2,6 +2,7 @@ create_subdirectory_options(CLANG TOOL)
add_clang_subdirectory(diagtool)
add_clang_subdirectory(driver)
+add_clang_subdirectory(apinotes-test)
add_clang_subdirectory(clang-
diff )
add_clang_subdirectory(clang-format)
add_clang_subdirectory(clang-format-vs)
diff --git a/clang/tools/apinotes-test/APINotesTest.cpp b/clang/tools/apinotes-test/APINotesTest.cpp
new file mode 100644
index 000000000000..8794546dd284
--- /dev/null
+++ b/clang/tools/apinotes-test/APINotesTest.cpp
@@ -0,0 +1,53 @@
+//===-- APINotesTest.cpp - API Notes Testing Tool ------------------ 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/APINotesYAMLCompiler.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/WithColor.h"
+
+static llvm::cl::list<std::string> APINotes(llvm::cl::Positional,
+ llvm::cl::desc("[<apinotes> ...]"),
+ llvm::cl::Required);
+
+static llvm::cl::opt<std::string>
+ OutputFileName("o", llvm::cl::desc("output filename"),
+ llvm::cl::value_desc("filename"), llvm::cl::init("-"));
+
+int main(int argc, const char **argv) {
+ const bool DisableCrashReporting = true;
+ llvm::sys::PrintStackTraceOnErrorSignal(argv[0], DisableCrashReporting);
+ llvm::cl::ParseCommandLineOptions(argc, argv);
+
+ auto Error = [](const llvm::Twine &Msg) {
+ llvm::WithColor::error(llvm::errs(), "apinotes-test") << Msg << '\n';
+ };
+
+ std::error_code EC;
+ auto Out = std::make_unique<llvm::ToolOutputFile>(OutputFileName, EC,
+ llvm::sys::fs::OF_None);
+ if (EC) {
+ Error("failed to open '" + OutputFileName + "': " + EC.message());
+ return EXIT_FAILURE;
+ }
+
+ for (const std::string &Notes : APINotes) {
+ llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> NotesOrError =
+ llvm::MemoryBuffer::getFileOrSTDIN(Notes);
+ if (std::error_code EC = NotesOrError.getError()) {
+ llvm::errs() << EC.message() << '\n';
+ return EXIT_FAILURE;
+ }
+
+ clang::api_notes::parseAndDumpAPINotes((*NotesOrError)->getBuffer(),
+ Out->os());
+ }
+
+ return EXIT_SUCCESS;
+}
diff --git a/clang/tools/apinotes-test/CMakeLists.txt b/clang/tools/apinotes-test/CMakeLists.txt
new file mode 100644
index 000000000000..39e82d90b74f
--- /dev/null
+++ b/clang/tools/apinotes-test/CMakeLists.txt
@@ -0,0 +1,6 @@
+set(LLVM_LINK_COMPONENTS
+ Support)
+add_clang_executable(apinotes-test
+ APINotesTest.cpp)
+clang_target_link_libraries(apinotes-test PRIVATE
+ clangAPINotes)
More information about the cfe-commits
mailing list