[clang] [APINotes] Upstream Sema logic to apply API Notes to decls (PR #78445)
Egor Zhdan via cfe-commits
cfe-commits at lists.llvm.org
Mon Feb 26 04:50:55 PST 2024
================
@@ -0,0 +1,989 @@
+//===--- 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 class IsActive_t : bool { Inactive, Active };
+enum class IsReplacement_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,
+ IsReplacement_t Replacement)
+ : Version(Version), IsActive(Active == IsActive_t::Active),
+ IsReplacement(Replacement == IsReplacement_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();
+}
+
+/// Apply nullability to the given declaration.
+static void applyNullability(Sema &S, Decl *D, NullabilityKind Nullability,
+ VersionedInfoMetadata Metadata) {
+ if (!Metadata.IsActive)
+ return;
+
+ auto IsUnmodified = [&](Decl *D, QualType QT,
+ NullabilityKind Nullability) -> bool {
+ QualType Original = QT;
+ S.CheckImplicitNullabilityTypeSpecifier(QT, Nullability, D->getLocation(),
+ isa<ParmVarDecl>(D),
+ /*OverrideExisting=*/true);
+ return QT.getTypePtr() == Original.getTypePtr();
+ };
+
+ if (auto Function = dyn_cast<FunctionDecl>(D)) {
+ if (!IsUnmodified(D, Function->getReturnType(), Nullability)) {
+ QualType FnType = Function->getType();
+ Function->setType(FnType);
+ }
+ } else if (auto Method = dyn_cast<ObjCMethodDecl>(D)) {
+ QualType Type = Method->getReturnType();
+ if (!IsUnmodified(D, Type, Nullability)) {
+ Method->setReturnType(Type);
+
+ // Make it a context-sensitive keyword if we can.
+ if (!isIndirectPointerType(Type))
+ Method->setObjCDeclQualifier(Decl::ObjCDeclQualifier(
+ Method->getObjCDeclQualifier() | Decl::OBJC_TQ_CSNullability));
+ }
+ } else if (auto Value = dyn_cast<ValueDecl>(D)) {
+ QualType Type = Value->getType();
+ if (!IsUnmodified(D, Type, Nullability)) {
+ Value->setType(Type);
+
+ // Make it a context-sensitive keyword if we can.
+ if (auto Parm = dyn_cast<ParmVarDecl>(D)) {
+ if (Parm->isObjCMethodParameter() && !isIndirectPointerType(Type))
+ Parm->setObjCDeclQualifier(Decl::ObjCDeclQualifier(
+ Parm->getObjCDeclQualifier() | Decl::OBJC_TQ_CSNullability));
+ }
+ }
+ } else if (auto Property = dyn_cast<ObjCPropertyDecl>(D)) {
+ QualType Type = Property->getType();
+ if (!IsUnmodified(D, Type, Nullability)) {
+ Property->setType(Type, Property->getTypeSourceInfo());
+
+ // Make it a property attribute if we can.
+ if (!isIndirectPointerType(Type))
+ Property->setPropertyAttributes(
+ ObjCPropertyAttribute::kind_null_resettable);
+ }
+ }
+}
+
+/// 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);
+ }
+
+ } else {
+ 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 = CFReturnsRetainedAttr>
----------------
egorzhdan wrote:
Good point, let's remove the default value here.
https://github.com/llvm/llvm-project/pull/78445
More information about the cfe-commits
mailing list