https://github.com/egorzhdan created https://github.com/llvm/llvm-project/pull/78445
This upstreams more of the Clang API Notes functionality that is currently implemented in the Apple fork: https://github.com/apple/llvm-project/tree/next/clang/lib/APINotes This was extracted from a larger PR: https://github.com/llvm/llvm-project/pull/73017 >From 983122bb88e736049a01b294b4f0f6eac0ade359 Mon Sep 17 00:00:00 2001 From: Egor Zhdan <e_zh...@apple.com> Date: Wed, 17 Jan 2024 13:41:25 +0000 Subject: [PATCH] [APINotes] Upstream Sema logic to apply API Notes to decls This upstreams more of the Clang API Notes functionality that is currently implemented in the Apple fork: https://github.com/apple/llvm-project/tree/next/clang/lib/APINotes --- .../clang/Basic/DiagnosticSemaKinds.td | 7 + clang/include/clang/Sema/Sema.h | 6 + clang/lib/Sema/CMakeLists.txt | 1 + clang/lib/Sema/SemaDecl.cpp | 4 + clang/lib/Sema/SemaDeclAttr.cpp | 3 + clang/lib/Sema/SemaDeclCXX.cpp | 6 +- clang/lib/Sema/SemaDeclObjC.cpp | 4 + clang/lib/Sema/SemaObjCProperty.cpp | 5 + clang/lib/Sema/SemaTemplate.cpp | 7 + llvm/clang/lib/Sema/SemaAPINotes.cpp | 1014 +++++++++++++++++ 10 files changed, 1056 insertions(+), 1 deletion(-) create mode 100644 llvm/clang/lib/Sema/SemaAPINotes.cpp diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 03b0122d1c08f75..b705310dd37d6cb 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10723,6 +10723,13 @@ def warn_imp_cast_drops_unaligned : Warning< } // end of sema category +let CategoryName = "API Notes Issue" in { + +def err_incompatible_replacement_type : Error< + "API notes replacement type %0 has a different size from original type %1">; + +} // end of API Notes category + let CategoryName = "OpenMP Issue" in { // OpenMP support. def err_omp_expected_var_arg : Error< diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 2c0ad022bcf19d4..f191f791741363b 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -4763,6 +4763,12 @@ class Sema final { bool checkCommonAttributeFeatures(const Stmt *S, const ParsedAttr &A, bool SkipArgCountCheck = false); + /// Map any API notes provided for this declaration to attributes on the + /// declaration. + /// + /// Triggered by declaration-attribute processing. + void ProcessAPINotes(Decl *D); + /// Determine if type T is a valid subject for a nonnull and similar /// attributes. By default, we look through references (the behavior used by /// nonnull), but if the second parameter is true, then we treat a reference diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt index 1856a88e9a3271a..4f72bce98fbbec3 100644 --- a/clang/lib/Sema/CMakeLists.txt +++ b/clang/lib/Sema/CMakeLists.txt @@ -27,6 +27,7 @@ add_clang_library(clangSema Sema.cpp SemaAccess.cpp SemaAttr.cpp + SemaAPINotes.cpp SemaAvailability.cpp SemaCXXScopeSpec.cpp SemaCast.cpp diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 9e6e4ab882c0be4..d001305d5ac175b 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -16364,6 +16364,7 @@ void Sema::ActOnFinishDelayedAttribute(Scope *S, Decl *D, if (TemplateDecl *TD = dyn_cast<TemplateDecl>(D)) D = TD->getTemplatedDecl(); ProcessDeclAttributeList(S, D, Attrs); + ProcessAPINotes(D); if (CXXMethodDecl *Method = dyn_cast_or_null<CXXMethodDecl>(D)) if (Method->isStatic()) @@ -19583,6 +19584,7 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl, CDecl->setIvarRBraceLoc(RBrac); } } + ProcessAPINotes(Record); } /// Determine whether the given integral value is representable within @@ -19897,6 +19899,7 @@ Decl *Sema::ActOnEnumConstant(Scope *S, Decl *theEnumDecl, Decl *lastEnumConst, // Process attributes. ProcessDeclAttributeList(S, New, Attrs); AddPragmaAttributes(S, New); + ProcessAPINotes(New); // Register this decl in the current scope stack. New->setAccess(TheEnumDecl->getAccess()); @@ -20095,6 +20098,7 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceRange BraceRange, QualType EnumType = Context.getTypeDeclType(Enum); ProcessDeclAttributeList(S, Enum, Attrs); + ProcessAPINotes(Enum); if (Enum->isDependentType()) { for (unsigned i = 0, e = Elements.size(); i != e; ++i) { diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index a482919356e1bce..21d3f1bb900986f 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -10129,6 +10129,9 @@ void Sema::ProcessDeclAttributes(Scope *S, Decl *D, const Declarator &PD) { // Apply additional attributes specified by '#pragma clang attribute'. AddPragmaAttributes(S, D); + + // Look for API notes that map to attributes. + ProcessAPINotes(D); } /// Is the given declaration allowed to use a forbidden type? diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index a2ce96188b4f161..5305da18e4cd105 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -11700,6 +11700,7 @@ Decl *Sema::ActOnStartNamespaceDef(Scope *NamespcScope, ProcessDeclAttributeList(DeclRegionScope, Namespc, AttrList); AddPragmaAttributes(DeclRegionScope, Namespc); + ProcessAPINotes(Namespc); // FIXME: Should we be merging attributes? if (const VisibilityAttr *Attr = Namespc->getAttr<VisibilityAttr>()) @@ -12246,8 +12247,10 @@ Decl *Sema::ActOnUsingDirective(Scope *S, SourceLocation UsingLoc, Diag(IdentLoc, diag::err_expected_namespace_name) << SS.getRange(); } - if (UDir) + if (UDir) { ProcessDeclAttributeList(S, UDir, AttrList); + ProcessAPINotes(UDir); + } return UDir; } @@ -13539,6 +13542,7 @@ Decl *Sema::ActOnAliasDeclaration(Scope *S, AccessSpecifier AS, ProcessDeclAttributeList(S, NewTD, AttrList); AddPragmaAttributes(S, NewTD); + ProcessAPINotes(NewTD); CheckTypedefForVariablyModifiedType(S, NewTD); Invalid |= NewTD->isInvalidDecl(); diff --git a/clang/lib/Sema/SemaDeclObjC.cpp b/clang/lib/Sema/SemaDeclObjC.cpp index bb0d0cd2030ba8a..2011f4084dd2ab6 100644 --- a/clang/lib/Sema/SemaDeclObjC.cpp +++ b/clang/lib/Sema/SemaDeclObjC.cpp @@ -1072,6 +1072,7 @@ ObjCInterfaceDecl *Sema::ActOnStartClassInterface( ProcessDeclAttributeList(TUScope, IDecl, AttrList); AddPragmaAttributes(TUScope, IDecl); + ProcessAPINotes(IDecl); // Merge attributes from previous declarations. if (PrevIDecl) @@ -1273,6 +1274,7 @@ ObjCProtocolDecl *Sema::ActOnStartProtocolInterface( ProcessDeclAttributeList(TUScope, PDecl, AttrList); AddPragmaAttributes(TUScope, PDecl); + ProcessAPINotes(PDecl); // Merge attributes from previous declarations. if (PrevDecl) @@ -4807,6 +4809,7 @@ Decl *Sema::ActOnMethodDeclaration( // Apply the attributes to the parameter. ProcessDeclAttributeList(TUScope, Param, ArgInfo[i].ArgAttrs); AddPragmaAttributes(TUScope, Param); + ProcessAPINotes(Param); if (Param->hasAttr<BlocksAttr>()) { Diag(Param->getLocation(), diag::err_block_on_nonlocal); @@ -4837,6 +4840,7 @@ Decl *Sema::ActOnMethodDeclaration( ProcessDeclAttributeList(TUScope, ObjCMethod, AttrList); AddPragmaAttributes(TUScope, ObjCMethod); + ProcessAPINotes(ObjCMethod); // Add the method now. const ObjCMethodDecl *PrevMethod = nullptr; diff --git a/clang/lib/Sema/SemaObjCProperty.cpp b/clang/lib/Sema/SemaObjCProperty.cpp index 349c7fc9c91bdb6..4636d89ebf2b84f 100644 --- a/clang/lib/Sema/SemaObjCProperty.cpp +++ b/clang/lib/Sema/SemaObjCProperty.cpp @@ -2509,6 +2509,8 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property) { GetterMethod->addAttr(SectionAttr::CreateImplicit( Context, SA->getName(), Loc, SectionAttr::GNU_section)); + ProcessAPINotes(GetterMethod); + if (getLangOpts().ObjCAutoRefCount) CheckARCMethodDecl(GetterMethod); } else @@ -2578,6 +2580,9 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property) { if (const SectionAttr *SA = property->getAttr<SectionAttr>()) SetterMethod->addAttr(SectionAttr::CreateImplicit( Context, SA->getName(), Loc, SectionAttr::GNU_section)); + + ProcessAPINotes(SetterMethod); + // It's possible for the user to have set a very odd custom // setter selector that causes it to have a method family. if (getLangOpts().ObjCAutoRefCount) diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index c0dcbda1fd6221d..476c13760789f0d 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -2154,6 +2154,7 @@ DeclResult Sema::CheckClassTemplate( NewClass->startDefinition(); ProcessDeclAttributeList(S, NewClass, Attr); + ProcessAPINotes(NewClass); if (PrevClassTemplate) mergeDeclAttributes(NewClass, PrevClassTemplate->getTemplatedDecl()); @@ -9045,6 +9046,7 @@ DeclResult Sema::ActOnClassTemplateSpecialization( } ProcessDeclAttributeList(S, Specialization, Attr); + ProcessAPINotes(Specialization); // Add alignment attributes if necessary; these attributes are checked when // the ASTContext lays out the structure. @@ -10280,6 +10282,7 @@ DeclResult Sema::ActOnExplicitInstantiation( bool PreviouslyDLLExported = Specialization->hasAttr<DLLExportAttr>(); ProcessDeclAttributeList(S, Specialization, Attr); + ProcessAPINotes(Specialization); // Add the explicit instantiation into its lexical context. However, // since explicit instantiations are never found by name lookup, we @@ -10690,6 +10693,9 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S, Prev->setTemplateSpecializationKind(TSK, D.getIdentifierLoc()); // Merge attributes. ProcessDeclAttributeList(S, Prev, D.getDeclSpec().getAttributes()); + if (PrevTemplate) + ProcessAPINotes(Prev); + if (TSK == TSK_ExplicitInstantiationDefinition) InstantiateVariableDefinition(D.getIdentifierLoc(), Prev); } @@ -10865,6 +10871,7 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S, } ProcessDeclAttributeList(S, Specialization, D.getDeclSpec().getAttributes()); + ProcessAPINotes(Specialization); // In MSVC mode, dllimported explicit instantiation definitions are treated as // instantiation declarations. diff --git a/llvm/clang/lib/Sema/SemaAPINotes.cpp b/llvm/clang/lib/Sema/SemaAPINotes.cpp new file mode 100644 index 000000000000000..0a04a998918f6f3 --- /dev/null +++ b/llvm/clang/lib/Sema/SemaAPINotes.cpp @@ -0,0 +1,1014 @@ +//===--- 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 "clang/APINotes/APINotesReader.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclObjC.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Lex/Lexer.h" +#include "clang/Sema/SemaInternal.h" + +using namespace clang; + +namespace { +enum IsActive_t : bool { IsNotActive, IsActive }; +enum IsReplacement_t : bool { IsNotReplacement, IsReplacement }; + +struct VersionedInfoMetadata { + /// An empty version refers to unversioned metadata. + VersionTuple Version; + unsigned IsActive : 1; + unsigned IsReplacement : 1; + + VersionedInfoMetadata(VersionTuple Version, IsActive_t Active, + IsReplacement_t Replacement) + : Version(Version), IsActive(Active == IsActive_t::IsActive), + IsReplacement(Replacement == IsReplacement_t::IsReplacement) {} +}; +} // 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 *D, 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>(D)) + Type = Function->getReturnType(); + else if (auto Method = dyn_cast<ObjCMethodDecl>(D)) + Type = Method->getReturnType(); + else if (auto Value = dyn_cast<ValueDecl>(D)) + Type = Value->getType(); + else if (auto Property = dyn_cast<ObjCPropertyDecl>(D)) + Type = Property->getType(); + else + return; + + // Check the nullability specifier on this type. + QualType OrigType = Type; + S.CheckImplicitNullabilityTypeSpecifier(Type, Nullability, D->getLocation(), + isa<ParmVarDecl>(D), + /*overrideExisting=*/true); + if (Type.getTypePtr() == OrigType.getTypePtr()) + return; + + if (auto Function = dyn_cast<FunctionDecl>(D)) { + 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>(D)) { + 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>(D)) { + Value->setType(Type); + + // Make it a context-sensitive keyword if we can. + if (auto Parm = dyn_cast<ParmVarDecl>(D)) { + if (Parm->isObjCMethodParameter() && !isMultiLevelPointerType(Type)) + Parm->setObjCDeclQualifier(Decl::ObjCDeclQualifier( + Parm->getObjCDeclQualifier() | Decl::OBJC_TQ_CSNullability)); + } + } else if (auto Property = dyn_cast<ObjCPropertyDecl>(D)) { + Property->setType(Type, Property->getTypeSourceInfo()); + + // Make it a property attribute if we can. + if (!isMultiLevelPointerType(Type)) + Property->setPropertyAttributes( + ObjCPropertyAttribute::kind_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()); +} + +static AttributeCommonInfo getDummyAttrInfo() { + 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 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<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 (ShouldAddAttribute) { + if (auto Attr = CreateAttr()) + D->addAttr(Attr); + } + + } else { + if (ShouldAddAttribute) { + 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 = CFReturnsRetainedAttr> +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, getDummyAttrInfo()); }, + [](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(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, getDummyAttrInfo(), + CopyString(S.Context, Info.UnavailableMsg)); + }); + } + + if (Info.UnavailableInSwift) { + handleAPINotedAttribute<AvailabilityAttr>( + S, D, true, Metadata, + [&] { + return new (S.Context) AvailabilityAttr( + S.Context, getDummyAttrInfo(), &S.Context.Idents.get("swift"), + VersionTuple(), VersionTuple(), VersionTuple(), + /*Unavailable=*/true, CopyString(S.Context, Info.UnavailableMsg), + /*Strict=*/false, + /*Replacement=*/StringRef(), + /*Priority=*/Sema::AP_Explicit); + }, + [](const Decl *D) { + return llvm::find_if(D->attrs(), [](const Attr *next) -> bool { + auto *AA = dyn_cast<AvailabilityAttr>(next); + if (!AA) + return false; + const IdentifierInfo *platform = AA->getPlatform(); + if (!platform) + return false; + return platform->isStr("swift"); + }); + }); + } + + // swift_private + if (auto SwiftPrivate = Info.isSwiftPrivate()) { + handleAPINotedAttribute<SwiftPrivateAttr>( + S, D, *SwiftPrivate, Metadata, [&] { + return new (S.Context) + SwiftPrivateAttr(S.Context, getDummyAttrInfo()); + }); + } + + // 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(), nullptr, + SourceLocation(), nullptr, nullptr, nullptr, + ParsedAttr::Form::GNU()); + + if (!S.DiagnoseSwiftName(D, Info.SwiftName, D->getLocation(), *SNA, + /*IsAsync=*/false)) + return nullptr; + + return new (S.Context) + SwiftNameAttr(S.Context, getDummyAttrInfo(), + 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 new (S.Context) + SwiftBridgeAttr(S.Context, getDummyAttrInfo(), + CopyString(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, getDummyAttrInfo(), + &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(), TypeInfo); + 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 new (S.Context) NoEscapeAttr(S.Context, getDummyAttrInfo()); + }); + + // 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 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, getDummyAttrInfo()); + }); + } +} + +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())); + } + } + + // 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 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, getDummyAttrInfo()); + }); + } + + // 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) { + auto attr = SwiftAttrAttr::Create(S.Context, "import_" + ImportAs.value()); + D->addAttr(attr); + } + if (auto RetainOp = Info.SwiftRetainOp) { + auto attr = SwiftAttrAttr::Create(S.Context, "retain:" + RetainOp.value()); + D->addAttr(attr); + } + if (auto ReleaseOp = Info.SwiftReleaseOp) { + auto attr = + SwiftAttrAttr::Create(S.Context, "release:" + ReleaseOp.value()); + D->addAttr(attr); + } + + 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, getDummyAttrInfo(), kind); + }); + } + + if (auto FlagEnum = Info.isFlagEnum()) { + handleAPINotedAttribute<FlagEnumAttr>(S, D, *FlagEnum, Metadata, [&] { + return new (S.Context) FlagEnumAttr(S.Context, getDummyAttrInfo()); + }); + } + + // 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::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 new (S.Context) + SwiftImportAsNonGenericAttr(S.Context, getDummyAttrInfo()); + }); + } + + if (auto ObjcMembers = Info.getSwiftObjCMembers()) { + handleAPINotedAttribute<SwiftObjCMembersAttr>( + S, D, *ObjcMembers, Metadata, [&] { + return new (S.Context) + SwiftObjCMembersAttr(S.Context, getDummyAttrInfo()); + }); + } + + // 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, IsNotActive, + IsReplacement); + 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().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 : IsNotActive; + auto Replacement = IsNotReplacement; + if (Active == IsNotActive && Version.empty()) { + Replacement = IsReplacement; + Version = Info[Selected].first; + } + ProcessAPINotes(S, D, InfoSlice, + VersionedInfoMetadata(Version, Active, Replacement)); + } +} + +/// 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() || + D->getDeclContext()->isNamespace() || + D->getDeclContext()->isExternCContext() || + D->getDeclContext()->isExternCXXContext()) { + std::optional<api_notes::Context> APINotesContext; + if (auto NamespaceContext = dyn_cast<NamespaceDecl>(D->getDeclContext())) { + 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) + break; + } + if (NamespaceID) + APINotesContext = api_notes::Context( + *NamespaceID, api_notes::ContextKind::Namespace); + } + } + + // 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)) { + std::string 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())) { + 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 (D->getDeclContext()->getRedeclContext()->isFileContext() || + D->getDeclContext()->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>(D->getDeclContext())) { + // 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; + 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; + } + + return; + } +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits