cb424d7448
verify-patch-sanity.py validates every active recipe .patch has internally- consistent hunk line counts — catching the 'malformed patch at line N' failure at commit/CI/preflight time instead of hours into a cook. This cycle hit that class three times (qtwaylandscanner, sddm, xwayland), each only discovered when cookbook tried to apply the patch. Running it across the repo found 29 latent malformed patches (validated against GNU patch: e.g. relibc/P3-sysv-ipc reproduces 'malformed patch at line 22'). They were harmless only because they sit in vendored recipes (baked, not re- applied) — but would fail on any version-bump re-derivation. --fix recounts the hunk headers (body untouched) and repaired all 29. Wired into build-preflight.sh (Phase 1.0D) and redbear-ci.yml, with a unit test (test-patch-sanity.sh). Skips archived/legacy trees and unvalidatable formats (empty placeholders, bare-@@ git hunks).
1200 lines
44 KiB
C++
1200 lines
44 KiB
C++
//===--- SemaAPINotes.cpp - API Notes Handling ----------------------------===//
|
|
//
|
|
// 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
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements the mapping from API notes to declaration attributes.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "CheckExprLifetime.h"
|
|
#include "TypeLocBuilder.h"
|
|
#include "clang/APINotes/APINotesReader.h"
|
|
#include "clang/AST/Decl.h"
|
|
#include "clang/AST/DeclCXX.h"
|
|
#include "clang/AST/DeclObjC.h"
|
|
#include "clang/AST/TypeLoc.h"
|
|
#include "clang/Basic/SourceLocation.h"
|
|
#include "clang/Lex/Lexer.h"
|
|
#include "clang/Sema/SemaObjC.h"
|
|
#include "clang/Sema/SemaSwift.h"
|
|
#include <stack>
|
|
|
|
using namespace clang;
|
|
|
|
namespace {
|
|
enum class IsActive_t : bool { Inactive, Active };
|
|
enum class IsSubstitution_t : bool { Original, Replacement };
|
|
|
|
struct VersionedInfoMetadata {
|
|
/// An empty version refers to unversioned metadata.
|
|
VersionTuple Version;
|
|
unsigned IsActive : 1;
|
|
unsigned IsReplacement : 1;
|
|
|
|
VersionedInfoMetadata(VersionTuple Version, IsActive_t Active,
|
|
IsSubstitution_t Replacement)
|
|
: Version(Version), IsActive(Active == IsActive_t::Active),
|
|
IsReplacement(Replacement == IsSubstitution_t::Replacement) {}
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
/// Determine whether this is a multi-level pointer type.
|
|
static bool isIndirectPointerType(QualType Type) {
|
|
QualType Pointee = Type->getPointeeType();
|
|
if (Pointee.isNull())
|
|
return false;
|
|
|
|
return Pointee->isAnyPointerType() || Pointee->isObjCObjectPointerType() ||
|
|
Pointee->isMemberPointerType();
|
|
}
|
|
|
|
static void applyAPINotesType(Sema &S, Decl *decl, StringRef typeString,
|
|
VersionedInfoMetadata metadata) {
|
|
if (typeString.empty())
|
|
|
|
return;
|
|
|
|
// Version-independent APINotes add "type" annotations
|
|
// with a versioned attribute for the client to select and apply.
|
|
if (S.captureSwiftVersionIndependentAPINotes()) {
|
|
auto *typeAttr = SwiftTypeAttr::CreateImplicit(S.Context, typeString);
|
|
auto *versioned = SwiftVersionedAdditionAttr::CreateImplicit(
|
|
S.Context, metadata.Version, typeAttr, metadata.IsReplacement);
|
|
decl->addAttr(versioned);
|
|
} else {
|
|
if (!metadata.IsActive)
|
|
return;
|
|
S.ApplyAPINotesType(decl, typeString);
|
|
}
|
|
}
|
|
|
|
/// Apply nullability to the given declaration.
|
|
static void applyNullability(Sema &S, Decl *decl, NullabilityKind nullability,
|
|
VersionedInfoMetadata metadata) {
|
|
// Version-independent APINotes add "nullability" annotations
|
|
// with a versioned attribute for the client to select and apply.
|
|
if (S.captureSwiftVersionIndependentAPINotes()) {
|
|
SwiftNullabilityAttr::Kind attrNullabilityKind;
|
|
switch (nullability) {
|
|
case NullabilityKind::NonNull:
|
|
attrNullabilityKind = SwiftNullabilityAttr::Kind::NonNull;
|
|
break;
|
|
case NullabilityKind::Nullable:
|
|
attrNullabilityKind = SwiftNullabilityAttr::Kind::Nullable;
|
|
break;
|
|
case NullabilityKind::Unspecified:
|
|
attrNullabilityKind = SwiftNullabilityAttr::Kind::Unspecified;
|
|
break;
|
|
case NullabilityKind::NullableResult:
|
|
attrNullabilityKind = SwiftNullabilityAttr::Kind::NullableResult;
|
|
break;
|
|
}
|
|
auto *nullabilityAttr =
|
|
SwiftNullabilityAttr::CreateImplicit(S.Context, attrNullabilityKind);
|
|
auto *versioned = SwiftVersionedAdditionAttr::CreateImplicit(
|
|
S.Context, metadata.Version, nullabilityAttr, metadata.IsReplacement);
|
|
decl->addAttr(versioned);
|
|
return;
|
|
} else {
|
|
if (!metadata.IsActive)
|
|
return;
|
|
|
|
S.ApplyNullability(decl, nullability);
|
|
}
|
|
}
|
|
|
|
/// Copy a string into ASTContext-allocated memory.
|
|
static StringRef ASTAllocateString(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());
|
|
}
|
|
|
|
static AttributeCommonInfo getPlaceholderAttrInfo() {
|
|
return AttributeCommonInfo(SourceRange(),
|
|
AttributeCommonInfo::UnknownAttribute,
|
|
{AttributeCommonInfo::AS_GNU,
|
|
/*Spelling*/ 0, /*IsAlignas*/ false,
|
|
/*IsRegularKeywordAttribute*/ false});
|
|
}
|
|
|
|
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 IsAddition 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 IsAddition, VersionedInfoMetadata Metadata,
|
|
llvm::function_ref<A *()> CreateAttr,
|
|
llvm::function_ref<Decl::attr_iterator(const Decl *)> GetExistingAttr) {
|
|
if (Metadata.IsActive) {
|
|
auto Existing = GetExistingAttr(D);
|
|
if (Existing != D->attr_end()) {
|
|
// Remove the existing attribute, and treat it as a superseded
|
|
// non-versioned attribute.
|
|
auto *Versioned = SwiftVersionedAdditionAttr::CreateImplicit(
|
|
S.Context, Metadata.Version, *Existing, /*IsReplacedByActive*/ true);
|
|
|
|
D->getAttrs().erase(Existing);
|
|
D->addAttr(Versioned);
|
|
}
|
|
|
|
// If we're supposed to add a new attribute, do so.
|
|
if (IsAddition) {
|
|
if (auto Attr = CreateAttr())
|
|
D->addAttr(Attr);
|
|
}
|
|
|
|
return;
|
|
}
|
|
if (IsAddition) {
|
|
if (auto Attr = CreateAttr()) {
|
|
auto *Versioned = SwiftVersionedAdditionAttr::CreateImplicit(
|
|
S.Context, Metadata.Version, Attr,
|
|
/*IsReplacedByActive*/ Metadata.IsReplacement);
|
|
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,
|
|
/*IsReplacedByActive*/ Metadata.IsReplacement);
|
|
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, [](const Decl *D) {
|
|
return llvm::find_if(D->attrs(),
|
|
[](const Attr *Next) { return isa<A>(Next); });
|
|
});
|
|
}
|
|
} // namespace
|
|
|
|
template <typename A>
|
|
static void handleAPINotedRetainCountAttribute(Sema &S, Decl *D,
|
|
bool ShouldAddAttribute,
|
|
VersionedInfoMetadata Metadata) {
|
|
// The template argument has a default to make the "removal" case more
|
|
// concise; it doesn't matter /which/ attribute is being removed.
|
|
handleAPINotedAttribute<A>(
|
|
S, D, ShouldAddAttribute, Metadata,
|
|
[&] { return new (S.Context) A(S.Context, getPlaceholderAttrInfo()); },
|
|
[](const Decl *D) -> Decl::attr_iterator {
|
|
return llvm::find_if(D->attrs(), [](const Attr *Next) -> bool {
|
|
return isa<CFReturnsRetainedAttr>(Next) ||
|
|
isa<CFReturnsNotRetainedAttr>(Next) ||
|
|
isa<NSReturnsRetainedAttr>(Next) ||
|
|
isa<NSReturnsNotRetainedAttr>(Next) ||
|
|
isa<CFAuditedTransferAttr>(Next);
|
|
});
|
|
});
|
|
}
|
|
|
|
static void handleAPINotedRetainCountConvention(
|
|
Sema &S, Decl *D, VersionedInfoMetadata Metadata,
|
|
std::optional<api_notes::RetainCountConventionKind> Convention) {
|
|
if (!Convention)
|
|
return;
|
|
switch (*Convention) {
|
|
case api_notes::RetainCountConventionKind::None:
|
|
if (isa<FunctionDecl>(D)) {
|
|
handleAPINotedRetainCountAttribute<CFUnknownTransferAttr>(
|
|
S, D, /*shouldAddAttribute*/ true, Metadata);
|
|
} else {
|
|
handleAPINotedRetainCountAttribute<CFReturnsRetainedAttr>(
|
|
S, D, /*shouldAddAttribute*/ false, Metadata);
|
|
}
|
|
break;
|
|
case api_notes::RetainCountConventionKind::CFReturnsRetained:
|
|
handleAPINotedRetainCountAttribute<CFReturnsRetainedAttr>(
|
|
S, D, /*shouldAddAttribute*/ true, Metadata);
|
|
break;
|
|
case api_notes::RetainCountConventionKind::CFReturnsNotRetained:
|
|
handleAPINotedRetainCountAttribute<CFReturnsNotRetainedAttr>(
|
|
S, D, /*shouldAddAttribute*/ true, Metadata);
|
|
break;
|
|
case api_notes::RetainCountConventionKind::NSReturnsRetained:
|
|
handleAPINotedRetainCountAttribute<NSReturnsRetainedAttr>(
|
|
S, D, /*shouldAddAttribute*/ true, Metadata);
|
|
break;
|
|
case api_notes::RetainCountConventionKind::NSReturnsNotRetained:
|
|
handleAPINotedRetainCountAttribute<NSReturnsNotRetainedAttr>(
|
|
S, D, /*shouldAddAttribute*/ true, Metadata);
|
|
break;
|
|
}
|
|
}
|
|
|
|
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 new (S.Context)
|
|
UnavailableAttr(S.Context, getPlaceholderAttrInfo(),
|
|
ASTAllocateString(S.Context, Info.UnavailableMsg));
|
|
});
|
|
}
|
|
|
|
if (Info.UnavailableInSwift) {
|
|
handleAPINotedAttribute<AvailabilityAttr>(
|
|
S, D, true, Metadata,
|
|
[&] {
|
|
return new (S.Context) AvailabilityAttr(
|
|
S.Context, getPlaceholderAttrInfo(),
|
|
&S.Context.Idents.get("swift"), VersionTuple(), VersionTuple(),
|
|
VersionTuple(),
|
|
/*Unavailable=*/true,
|
|
ASTAllocateString(S.Context, Info.UnavailableMsg),
|
|
/*Strict=*/false,
|
|
/*Replacement=*/StringRef(),
|
|
/*Priority=*/Sema::AP_Explicit,
|
|
/*Environment=*/nullptr);
|
|
},
|
|
[](const Decl *D) {
|
|
return llvm::find_if(D->attrs(), [](const Attr *next) -> bool {
|
|
if (const auto *AA = dyn_cast<AvailabilityAttr>(next))
|
|
if (const auto *II = AA->getPlatform())
|
|
return II->isStr("swift");
|
|
return false;
|
|
});
|
|
});
|
|
}
|
|
|
|
// swift_private
|
|
if (auto SwiftPrivate = Info.isSwiftPrivate()) {
|
|
handleAPINotedAttribute<SwiftPrivateAttr>(
|
|
S, D, *SwiftPrivate, Metadata, [&] {
|
|
return new (S.Context)
|
|
SwiftPrivateAttr(S.Context, getPlaceholderAttrInfo());
|
|
});
|
|
}
|
|
|
|
// swift_name
|
|
if (!Info.SwiftName.empty()) {
|
|
handleAPINotedAttribute<SwiftNameAttr>(
|
|
S, D, true, Metadata, [&]() -> SwiftNameAttr * {
|
|
AttributeFactory AF{};
|
|
AttributePool AP{AF};
|
|
auto &C = S.getASTContext();
|
|
ParsedAttr *SNA = AP.create(
|
|
&C.Idents.get("swift_name"), SourceRange(), AttributeScopeInfo(),
|
|
nullptr, nullptr, nullptr, ParsedAttr::Form::GNU());
|
|
|
|
if (!S.Swift().DiagnoseName(D, Info.SwiftName, D->getLocation(), *SNA,
|
|
/*IsAsync=*/false))
|
|
return nullptr;
|
|
|
|
return new (S.Context)
|
|
SwiftNameAttr(S.Context, getPlaceholderAttrInfo(),
|
|
ASTAllocateString(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 new (S.Context)
|
|
SwiftBridgeAttr(S.Context, getPlaceholderAttrInfo(),
|
|
ASTAllocateString(S.Context, *SwiftBridge));
|
|
});
|
|
}
|
|
|
|
// ns_error_domain
|
|
if (auto NSErrorDomain = Info.getNSErrorDomain()) {
|
|
handleAPINotedAttribute<NSErrorDomainAttr>(
|
|
S, D, !NSErrorDomain->empty(), Metadata, [&] {
|
|
return new (S.Context)
|
|
NSErrorDomainAttr(S.Context, getPlaceholderAttrInfo(),
|
|
&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;
|
|
}
|
|
|
|
void Sema::ApplyAPINotesType(Decl *D, StringRef TypeString) {
|
|
if (!TypeString.empty() && ParseTypeFromStringCallback) {
|
|
auto ParsedType = ParseTypeFromStringCallback(TypeString, "<API Notes>",
|
|
D->getLocation());
|
|
if (ParsedType.isUsable()) {
|
|
QualType Type = Sema::GetTypeFromParser(ParsedType.get());
|
|
auto TypeInfo = Context.getTrivialTypeSourceInfo(Type, D->getLocation());
|
|
if (auto Var = dyn_cast<VarDecl>(D)) {
|
|
// Make adjustments to parameter types.
|
|
if (isa<ParmVarDecl>(Var)) {
|
|
Type = ObjC().AdjustParameterTypeForObjCAutoRefCount(
|
|
Type, D->getLocation(), TypeInfo);
|
|
Type = Context.getAdjustedParameterType(Type);
|
|
}
|
|
|
|
if (!checkAPINotesReplacementType(*this, Var->getLocation(),
|
|
Var->getType(), Type)) {
|
|
Var->setType(Type);
|
|
Var->setTypeSourceInfo(TypeInfo);
|
|
}
|
|
} else if (auto property = dyn_cast<ObjCPropertyDecl>(D)) {
|
|
if (!checkAPINotesReplacementType(*this, property->getLocation(),
|
|
property->getType(), Type)) {
|
|
property->setType(Type, TypeInfo);
|
|
}
|
|
} else {
|
|
llvm_unreachable("API notes allowed a type on an unknown declaration");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void Sema::ApplyNullability(Decl *D, NullabilityKind Nullability) {
|
|
auto GetModified =
|
|
[&](class Decl *D, QualType QT,
|
|
NullabilityKind Nullability) -> std::optional<QualType> {
|
|
QualType Original = QT;
|
|
CheckImplicitNullabilityTypeSpecifier(QT, Nullability, D->getLocation(),
|
|
isa<ParmVarDecl>(D),
|
|
/*OverrideExisting=*/true);
|
|
return (QT.getTypePtr() != Original.getTypePtr()) ? std::optional(QT)
|
|
: std::nullopt;
|
|
};
|
|
|
|
if (auto Function = dyn_cast<FunctionDecl>(D)) {
|
|
if (auto Modified =
|
|
GetModified(D, Function->getReturnType(), Nullability)) {
|
|
const FunctionType *FnType = Function->getType()->castAs<FunctionType>();
|
|
if (const FunctionProtoType *proto = dyn_cast<FunctionProtoType>(FnType))
|
|
Function->setType(Context.getFunctionType(
|
|
*Modified, proto->getParamTypes(), proto->getExtProtoInfo()));
|
|
else
|
|
Function->setType(
|
|
Context.getFunctionNoProtoType(*Modified, FnType->getExtInfo()));
|
|
}
|
|
} else if (auto Method = dyn_cast<ObjCMethodDecl>(D)) {
|
|
if (auto Modified = GetModified(D, Method->getReturnType(), Nullability)) {
|
|
Method->setReturnType(*Modified);
|
|
|
|
// Make it a context-sensitive keyword if we can.
|
|
if (!isIndirectPointerType(*Modified))
|
|
Method->setObjCDeclQualifier(Decl::ObjCDeclQualifier(
|
|
Method->getObjCDeclQualifier() | Decl::OBJC_TQ_CSNullability));
|
|
}
|
|
} else if (auto Value = dyn_cast<ValueDecl>(D)) {
|
|
if (auto Modified = GetModified(D, Value->getType(), Nullability)) {
|
|
Value->setType(*Modified);
|
|
|
|
// Make it a context-sensitive keyword if we can.
|
|
if (auto Parm = dyn_cast<ParmVarDecl>(D)) {
|
|
if (Parm->isObjCMethodParameter() && !isIndirectPointerType(*Modified))
|
|
Parm->setObjCDeclQualifier(Decl::ObjCDeclQualifier(
|
|
Parm->getObjCDeclQualifier() | Decl::OBJC_TQ_CSNullability));
|
|
}
|
|
}
|
|
} else if (auto Property = dyn_cast<ObjCPropertyDecl>(D)) {
|
|
if (auto Modified = GetModified(D, Property->getType(), Nullability)) {
|
|
Property->setType(*Modified, Property->getTypeSourceInfo());
|
|
|
|
// Make it a property attribute if we can.
|
|
if (!isIndirectPointerType(*Modified))
|
|
Property->setPropertyAttributes(
|
|
ObjCPropertyAttribute::kind_null_resettable);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Process API notes for a variable or property.
|
|
static void ProcessAPINotes(Sema &S, Decl *D,
|
|
const api_notes::VariableInfo &Info,
|
|
VersionedInfoMetadata Metadata) {
|
|
// Type override.
|
|
applyAPINotesType(S, D, Info.getType(), Metadata);
|
|
|
|
// 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 new (S.Context) NoEscapeAttr(S.Context, getPlaceholderAttrInfo());
|
|
});
|
|
|
|
if (auto Lifetimebound = Info.isLifetimebound())
|
|
handleAPINotedAttribute<LifetimeBoundAttr>(
|
|
S, D, *Lifetimebound, Metadata, [&] {
|
|
return new (S.Context)
|
|
LifetimeBoundAttr(S.Context, getPlaceholderAttrInfo());
|
|
});
|
|
|
|
// Retain count convention
|
|
handleAPINotedRetainCountConvention(S, D, Metadata,
|
|
Info.getRetainCountConvention());
|
|
|
|
// 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 a C field.
|
|
static void ProcessAPINotes(Sema &S, FieldDecl *D,
|
|
const api_notes::FieldInfo &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 new (S.Context) SwiftImportPropertyAsAccessorsAttr(
|
|
S.Context, getPlaceholderAttrInfo());
|
|
});
|
|
}
|
|
}
|
|
|
|
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 = dyn_cast<FunctionDecl *>(AnyFunc);
|
|
Decl *D = FD;
|
|
ObjCMethodDecl *MD = nullptr;
|
|
if (!D) {
|
|
MD = cast<ObjCMethodDecl *>(AnyFunc);
|
|
D = MD;
|
|
}
|
|
|
|
assert((FD || MD) && "Expecting Function or ObjCMethod");
|
|
|
|
// Nullability of return type.
|
|
if (Info.NullabilityAudited)
|
|
applyNullability(S, D, Info.getReturnTypeInfo(), Metadata);
|
|
|
|
// Parameters.
|
|
unsigned NumParams = FD ? FD->getNumParams() : MD->param_size();
|
|
|
|
bool AnyTypeChanged = false;
|
|
for (unsigned I = 0; I != NumParams; ++I) {
|
|
ParmVarDecl *Param = FD ? FD->getParamDecl(I) : 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;
|
|
}
|
|
|
|
// returns_(un)retained
|
|
if (!Info.SwiftReturnOwnership.empty())
|
|
D->addAttr(SwiftAttrAttr::Create(S.Context,
|
|
"returns_" + Info.SwiftReturnOwnership));
|
|
|
|
// 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()));
|
|
}
|
|
}
|
|
|
|
// Retain count convention
|
|
handleAPINotedRetainCountConvention(S, D, Metadata,
|
|
Info.getRetainCountConvention());
|
|
|
|
// Handle common entity information.
|
|
ProcessAPINotes(S, D, static_cast<const api_notes::CommonEntityInfo &>(Info),
|
|
Metadata);
|
|
}
|
|
|
|
/// Process API notes for a C++ method.
|
|
static void ProcessAPINotes(Sema &S, CXXMethodDecl *Method,
|
|
const api_notes::CXXMethodInfo &Info,
|
|
VersionedInfoMetadata Metadata) {
|
|
if (Info.This && Info.This->isLifetimebound() &&
|
|
!sema::implicitObjectParamIsLifetimeBound(Method)) {
|
|
auto MethodType = Method->getType();
|
|
auto *attr = ::new (S.Context)
|
|
LifetimeBoundAttr(S.Context, getPlaceholderAttrInfo());
|
|
QualType AttributedType =
|
|
S.Context.getAttributedType(attr, MethodType, MethodType);
|
|
TypeLocBuilder TLB;
|
|
TLB.pushFullCopy(Method->getTypeSourceInfo()->getTypeLoc());
|
|
AttributedTypeLoc TyLoc = TLB.push<AttributedTypeLoc>(AttributedType);
|
|
TyLoc.setAttr(attr);
|
|
Method->setType(AttributedType);
|
|
Method->setTypeSourceInfo(TLB.getTypeSourceInfo(S.Context, AttributedType));
|
|
}
|
|
|
|
ProcessAPINotes(S, (FunctionOrMethod)Method, 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 new (S.Context) ObjCDesignatedInitializerAttr(
|
|
S.Context, getPlaceholderAttrInfo());
|
|
});
|
|
}
|
|
|
|
// 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 ImportAs = Info.SwiftImportAs)
|
|
D->addAttr(SwiftAttrAttr::Create(S.Context, "import_" + ImportAs.value()));
|
|
|
|
if (auto RetainOp = Info.SwiftRetainOp)
|
|
D->addAttr(SwiftAttrAttr::Create(S.Context, "retain:" + RetainOp.value()));
|
|
|
|
if (auto ReleaseOp = Info.SwiftReleaseOp)
|
|
D->addAttr(
|
|
SwiftAttrAttr::Create(S.Context, "release:" + ReleaseOp.value()));
|
|
if (auto DefaultOwnership = Info.SwiftDefaultOwnership)
|
|
D->addAttr(SwiftAttrAttr::Create(
|
|
S.Context, "returned_as_" + DefaultOwnership.value() + "_by_default"));
|
|
|
|
if (auto ConformsTo = Info.SwiftConformance)
|
|
D->addAttr(
|
|
SwiftAttrAttr::Create(S.Context, "conforms_to:" + ConformsTo.value()));
|
|
|
|
if (auto Copyable = Info.isSwiftCopyable()) {
|
|
if (!*Copyable)
|
|
D->addAttr(SwiftAttrAttr::Create(S.Context, "~Copyable"));
|
|
}
|
|
|
|
if (auto Escapable = Info.isSwiftEscapable()) {
|
|
D->addAttr(SwiftAttrAttr::Create(S.Context,
|
|
*Escapable ? "Escapable" : "~Escapable"));
|
|
}
|
|
|
|
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) {
|
|
case EnumExtensibilityKind::None:
|
|
llvm_unreachable("remove only");
|
|
case EnumExtensibilityKind::Open:
|
|
kind = EnumExtensibilityAttr::Open;
|
|
break;
|
|
case EnumExtensibilityKind::Closed:
|
|
kind = EnumExtensibilityAttr::Closed;
|
|
break;
|
|
}
|
|
return new (S.Context)
|
|
EnumExtensibilityAttr(S.Context, getPlaceholderAttrInfo(), kind);
|
|
});
|
|
}
|
|
|
|
if (auto FlagEnum = Info.isFlagEnum()) {
|
|
handleAPINotedAttribute<FlagEnumAttr>(S, D, *FlagEnum, Metadata, [&] {
|
|
return new (S.Context) FlagEnumAttr(S.Context, getPlaceholderAttrInfo());
|
|
});
|
|
}
|
|
|
|
// 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::SwiftNewTypeKind;
|
|
|
|
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;
|
|
}
|
|
AttributeCommonInfo SyntaxInfo{
|
|
SourceRange(),
|
|
AttributeCommonInfo::AT_SwiftNewType,
|
|
{AttributeCommonInfo::AS_GNU, SwiftNewTypeAttr::GNU_swift_wrapper,
|
|
/*IsAlignas*/ false, /*IsRegularKeywordAttribute*/ false}};
|
|
return new (S.Context) SwiftNewTypeAttr(S.Context, SyntaxInfo, 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::ContextInfo &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::ContextInfo &Info,
|
|
VersionedInfoMetadata Metadata) {
|
|
if (auto AsNonGeneric = Info.getSwiftImportAsNonGeneric()) {
|
|
handleAPINotedAttribute<SwiftImportAsNonGenericAttr>(
|
|
S, D, *AsNonGeneric, Metadata, [&] {
|
|
return new (S.Context)
|
|
SwiftImportAsNonGenericAttr(S.Context, getPlaceholderAttrInfo());
|
|
});
|
|
}
|
|
|
|
if (auto ObjcMembers = Info.getSwiftObjCMembers()) {
|
|
handleAPINotedAttribute<SwiftObjCMembersAttr>(
|
|
S, D, *ObjcMembers, Metadata, [&] {
|
|
return new (S.Context)
|
|
SwiftObjCMembersAttr(S.Context, getPlaceholderAttrInfo());
|
|
});
|
|
}
|
|
|
|
// 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
|
|
/// SwiftVersionedAdditionAttr.
|
|
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(
|
|
SelectedVersion, IsActive_t::Inactive, IsSubstitution_t::Replacement);
|
|
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) {
|
|
|
|
if (!S.captureSwiftVersionIndependentAPINotes())
|
|
maybeAttachUnversionedSwiftName(S, D, Info);
|
|
|
|
unsigned Selected = Info.getSelected().value_or(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_t::Active : IsActive_t::Inactive;
|
|
auto Replacement = IsSubstitution_t::Original;
|
|
|
|
// When collection all APINotes as version-independent,
|
|
// capture all as inactive and defer to the client select the
|
|
// right one.
|
|
if (S.captureSwiftVersionIndependentAPINotes()) {
|
|
Active = IsActive_t::Inactive;
|
|
Replacement = IsSubstitution_t::Original;
|
|
} else if (Active == IsActive_t::Inactive && Version.empty()) {
|
|
Replacement = IsSubstitution_t::Replacement;
|
|
Version = Info[Selected].first;
|
|
}
|
|
|
|
ProcessAPINotes(S, D, InfoSlice,
|
|
VersionedInfoMetadata(Version, Active, Replacement));
|
|
}
|
|
}
|
|
|
|
static std::optional<api_notes::Context>
|
|
UnwindNamespaceContext(DeclContext *DC, api_notes::APINotesManager &APINotes) {
|
|
if (auto NamespaceContext = dyn_cast<NamespaceDecl>(DC)) {
|
|
for (auto Reader : APINotes.findAPINotes(NamespaceContext->getLocation())) {
|
|
// Retrieve the context ID for the parent namespace of the decl.
|
|
std::stack<NamespaceDecl *> NamespaceStack;
|
|
{
|
|
for (auto CurrentNamespace = NamespaceContext; CurrentNamespace;
|
|
CurrentNamespace =
|
|
dyn_cast<NamespaceDecl>(CurrentNamespace->getParent())) {
|
|
if (!CurrentNamespace->isInlineNamespace())
|
|
NamespaceStack.push(CurrentNamespace);
|
|
}
|
|
}
|
|
std::optional<api_notes::ContextID> NamespaceID;
|
|
while (!NamespaceStack.empty()) {
|
|
auto CurrentNamespace = NamespaceStack.top();
|
|
NamespaceStack.pop();
|
|
NamespaceID =
|
|
Reader->lookupNamespaceID(CurrentNamespace->getName(), NamespaceID);
|
|
if (!NamespaceID)
|
|
return std::nullopt;
|
|
}
|
|
if (NamespaceID)
|
|
return api_notes::Context(*NamespaceID,
|
|
api_notes::ContextKind::Namespace);
|
|
}
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
static std::optional<api_notes::Context>
|
|
UnwindTagContext(TagDecl *DC, api_notes::APINotesManager &APINotes) {
|
|
assert(DC && "tag context must not be null");
|
|
for (auto Reader : APINotes.findAPINotes(DC->getLocation())) {
|
|
// Retrieve the context ID for the parent tag of the decl.
|
|
std::stack<TagDecl *> TagStack;
|
|
{
|
|
for (auto CurrentTag = DC; CurrentTag;
|
|
CurrentTag = dyn_cast<TagDecl>(CurrentTag->getParent()))
|
|
TagStack.push(CurrentTag);
|
|
}
|
|
assert(!TagStack.empty());
|
|
std::optional<api_notes::Context> Ctx =
|
|
UnwindNamespaceContext(TagStack.top()->getDeclContext(), APINotes);
|
|
while (!TagStack.empty()) {
|
|
auto CurrentTag = TagStack.top();
|
|
TagStack.pop();
|
|
auto CtxID = Reader->lookupTagID(CurrentTag->getName(), Ctx);
|
|
if (!CtxID)
|
|
return std::nullopt;
|
|
Ctx = api_notes::Context(*CtxID, api_notes::ContextKind::Tag);
|
|
}
|
|
return Ctx;
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
/// Process API notes that are associated with this declaration, mapping them
|
|
/// to attributes as appropriate.
|
|
void Sema::ProcessAPINotes(Decl *D) {
|
|
if (!D)
|
|
return;
|
|
|
|
auto *DC = D->getDeclContext();
|
|
// Globals.
|
|
if (DC->isFileContext() || DC->isNamespace() || DC->isExternCContext() ||
|
|
DC->isExternCXXContext()) {
|
|
std::optional<api_notes::Context> APINotesContext =
|
|
UnwindNamespaceContext(DC, APINotes);
|
|
// Global variables.
|
|
if (auto VD = dyn_cast<VarDecl>(D)) {
|
|
for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
|
|
auto Info =
|
|
Reader->lookupGlobalVariable(VD->getName(), APINotesContext);
|
|
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(), APINotesContext);
|
|
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)) {
|
|
// Determine the name of the entity to search for. If this is an
|
|
// anonymous tag that gets its linked name from a typedef, look for the
|
|
// typedef name. This allows tag-specific information to be added
|
|
// to the declaration.
|
|
std::string LookupName;
|
|
if (auto typedefName = Tag->getTypedefNameForAnonDecl())
|
|
LookupName = typedefName->getName().str();
|
|
else
|
|
LookupName = Tag->getName().str();
|
|
|
|
// Use the source location to discern if this Tag is an OPTIONS macro.
|
|
// For now we would like to limit this trick of looking up the APINote tag
|
|
// using the EnumDecl's QualType in the case where the enum is anonymous.
|
|
// This is only being used to support APINotes lookup for C++
|
|
// NS/CF_OPTIONS when C++-Interop is enabled.
|
|
std::string MacroName =
|
|
LookupName.empty() && Tag->getOuterLocStart().isMacroID()
|
|
? clang::Lexer::getImmediateMacroName(
|
|
Tag->getOuterLocStart(),
|
|
Tag->getASTContext().getSourceManager(), LangOpts)
|
|
.str()
|
|
: "";
|
|
|
|
if (LookupName.empty() && isa<clang::EnumDecl>(Tag) &&
|
|
(MacroName == "CF_OPTIONS" || MacroName == "NS_OPTIONS" ||
|
|
MacroName == "OBJC_OPTIONS" || MacroName == "SWIFT_OPTIONS")) {
|
|
|
|
clang::QualType T = llvm::cast<clang::EnumDecl>(Tag)->getIntegerType();
|
|
LookupName = clang::QualType::getAsString(
|
|
T.split(), getASTContext().getPrintingPolicy());
|
|
}
|
|
|
|
for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
|
|
if (auto ParentTag = dyn_cast<TagDecl>(Tag->getDeclContext()))
|
|
APINotesContext = UnwindTagContext(ParentTag, APINotes);
|
|
auto Info = Reader->lookupTag(LookupName, APINotesContext);
|
|
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(), APINotesContext);
|
|
ProcessVersionedAPINotes(*this, Typedef, Info);
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Enumerators.
|
|
if (DC->getRedeclContext()->isFileContext() ||
|
|
DC->getRedeclContext()->isExternCContext()) {
|
|
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>(DC)) {
|
|
// Location function that looks up an Objective-C context.
|
|
auto GetContext = [&](api_notes::APINotesReader *Reader)
|
|
-> std::optional<api_notes::ContextID> {
|
|
if (auto Protocol = dyn_cast<ObjCProtocolDecl>(ObjCContainer)) {
|
|
if (auto Found = Reader->lookupObjCProtocolID(Protocol->getName()))
|
|
return *Found;
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
if (auto Impl = dyn_cast<ObjCCategoryImplDecl>(ObjCContainer)) {
|
|
if (auto Cat = Impl->getCategoryDecl())
|
|
ObjCContainer = Cat->getClassInterface();
|
|
else
|
|
return std::nullopt;
|
|
}
|
|
|
|
if (auto Category = dyn_cast<ObjCCategoryDecl>(ObjCContainer)) {
|
|
if (Category->getClassInterface())
|
|
ObjCContainer = Category->getClassInterface();
|
|
else
|
|
return std::nullopt;
|
|
}
|
|
|
|
if (auto Impl = dyn_cast<ObjCImplDecl>(ObjCContainer)) {
|
|
if (Impl->getClassInterface())
|
|
ObjCContainer = Impl->getClassInterface();
|
|
else
|
|
return std::nullopt;
|
|
}
|
|
|
|
if (auto Class = dyn_cast<ObjCInterfaceDecl>(ObjCContainer)) {
|
|
if (auto Found = Reader->lookupObjCClassID(Class->getName()))
|
|
return *Found;
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
return std::nullopt;
|
|
};
|
|
|
|
// 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.NumArgs = 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() &
|
|
ObjCPropertyAttribute::kind_class) == 0;
|
|
auto Info = Reader->lookupObjCProperty(*Context, Property->getName(),
|
|
isInstanceProperty);
|
|
ProcessVersionedAPINotes(*this, Property, Info);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (auto TagContext = dyn_cast<TagDecl>(DC)) {
|
|
if (auto CXXMethod = dyn_cast<CXXMethodDecl>(D)) {
|
|
if (!isa<CXXConstructorDecl>(CXXMethod) &&
|
|
!isa<CXXDestructorDecl>(CXXMethod) &&
|
|
!isa<CXXConversionDecl>(CXXMethod) &&
|
|
!CXXMethod->isOverloadedOperator()) {
|
|
for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
|
|
if (auto Context = UnwindTagContext(TagContext, APINotes)) {
|
|
auto Info =
|
|
Reader->lookupCXXMethod(Context->id, CXXMethod->getName());
|
|
ProcessVersionedAPINotes(*this, CXXMethod, Info);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (auto Field = dyn_cast<FieldDecl>(D)) {
|
|
if (!Field->isUnnamedBitField() && !Field->isAnonymousStructOrUnion()) {
|
|
for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
|
|
if (auto Context = UnwindTagContext(TagContext, APINotes)) {
|
|
auto Info = Reader->lookupField(Context->id, Field->getName());
|
|
ProcessVersionedAPINotes(*this, Field, Info);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (auto Tag = dyn_cast<TagDecl>(D)) {
|
|
for (auto Reader : APINotes.findAPINotes(D->getLocation())) {
|
|
if (auto Context = UnwindTagContext(TagContext, APINotes)) {
|
|
auto Info = Reader->lookupTag(Tag->getName(), Context);
|
|
ProcessVersionedAPINotes(*this, Tag, Info);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|