blob: 6145fef3de26847a73d47b8ee4505a2aca67905f [file] [log] [blame]
//===--- SemaAPINotes.cpp - API Notes Handling ----------------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the mapping from API notes to declaration attributes.
//
//===----------------------------------------------------------------------===//
#include "clang/Sema/SemaInternal.h"
#include "clang/AST/DeclObjC.h"
#include "clang/APINotes/APINotesReader.h"
using namespace clang;
namespace {
enum IsActive_t : bool {
IsNotActive,
IsActive
};
struct VersionedInfoMetadata {
/// An empty version refers to unversioned metadata.
VersionTuple Version;
bool IsActive;
VersionedInfoMetadata(VersionTuple version, IsActive_t active)
: Version(version), IsActive(active == IsActive_t::IsActive) {}
};
} // end anonymous namespace
/// Determine whether this is a multi-level pointer type.
static bool isMultiLevelPointerType(QualType type) {
QualType pointee = type->getPointeeType();
if (pointee.isNull())
return false;
return pointee->isAnyPointerType() || pointee->isObjCObjectPointerType() ||
pointee->isMemberPointerType();
}
// Apply nullability to the given declaration.
static void applyNullability(Sema &S, Decl *decl, NullabilityKind nullability,
VersionedInfoMetadata metadata) {
if (!metadata.IsActive)
return;
QualType type;
// Nullability for a function/method appertains to the retain type.
if (auto function = dyn_cast<FunctionDecl>(decl)) {
type = function->getReturnType();
} else if (auto method = dyn_cast<ObjCMethodDecl>(decl)) {
type = method->getReturnType();
} else if (auto value = dyn_cast<ValueDecl>(decl)) {
type = value->getType();
} else if (auto property = dyn_cast<ObjCPropertyDecl>(decl)) {
type = property->getType();
} else {
return;
}
// Check the nullability specifier on this type.
QualType origType = type;
S.checkNullabilityTypeSpecifier(type, nullability, decl->getLocation(),
/*isContextSensitive=*/false,
isa<ParmVarDecl>(decl), /*implicit=*/true,
/*overrideExisting=*/true);
if (type.getTypePtr() == origType.getTypePtr())
return;
if (auto function = dyn_cast<FunctionDecl>(decl)) {
const FunctionType *fnType = function->getType()->castAs<FunctionType>();
if (const FunctionProtoType *proto = dyn_cast<FunctionProtoType>(fnType)) {
function->setType(S.Context.getFunctionType(type, proto->getParamTypes(),
proto->getExtProtoInfo()));
} else {
function->setType(S.Context.getFunctionNoProtoType(type,
fnType->getExtInfo()));
}
} else if (auto method = dyn_cast<ObjCMethodDecl>(decl)) {
method->setReturnType(type);
// Make it a context-sensitive keyword if we can.
if (!isMultiLevelPointerType(type)) {
method->setObjCDeclQualifier(
Decl::ObjCDeclQualifier(method->getObjCDeclQualifier() |
Decl::OBJC_TQ_CSNullability));
}
} else if (auto value = dyn_cast<ValueDecl>(decl)) {
value->setType(type);
// Make it a context-sensitive keyword if we can.
if (auto parm = dyn_cast<ParmVarDecl>(decl)) {
if (parm->isObjCMethodParameter() && !isMultiLevelPointerType(type)) {
parm->setObjCDeclQualifier(
Decl::ObjCDeclQualifier(parm->getObjCDeclQualifier() |
Decl::OBJC_TQ_CSNullability));
}
}
} else if (auto property = dyn_cast<ObjCPropertyDecl>(decl)) {
property->setType(type, property->getTypeSourceInfo());
// Make it a property attribute if we can.
if (!isMultiLevelPointerType(type)) {
property->setPropertyAttributes(
ObjCPropertyDecl::OBJC_PR_null_resettable);
}
} else {
llvm_unreachable("cannot handle nullability here");
}
}
/// Copy a string into ASTContext-allocated memory.
static StringRef CopyString(ASTContext &ctx, StringRef string) {
void *mem = ctx.Allocate(string.size(), alignof(char));
memcpy(mem, string.data(), string.size());
return StringRef(static_cast<char *>(mem), string.size());
}
namespace {
template <typename A>
struct AttrKindFor {};
#define ATTR(X) \
template <> struct AttrKindFor<X##Attr> { \
static const attr::Kind value = attr::X; \
};
#include "clang/Basic/AttrList.inc"
/// Handle an attribute introduced by API notes.
///
/// \param shouldAddAttribute Whether we should add a new attribute
/// (otherwise, we might remove an existing attribute).
/// \param createAttr Create the new attribute to be added.
template<typename A>
void handleAPINotedAttribute(
Sema &S, Decl *D, bool shouldAddAttribute,
VersionedInfoMetadata metadata,
llvm::function_ref<A *()> createAttr,
llvm::function_ref<specific_attr_iterator<A>(Decl*)> getExistingAttr) {
if (metadata.IsActive) {
auto end = D->specific_attr_end<A>();
auto existing = getExistingAttr(D);
if (existing != end) {
// Remove the existing attribute, and treat it as a superseded
// non-versioned attribute.
auto *versioned =
SwiftVersionedAttr::CreateImplicit(S.Context, clang::VersionTuple(),
*existing);
D->getAttrs().erase(existing.getCurrent());
D->addAttr(versioned);
}
// If we're supposed to add a new attribute, do so.
if (shouldAddAttribute) {
if (auto attr = createAttr()) {
D->addAttr(attr);
}
}
} else {
if (shouldAddAttribute) {
if (auto attr = createAttr()) {
auto *versioned =
SwiftVersionedAttr::CreateImplicit(S.Context, metadata.Version,
attr);
D->addAttr(versioned);
}
} else {
// FIXME: This isn't preserving enough information for things like
// availability, where we're trying to remove a /specific/ kind of
// attribute.
auto *versioned =
SwiftVersionedRemovalAttr::CreateImplicit(S.Context,
metadata.Version,
AttrKindFor<A>::value);
D->addAttr(versioned);
}
}
}
template<typename A>
void handleAPINotedAttribute(
Sema &S, Decl *D, bool shouldAddAttribute,
VersionedInfoMetadata metadata,
llvm::function_ref<A *()> createAttr) {
handleAPINotedAttribute<A>(S, D, shouldAddAttribute, metadata, createAttr,
[](Decl *decl) {
return decl->specific_attr_begin<A>();
});
}
}
static void ProcessAPINotes(Sema &S, Decl *D,
const api_notes::CommonEntityInfo &info,
VersionedInfoMetadata metadata) {
// Availability
if (info.Unavailable) {
handleAPINotedAttribute<UnavailableAttr>(S, D, true, metadata,
[&] {
return UnavailableAttr::CreateImplicit(S.Context,
CopyString(S.Context,
info.UnavailableMsg));
});
}
if (info.UnavailableInSwift) {
handleAPINotedAttribute<AvailabilityAttr>(S, D, true, metadata, [&] {
return AvailabilityAttr::CreateImplicit(
S.Context,
&S.Context.Idents.get("swift"),
VersionTuple(),
VersionTuple(),
VersionTuple(),
/*Unavailable=*/true,
CopyString(S.Context, info.UnavailableMsg),
/*Strict=*/false,
/*Replacement=*/StringRef());
},
[](Decl *decl) {
auto existing = decl->specific_attr_begin<AvailabilityAttr>(),
end = decl->specific_attr_end<AvailabilityAttr>();
while (existing != end) {
if (auto platform = (*existing)->getPlatform()) {
if (platform->isStr("swift"))
break;
}
++existing;
}
return existing;
});
}
// swift_private
if (auto swiftPrivate = info.isSwiftPrivate()) {
handleAPINotedAttribute<SwiftPrivateAttr>(S, D, *swiftPrivate, metadata,
[&] {
return SwiftPrivateAttr::CreateImplicit(S.Context);
});
}
// swift_name
if (!info.SwiftName.empty()) {
handleAPINotedAttribute<SwiftNameAttr>(S, D, true, metadata,
[&]() -> SwiftNameAttr * {
auto &APINoteName = S.getASTContext().Idents.get("SwiftName API Note");
if (!S.DiagnoseSwiftName(D, info.SwiftName, D->getLocation(),
&APINoteName)) {
return nullptr;
}
return SwiftNameAttr::CreateImplicit(S.Context,
CopyString(S.Context,
info.SwiftName));
});
}
}
static void ProcessAPINotes(Sema &S, Decl *D,
const api_notes::CommonTypeInfo &info,
VersionedInfoMetadata metadata) {
// swift_bridge
if (auto swiftBridge = info.getSwiftBridge()) {
handleAPINotedAttribute<SwiftBridgeAttr>(S, D, !swiftBridge->empty(),
metadata, [&] {
return SwiftBridgeAttr::CreateImplicit(S.Context,
CopyString(S.Context,
*swiftBridge));
});
}
// ns_error_domain
if (auto nsErrorDomain = info.getNSErrorDomain()) {
handleAPINotedAttribute<NSErrorDomainAttr>(S, D, !nsErrorDomain->empty(),
metadata, [&] {
return NSErrorDomainAttr::CreateImplicit(
S.Context,
&S.Context.Idents.get(*nsErrorDomain));
});
}
ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(info),
metadata);
}
/// Check that the replacement type provided by API notes is reasonable.
///
/// This is a very weak form of ABI check.
static bool checkAPINotesReplacementType(Sema &S, SourceLocation loc,
QualType origType,
QualType replacementType) {
if (S.Context.getTypeSize(origType) !=
S.Context.getTypeSize(replacementType)) {
S.Diag(loc, diag::err_incompatible_replacement_type)
<< replacementType << origType;
return true;
}
return false;
}
/// Process API notes for a variable or property.
static void ProcessAPINotes(Sema &S, Decl *D,
const api_notes::VariableInfo &info,
VersionedInfoMetadata metadata) {
// Type override.
if (metadata.IsActive && !info.getType().empty() &&
S.ParseTypeFromStringCallback) {
auto parsedType = S.ParseTypeFromStringCallback(info.getType(),
"<API Notes>",
D->getLocation());
if (parsedType.isUsable()) {
QualType type = Sema::GetTypeFromParser(parsedType.get());
auto typeInfo =
S.Context.getTrivialTypeSourceInfo(type, D->getLocation());
if (auto var = dyn_cast<VarDecl>(D)) {
// Make adjustments to parameter types.
if (isa<ParmVarDecl>(var)) {
type = S.adjustParameterTypeForObjCAutoRefCount(type,
D->getLocation());
type = S.Context.getAdjustedParameterType(type);
}
if (!checkAPINotesReplacementType(S, var->getLocation(), var->getType(),
type)) {
var->setType(type);
var->setTypeSourceInfo(typeInfo);
}
} else if (auto property = dyn_cast<ObjCPropertyDecl>(D)) {
if (!checkAPINotesReplacementType(S, property->getLocation(),
property->getType(),
type)) {
property->setType(type, typeInfo);
}
} else {
llvm_unreachable("API notes allowed a type on an unknown declaration");
}
}
}
// Nullability.
if (auto Nullability = info.getNullability()) {
applyNullability(S, D, *Nullability, metadata);
}
// Handle common entity information.
ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(info),
metadata);
}
/// Process API notes for a parameter.
static void ProcessAPINotes(Sema &S, ParmVarDecl *D,
const api_notes::ParamInfo &info,
VersionedInfoMetadata metadata) {
// noescape
if (auto noescape = info.isNoEscape()) {
handleAPINotedAttribute<NoEscapeAttr>(S, D, *noescape, metadata, [&] {
return NoEscapeAttr::CreateImplicit(S.Context);
});
}
// Handle common entity information.
ProcessAPINotes(S, D, static_cast<const api_notes::VariableInfo &>(info),
metadata);
}
/// Process API notes for a global variable.
static void ProcessAPINotes(Sema &S, VarDecl *D,
const api_notes::GlobalVariableInfo &info,
VersionedInfoMetadata metadata) {
// Handle common entity information.
ProcessAPINotes(S, D, static_cast<const api_notes::VariableInfo &>(info),
metadata);
}
/// Process API notes for an Objective-C property.
static void ProcessAPINotes(Sema &S, ObjCPropertyDecl *D,
const api_notes::ObjCPropertyInfo &info,
VersionedInfoMetadata metadata) {
// Handle common entity information.
ProcessAPINotes(S, D, static_cast<const api_notes::VariableInfo &>(info),
metadata);
if (auto asAccessors = info.getSwiftImportAsAccessors()) {
handleAPINotedAttribute<SwiftImportPropertyAsAccessorsAttr>(S, D,
*asAccessors,
metadata, [&] {
return SwiftImportPropertyAsAccessorsAttr::CreateImplicit(S.Context);
});
}
}
namespace {
typedef llvm::PointerUnion<FunctionDecl *, ObjCMethodDecl *> FunctionOrMethod;
}
/// Process API notes for a function or method.
static void ProcessAPINotes(Sema &S, FunctionOrMethod AnyFunc,
const api_notes::FunctionInfo &info,
VersionedInfoMetadata metadata) {
// Find the declaration itself.
FunctionDecl *FD = AnyFunc.dyn_cast<FunctionDecl *>();
Decl *D = FD;
ObjCMethodDecl *MD = 0;
if (!D) {
MD = AnyFunc.get<ObjCMethodDecl *>();
D = MD;
}
// Nullability of return type.
if (info.NullabilityAudited) {
applyNullability(S, D, info.getReturnTypeInfo(), metadata);
}
// Parameters.
unsigned NumParams;
if (FD)
NumParams = FD->getNumParams();
else
NumParams = MD->param_size();
bool anyTypeChanged = false;
for (unsigned I = 0; I != NumParams; ++I) {
ParmVarDecl *Param;
if (FD)
Param = FD->getParamDecl(I);
else
Param = MD->param_begin()[I];
QualType paramTypeBefore = Param->getType();
if (I < info.Params.size()) {
ProcessAPINotes(S, Param, info.Params[I], metadata);
}
// Nullability.
if (info.NullabilityAudited)
applyNullability(S, Param, info.getParamTypeInfo(I), metadata);
if (paramTypeBefore.getAsOpaquePtr() != Param->getType().getAsOpaquePtr())
anyTypeChanged = true;
}
// Result type override.
QualType overriddenResultType;
if (metadata.IsActive && !info.ResultType.empty() &&
S.ParseTypeFromStringCallback) {
auto parsedType = S.ParseTypeFromStringCallback(info.ResultType,
"<API Notes>",
D->getLocation());
if (parsedType.isUsable()) {
QualType resultType = Sema::GetTypeFromParser(parsedType.get());
if (MD) {
if (!checkAPINotesReplacementType(S, D->getLocation(),
MD->getReturnType(), resultType)) {
auto resultTypeInfo =
S.Context.getTrivialTypeSourceInfo(resultType, D->getLocation());
MD->setReturnType(resultType);
MD->setReturnTypeSourceInfo(resultTypeInfo);
}
} else if (!checkAPINotesReplacementType(S, FD->getLocation(),
FD->getReturnType(),
resultType)) {
overriddenResultType = resultType;
anyTypeChanged = true;
}
}
}
// If the result type or any of the parameter types changed for a function
// declaration, we have to rebuild the type.
if (FD && anyTypeChanged) {
if (const auto *fnProtoType = FD->getType()->getAs<FunctionProtoType>()) {
if (overriddenResultType.isNull())
overriddenResultType = fnProtoType->getReturnType();
SmallVector<QualType, 4> paramTypes;
for (auto param : FD->parameters()) {
paramTypes.push_back(param->getType());
}
FD->setType(S.Context.getFunctionType(overriddenResultType,
paramTypes,
fnProtoType->getExtProtoInfo()));
} else if (!overriddenResultType.isNull()) {
const auto *fnNoProtoType = FD->getType()->castAs<FunctionNoProtoType>();
FD->setType(
S.Context.getFunctionNoProtoType(overriddenResultType,
fnNoProtoType->getExtInfo()));
}
}
// Handle common entity information.
ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(info),
metadata);
}
/// Process API notes for a global function.
static void ProcessAPINotes(Sema &S, FunctionDecl *D,
const api_notes::GlobalFunctionInfo &info,
VersionedInfoMetadata metadata) {
// Handle common function information.
ProcessAPINotes(S, FunctionOrMethod(D),
static_cast<const api_notes::FunctionInfo &>(info), metadata);
}
/// Process API notes for an enumerator.
static void ProcessAPINotes(Sema &S, EnumConstantDecl *D,
const api_notes::EnumConstantInfo &info,
VersionedInfoMetadata metadata) {
// Handle common information.
ProcessAPINotes(S, D,
static_cast<const api_notes::CommonEntityInfo &>(info),
metadata);
}
/// Process API notes for an Objective-C method.
static void ProcessAPINotes(Sema &S, ObjCMethodDecl *D,
const api_notes::ObjCMethodInfo &info,
VersionedInfoMetadata metadata) {
// Designated initializers.
if (info.DesignatedInit) {
handleAPINotedAttribute<ObjCDesignatedInitializerAttr>(S, D, true, metadata,
[&] {
if (ObjCInterfaceDecl *IFace = D->getClassInterface()) {
IFace->setHasDesignatedInitializers();
}
return ObjCDesignatedInitializerAttr::CreateImplicit(S.Context);
});
}
// Handle common function information.
ProcessAPINotes(S, FunctionOrMethod(D),
static_cast<const api_notes::FunctionInfo &>(info), metadata);
}
/// Process API notes for a tag.
static void ProcessAPINotes(Sema &S, TagDecl *D,
const api_notes::TagInfo &info,
VersionedInfoMetadata metadata) {
if (auto extensibility = info.EnumExtensibility) {
using api_notes::EnumExtensibilityKind;
bool shouldAddAttribute = (*extensibility != EnumExtensibilityKind::None);
handleAPINotedAttribute<EnumExtensibilityAttr>(S, D, shouldAddAttribute,
metadata, [&] {
EnumExtensibilityAttr::Kind kind;
switch (extensibility.getValue()) {
case EnumExtensibilityKind::None:
llvm_unreachable("remove only");
case EnumExtensibilityKind::Open:
kind = EnumExtensibilityAttr::Open;
break;
case EnumExtensibilityKind::Closed:
kind = EnumExtensibilityAttr::Closed;
break;
}
return EnumExtensibilityAttr::CreateImplicit(S.Context, kind);
});
}
if (auto flagEnum = info.isFlagEnum()) {
handleAPINotedAttribute<FlagEnumAttr>(S, D, flagEnum.getValue(), metadata,
[&] {
return FlagEnumAttr::CreateImplicit(S.Context);
});
}
// Handle common type information.
ProcessAPINotes(S, D, static_cast<const api_notes::CommonTypeInfo &>(info),
metadata);
}
/// Process API notes for a typedef.
static void ProcessAPINotes(Sema &S, TypedefNameDecl *D,
const api_notes::TypedefInfo &info,
VersionedInfoMetadata metadata) {
// swift_wrapper
using SwiftWrapperKind = api_notes::SwiftWrapperKind;
if (auto swiftWrapper = info.SwiftWrapper) {
handleAPINotedAttribute<SwiftNewtypeAttr>(S, D,
*swiftWrapper != SwiftWrapperKind::None, metadata,
[&] {
SwiftNewtypeAttr::NewtypeKind kind;
switch (*swiftWrapper) {
case SwiftWrapperKind::None:
llvm_unreachable("Shouldn't build an attribute");
case SwiftWrapperKind::Struct:
kind = SwiftNewtypeAttr::NK_Struct;
break;
case SwiftWrapperKind::Enum:
kind = SwiftNewtypeAttr::NK_Enum;
break;
}
return SwiftNewtypeAttr::CreateImplicit(
S.Context,
SwiftNewtypeAttr::GNU_swift_wrapper,
kind);
});
}
// Handle common type information.
ProcessAPINotes(S, D, static_cast<const api_notes::CommonTypeInfo &>(info),
metadata);
}
/// Process API notes for an Objective-C class or protocol.
static void ProcessAPINotes(Sema &S, ObjCContainerDecl *D,
const api_notes::ObjCContextInfo &info,
VersionedInfoMetadata metadata) {
// Handle common type information.
ProcessAPINotes(S, D, static_cast<const api_notes::CommonTypeInfo &>(info),
metadata);
}
/// Process API notes for an Objective-C class.
static void ProcessAPINotes(Sema &S, ObjCInterfaceDecl *D,
const api_notes::ObjCContextInfo &info,
VersionedInfoMetadata metadata) {
if (auto asNonGeneric = info.getSwiftImportAsNonGeneric()) {
handleAPINotedAttribute<SwiftImportAsNonGenericAttr>(S, D, *asNonGeneric,
metadata, [&] {
return SwiftImportAsNonGenericAttr::CreateImplicit(S.Context);
});
}
if (auto objcMembers = info.getSwiftObjCMembers()) {
handleAPINotedAttribute<SwiftObjCMembersAttr>(S, D, *objcMembers,
metadata, [&] {
return SwiftObjCMembersAttr::CreateImplicit(S.Context);
});
}
// Handle information common to Objective-C classes and protocols.
ProcessAPINotes(S, static_cast<clang::ObjCContainerDecl *>(D), info,
metadata);
}
/// If we're applying API notes with an active, non-default version, and the
/// versioned API notes have a SwiftName but the declaration normally wouldn't
/// have one, add a removal attribute to make it clear that the new SwiftName
/// attribute only applies to the active version of \p D, not to all versions.
///
/// This must be run \em before processing API notes for \p D, because otherwise
/// any existing SwiftName attribute will have been packaged up in a
/// SwiftVersionedAttr.
template <typename SpecificInfo>
static void maybeAttachUnversionedSwiftName(
Sema &S, Decl *D,
const api_notes::APINotesReader::VersionedInfo<SpecificInfo> Info) {
if (D->hasAttr<SwiftNameAttr>())
return;
if (!Info.getSelected())
return;
// Is the active slice versioned, and does it set a Swift name?
VersionTuple SelectedVersion;
SpecificInfo SelectedInfoSlice;
std::tie(SelectedVersion, SelectedInfoSlice) = Info[*Info.getSelected()];
if (SelectedVersion.empty())
return;
if (SelectedInfoSlice.SwiftName.empty())
return;
// Does the unversioned slice /not/ set a Swift name?
for (const auto &VersionAndInfoSlice : Info) {
if (!VersionAndInfoSlice.first.empty())
continue;
if (!VersionAndInfoSlice.second.SwiftName.empty())
return;
}
// Then explicitly call that out with a removal attribute.
VersionedInfoMetadata DummyFutureMetadata(VersionTuple(), IsNotActive);
handleAPINotedAttribute<SwiftNameAttr>(S, D, /*add*/false,
DummyFutureMetadata,
[]() -> SwiftNameAttr * {
llvm_unreachable("should not try to add an attribute here");
});
}
/// Processes all versions of versioned API notes.
///
/// Just dispatches to the various ProcessAPINotes functions in this file.
template <typename SpecificDecl, typename SpecificInfo>
static void ProcessVersionedAPINotes(
Sema &S, SpecificDecl *D,
const api_notes::APINotesReader::VersionedInfo<SpecificInfo> Info) {
maybeAttachUnversionedSwiftName(S, D, Info);
unsigned Selected = Info.getSelected().getValueOr(Info.size());
VersionTuple Version;
SpecificInfo InfoSlice;
for (unsigned i = 0, e = Info.size(); i != e; ++i) {
std::tie(Version, InfoSlice) = Info[i];
auto Active = (i == Selected) ? IsActive : IsNotActive;
ProcessAPINotes(S, D, InfoSlice, VersionedInfoMetadata(Version, Active));
}
}
/// Process API notes that are associated with this declaration, mapping them
/// to attributes as appropriate.
void Sema::ProcessAPINotes(Decl *D) {
if (!D)
return;
// Globals.
if (D->getDeclContext()->isFileContext()) {
// Global variables.
if (auto VD = dyn_cast<VarDecl>(D)) {
for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
auto Info = Reader->lookupGlobalVariable(VD->getName());
ProcessVersionedAPINotes(*this, VD, Info);
}
return;
}
// Global functions.
if (auto FD = dyn_cast<FunctionDecl>(D)) {
if (FD->getDeclName().isIdentifier()) {
for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
auto Info = Reader->lookupGlobalFunction(FD->getName());
ProcessVersionedAPINotes(*this, FD, Info);
}
}
return;
}
// Objective-C classes.
if (auto Class = dyn_cast<ObjCInterfaceDecl>(D)) {
for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
auto Info = Reader->lookupObjCClassInfo(Class->getName());
ProcessVersionedAPINotes(*this, Class, Info);
}
return;
}
// Objective-C protocols.
if (auto Protocol = dyn_cast<ObjCProtocolDecl>(D)) {
for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
auto Info = Reader->lookupObjCProtocolInfo(Protocol->getName());
ProcessVersionedAPINotes(*this, Protocol, Info);
}
return;
}
// Tags
if (auto Tag = dyn_cast<TagDecl>(D)) {
for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
auto Info = Reader->lookupTag(Tag->getName());
ProcessVersionedAPINotes(*this, Tag, Info);
}
return;
}
// Typedefs
if (auto Typedef = dyn_cast<TypedefNameDecl>(D)) {
for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
auto Info = Reader->lookupTypedef(Typedef->getName());
ProcessVersionedAPINotes(*this, Typedef, Info);
}
return;
}
return;
}
// Enumerators.
if (D->getDeclContext()->getRedeclContext()->isFileContext()) {
if (auto EnumConstant = dyn_cast<EnumConstantDecl>(D)) {
for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
auto Info = Reader->lookupEnumConstant(EnumConstant->getName());
ProcessVersionedAPINotes(*this, EnumConstant, Info);
}
return;
}
}
if (auto ObjCContainer = dyn_cast<ObjCContainerDecl>(D->getDeclContext())) {
// Location function that looks up an Objective-C context.
auto GetContext = [&](api_notes::APINotesReader *Reader)
-> Optional<api_notes::ContextID> {
if (auto Protocol = dyn_cast<ObjCProtocolDecl>(ObjCContainer)) {
if (auto Found = Reader->lookupObjCProtocolID(Protocol->getName()))
return *Found;
return None;
}
if (auto Impl = dyn_cast<ObjCCategoryImplDecl>(ObjCContainer)) {
if (auto Cat = Impl->getCategoryDecl())
ObjCContainer = Cat;
else
return None;
}
if (auto Category = dyn_cast<ObjCCategoryDecl>(ObjCContainer)) {
if (Category->getClassInterface())
ObjCContainer = Category->getClassInterface();
else
return None;
}
if (auto Impl = dyn_cast<ObjCImplDecl>(ObjCContainer)) {
if (Impl->getClassInterface())
ObjCContainer = Impl->getClassInterface();
else
return None;
}
if (auto Class = dyn_cast<ObjCInterfaceDecl>(ObjCContainer)) {
if (auto Found = Reader->lookupObjCClassID(Class->getName()))
return *Found;
return None;
}
return None;
};
// Objective-C methods.
if (auto Method = dyn_cast<ObjCMethodDecl>(D)) {
for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
if (auto Context = GetContext(Reader)) {
// Map the selector.
Selector Sel = Method->getSelector();
SmallVector<StringRef, 2> SelPieces;
if (Sel.isUnarySelector())
SelPieces.push_back(Sel.getNameForSlot(0));
else {
for (unsigned i = 0, n = Sel.getNumArgs(); i != n; ++i)
SelPieces.push_back(Sel.getNameForSlot(i));
}
api_notes::ObjCSelectorRef SelectorRef;
SelectorRef.NumPieces = Sel.getNumArgs();
SelectorRef.Identifiers = SelPieces;
auto Info = Reader->lookupObjCMethod(*Context, SelectorRef,
Method->isInstanceMethod());
ProcessVersionedAPINotes(*this, Method, Info);
}
}
}
// Objective-C properties.
if (auto Property = dyn_cast<ObjCPropertyDecl>(D)) {
for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
if (auto Context = GetContext(Reader)) {
bool isInstanceProperty =
(Property->getPropertyAttributesAsWritten() &
ObjCPropertyDecl::OBJC_PR_class) == 0;
auto Info = Reader->lookupObjCProperty(*Context, Property->getName(),
isInstanceProperty);
ProcessVersionedAPINotes(*this, Property, Info);
}
}
return;
}
return;
}
}