[clang] [llvm] [APINotes] Upstream Sema logic to apply API Notes to decls (PR #78445)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Jan 17 05:43:54 PST 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Egor Zhdan (egorzhdan)
<details>
<summary>Changes</summary>
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
---
Patch is 47.11 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/78445.diff
10 Files Affected:
- (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+7)
- (modified) clang/include/clang/Sema/Sema.h (+6)
- (modified) clang/lib/Sema/CMakeLists.txt (+1)
- (modified) clang/lib/Sema/SemaDecl.cpp (+4)
- (modified) clang/lib/Sema/SemaDeclAttr.cpp (+3)
- (modified) clang/lib/Sema/SemaDeclCXX.cpp (+5-1)
- (modified) clang/lib/Sema/SemaDeclObjC.cpp (+4)
- (modified) clang/lib/Sema/SemaObjCProperty.cpp (+5)
- (modified) clang/lib/Sema/SemaTemplate.cpp (+7)
- (added) llvm/clang/lib/Sema/SemaAPINotes.cpp (+1014)
``````````diff
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 03b0122d1c08f7..b705310dd37d6c 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 2c0ad022bcf19d..f191f791741363 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 1856a88e9a3271..4f72bce98fbbec 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 9e6e4ab882c0be..d001305d5ac175 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 a482919356e1bc..21d3f1bb900986 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 a2ce96188b4f16..5305da18e4cd10 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 bb0d0cd2030ba8..2011f4084dd2ab 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 349c7fc9c91bdb..4636d89ebf2b84 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 c0dcbda1fd6221..476c13760789f0 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 00000000000000..0a04a998918f6f
--- /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.Unavailable...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/78445
More information about the cfe-commits
mailing list