Files
clang-p2996/clang/lib/APINotes/APINotesYAMLCompiler.cpp
fahadnayyar 62a2f0fdc7 [APINotes] Add support for SWIFT_RETURED_AS_UNRETAINED_BY_DEFAULT (#138699)
This patch adds support in APINotes for annotating C++ user-defined
types with: `swift_attr("returned_as_unretained_by_default")`
This attribute allows to specify a default ownership convention for
return values of `SWIFT_SHARED_REFERENCE` c++ types. Specifically, it
marks all unannotated return values of this type as `unretained` (`+0`)
by default, unless explicitly overridden at the API level using
`swift_attr("returns_retained")` or `swift_attr("returns_unretained")`.

The corresponding Swift compiler support for this annotation enables
developers to suppress warnings about unannotated return ownership in
large codebases while maintaining safe and predictable ownership
semantics. By enabling this in APINotes, library authors can define this
behavior externally without needing to modify C++ source headers
directly.

### Example usage in APINotes:
```
- Name: RefCountedTypeWithDefaultConvention
  SwiftImportAs: reference
  SwiftDefaultOwnership: unretained

```
rdar://150764491
2025-05-07 13:42:39 -07:00

1231 lines
40 KiB
C++

//===-- 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 <type_traits>
#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 {
int Position;
std::optional<bool> NoEscape = false;
std::optional<bool> Lifetimebound = 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("Lifetimebound", P.Lifetimebound);
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;
StringRef SwiftReturnOwnership;
};
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(""));
IO.mapOptional("SwiftReturnOwnership", M.SwiftReturnOwnership,
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;
StringRef SwiftReturnOwnership;
};
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(""));
IO.mapOptional("SwiftReturnOwnership", F.SwiftReturnOwnership,
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> SwiftDefaultOwnership;
std::optional<std::string> SwiftConformance;
std::optional<EnumExtensibilityKind> EnumExtensibility;
std::optional<bool> FlagEnum;
std::optional<EnumConvenienceAliasKind> EnumConvenienceKind;
std::optional<bool> SwiftCopyable;
std::optional<bool> SwiftEscapable;
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("SwiftDefaultOwnership", T.SwiftDefaultOwnership);
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("SwiftEscapable", T.SwiftEscapable);
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,
std::optional<ParamInfo> &thisOrSelf) {
for (const auto &P : Params) {
ParamInfo PI;
if (P.Nullability)
PI.setNullabilityAudited(*P.Nullability);
PI.setNoEscape(P.NoEscape);
PI.setLifetimebound(P.Lifetimebound);
PI.setType(std::string(P.Type));
PI.setRetainCountConvention(P.RetainCountConvention);
if (static_cast<int>(OutInfo.Params.size()) <= P.Position)
OutInfo.Params.resize(P.Position + 1);
if (P.Position == -1)
thisOrSelf = PI;
else if (P.Position >= 0)
OutInfo.Params[P.Position] |= PI;
else
emitError("invalid parameter position " + llvm::itostr(P.Position));
}
}
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.value_or(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);
MI.SwiftReturnOwnership = std::string(M.SwiftReturnOwnership);
// Translate parameter information.
convertParams(M.Params, MI, MI.Self);
// 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);
}
template <typename FuncOrMethodInfo>
void convertFunction(const Function &Function, FuncOrMethodInfo &FI) {
convertAvailability(Function.Availability, FI, Function.Name);
FI.setSwiftPrivate(Function.SwiftPrivate);
FI.SwiftName = std::string(Function.SwiftName);
std::optional<ParamInfo> This;
convertParams(Function.Params, FI, This);
if constexpr (std::is_same_v<FuncOrMethodInfo, CXXMethodInfo>)
FI.This = This;
else if (This)
emitError("implicit instance parameter is only permitted on C++ and "
"Objective-C methods");
convertNullability(Function.Nullability, Function.NullabilityOfRet, FI,
Function.Name);
FI.ResultType = std::string(Function.ResultType);
FI.SwiftReturnOwnership = std::string(Function.SwiftReturnOwnership);
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.SwiftDefaultOwnership)
TI.SwiftDefaultOwnership = T.SwiftDefaultOwnership;
if (T.SwiftCopyable)
TI.setSwiftCopyable(T.SwiftCopyable);
if (T.SwiftEscapable)
TI.setSwiftEscapable(T.SwiftEscapable);
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);
}