forked from OSchip/llvm-project
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
This commit is contained in:
parent
35d6251254
commit
82f86ae01a
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
set(LLVM_LINK_COMPONENTS
|
||||
Support)
|
||||
add_clang_library(clangAPINotes
|
||||
APINotesYAMLCompiler.cpp
|
||||
LINK_LIBS
|
||||
clangBasic)
|
|
@ -1,5 +1,6 @@
|
|||
add_subdirectory(Headers)
|
||||
add_subdirectory(Basic)
|
||||
add_subdirectory(APINotes)
|
||||
add_subdirectory(Lex)
|
||||
add_subdirectory(Parse)
|
||||
add_subdirectory(AST)
|
||||
|
|
|
@ -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
|
|
@ -0,0 +1,19 @@
|
|||
#ifndef SIMPLE_H
|
||||
#define SIMPLE_H
|
||||
|
||||
__attribute__((__objc_root__))
|
||||
@interface I
|
||||
@property(class, nonatomic, readonly) id nonnullProperty;
|
||||
@property(class, nonatomic, readonly) id nonnullNewProperty;
|
||||
|
||||
@property(class, nonatomic, readonly) id optionalProperty;
|
||||
@property(class, nonatomic, readonly) id optionalNewProperty;
|
||||
|
||||
@property(nonatomic, readonly) id unspecifiedProperty;
|
||||
@property(nonatomic, readonly) id unspecifiedNewProperty;
|
||||
|
||||
@property(nonatomic, readonly) id scalarProperty;
|
||||
@property(nonatomic, readonly) id scalarNewProperty;
|
||||
@end
|
||||
|
||||
#endif
|
|
@ -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
|
|
@ -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
|
||||
|
||||
@interface MethodTest
|
||||
- (id)getOwnedToUnowned __attribute__((__ns_returns_retained__));
|
||||
- (id)getUnownedToOwned __attribute__((__ns_returns_not_retained__));
|
||||
@end
|
|
@ -0,0 +1,5 @@
|
|||
framework module SimpleKit {
|
||||
umbrella header "SimpleKit.h"
|
||||
export *
|
||||
module * { export * }
|
||||
}
|
|
@ -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
|
|
@ -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 different 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
|
|
@ -58,6 +58,7 @@ if(CLANG_TEST_USE_VG)
|
|||
endif ()
|
||||
|
||||
list(APPEND CLANG_TEST_DEPS
|
||||
apinotes-test
|
||||
c-index-test
|
||||
clang
|
||||
clang-resource-headers
|
||||
|
|
|
@ -63,7 +63,8 @@ config.substitutions.append(('%PATH%', config.environment['PATH']))
|
|||
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'),
|
||||
]
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
set(LLVM_LINK_COMPONENTS
|
||||
Support)
|
||||
add_clang_executable(apinotes-test
|
||||
APINotesTest.cpp)
|
||||
clang_target_link_libraries(apinotes-test PRIVATE
|
||||
clangAPINotes)
|
Loading…
Reference in New Issue