[clang] [clang] Fix deprecation attribute being ignored when used inside other attributes that are already applied to a deprecated symbol (PR #222094)

via cfe-commits cfe-commits at lists.llvm.org
Tue Sep 8 11:52:15 PDT 2026


https://github.com/higher-performance updated https://github.com/llvm/llvm-project/pull/222094

>From 0f2dcd88c441b970903b5fc1a506c2385480aa77 Mon Sep 17 00:00:00 2001
From: higher-performance <higher.performance.github at gmail.com>
Date: Tue, 8 Sep 2026 14:08:35 -0400
Subject: [PATCH] [clang] Fix deprecation attribute being ignored when used
 inside other attributes that are already applied to a deprecated symbol

---
 clang/include/clang/Sema/DelayedDiagnostic.h |  9 ++++-
 clang/include/clang/Sema/Sema.h              |  9 +++++
 clang/lib/Sema/DelayedDiagnostic.cpp         | 16 ++++----
 clang/lib/Sema/SemaAvailability.cpp          | 40 +++++++++++---------
 clang/test/SemaCXX/attr-deprecated.cpp       | 27 +++++++++++++
 5 files changed, 73 insertions(+), 28 deletions(-)

diff --git a/clang/include/clang/Sema/DelayedDiagnostic.h b/clang/include/clang/Sema/DelayedDiagnostic.h
index 0105089a393f1..ebe865e284336 100644
--- a/clang/include/clang/Sema/DelayedDiagnostic.h
+++ b/clang/include/clang/Sema/DelayedDiagnostic.h
@@ -141,7 +141,8 @@ class DelayedDiagnostic {
                                             const ObjCInterfaceDecl *UnknownObjCClass,
                                             const ObjCPropertyDecl  *ObjCProperty,
                                             StringRef Msg,
-                                            bool ObjCPropertyAccess);
+                                            bool ObjCPropertyAccess,
+                                            bool InAttrArg);
 
   static DelayedDiagnostic makeAccess(SourceLocation Loc,
                                       const AccessedEntity &Entity) {
@@ -232,6 +233,11 @@ class DelayedDiagnostic {
     return AvailabilityData.ObjCPropertyAccess;
   }
 
+  bool getAvailabilityInAttrArg() const {
+    assert(Kind == Availability && "Not an availability diagnostic.");
+    return AvailabilityData.InAttrArg;
+  }
+
 private:
   struct AD {
     const NamedDecl *ReferringDecl;
@@ -244,6 +250,7 @@ class DelayedDiagnostic {
     size_t NumSelectorLocs;
     AvailabilityResult AR;
     bool ObjCPropertyAccess;
+    bool InAttrArg;
   };
 
   struct FTD {
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 4ff4c669a6b70..b2d600ba3ab61 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -6989,6 +6989,15 @@ class Sema final : public SemaBase {
            ExpressionEvaluationContextRecord::ExpressionKind::EK_AttrArgument;
   }
 
+  bool isInsideAttrContext() const {
+    for (const auto &Record : ExprEvalContexts) {
+      if (Record.ExprContext ==
+          ExpressionEvaluationContextRecord::ExpressionKind::EK_AttrArgument)
+        return true;
+    }
+    return false;
+  }
+
   /// Increment when we find a reference; decrement when we find an ignored
   /// assignment.  Ultimately the value is 0 if every reference is an ignored
   /// assignment.
diff --git a/clang/lib/Sema/DelayedDiagnostic.cpp b/clang/lib/Sema/DelayedDiagnostic.cpp
index cb2721b92090e..42f9fd0f73c17 100644
--- a/clang/lib/Sema/DelayedDiagnostic.cpp
+++ b/clang/lib/Sema/DelayedDiagnostic.cpp
@@ -20,15 +20,12 @@
 using namespace clang;
 using namespace sema;
 
-DelayedDiagnostic
-DelayedDiagnostic::makeAvailability(AvailabilityResult AR,
-                                    ArrayRef<SourceLocation> Locs,
-                                    const NamedDecl *ReferringDecl,
-                                    const NamedDecl *OffendingDecl,
-                                    const ObjCInterfaceDecl *UnknownObjCClass,
-                                    const ObjCPropertyDecl  *ObjCProperty,
-                                    StringRef Msg,
-                                    bool ObjCPropertyAccess) {
+DelayedDiagnostic DelayedDiagnostic::makeAvailability(
+    AvailabilityResult AR, ArrayRef<SourceLocation> Locs,
+    const NamedDecl *ReferringDecl, const NamedDecl *OffendingDecl,
+    const ObjCInterfaceDecl *UnknownObjCClass,
+    const ObjCPropertyDecl *ObjCProperty, StringRef Msg,
+    bool ObjCPropertyAccess, bool InAttrArg) {
   assert(!Locs.empty());
   DelayedDiagnostic DD;
   DD.Kind = Availability;
@@ -53,6 +50,7 @@ DelayedDiagnostic::makeAvailability(AvailabilityResult AR,
 
   DD.AvailabilityData.AR = AR;
   DD.AvailabilityData.ObjCPropertyAccess = ObjCPropertyAccess;
+  DD.AvailabilityData.InAttrArg = InAttrArg;
   return DD;
 }
 
diff --git a/clang/lib/Sema/SemaAvailability.cpp b/clang/lib/Sema/SemaAvailability.cpp
index 6ae08b0783f40..609970efc4728 100644
--- a/clang/lib/Sema/SemaAvailability.cpp
+++ b/clang/lib/Sema/SemaAvailability.cpp
@@ -163,9 +163,12 @@ Sema::ShouldDiagnoseAvailabilityOfDecl(const NamedDecl *D, std::string *Message,
 /// whether we should emit a diagnostic for \c K and \c DeclVersion in
 /// the context of \c Ctx. For example, we should emit an unavailable diagnostic
 /// in a deprecated context, but not the other way around.
-static bool ShouldDiagnoseAvailabilityInContext(
-    Sema &S, AvailabilityResult K, VersionTuple DeclVersion,
-    const IdentifierInfo *DeclEnv, Decl *Ctx, const NamedDecl *OffendingDecl) {
+static bool ShouldDiagnoseAvailabilityInContext(Sema &S, AvailabilityResult K,
+                                                VersionTuple DeclVersion,
+                                                const IdentifierInfo *DeclEnv,
+                                                Decl *Ctx,
+                                                const NamedDecl *OffendingDecl,
+                                                bool InAttrArg) {
   assert(K != AR_Available && "Expected an unavailable declaration here!");
 
   // If this was defined using CF_OPTIONS, etc. then ignore the diagnostic.
@@ -210,7 +213,7 @@ static bool ShouldDiagnoseAvailabilityInContext(
             AA->getEffectiveEnvironment() == DeclEnv)
           return true;
     } else if (K == AR_Deprecated) {
-      if (C->isDeprecated())
+      if (!InAttrArg && C->isDeprecated())
         return true;
       // Don't emit deprecated warnings when defining special member functions.
       if (const auto *FD = dyn_cast<FunctionDecl>(C); FD && FD->isDefaulted())
@@ -227,7 +230,7 @@ static bool ShouldDiagnoseAvailabilityInContext(
       }
     }
 
-    if (C->isUnavailable())
+    if (!InAttrArg && C->isUnavailable())
       return true;
     return false;
   };
@@ -408,14 +411,14 @@ createAttributeInsertion(const NamedDecl *D, const SourceManager &SM,
 /// may not be the same as ReferringDecl, i.e. if an EnumDecl is annotated and
 /// we refer to a member EnumConstantDecl, ReferringDecl is the EnumConstantDecl
 /// and OffendingDecl is the EnumDecl.
-static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K,
-                                      Decl *Ctx, const NamedDecl *ReferringDecl,
+static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K, Decl *Ctx,
+                                      const NamedDecl *ReferringDecl,
                                       const NamedDecl *OffendingDecl,
                                       StringRef Message,
                                       ArrayRef<SourceLocation> Locs,
                                       const ObjCInterfaceDecl *UnknownObjCClass,
                                       const ObjCPropertyDecl *ObjCProperty,
-                                      bool ObjCPropertyAccess) {
+                                      bool ObjCPropertyAccess, bool InAttrArg) {
   // Diagnostics for deprecated or unavailable.
   unsigned diag, diag_message, diag_fwdclass_message;
   unsigned diag_available_here = diag::note_availability_specified_here;
@@ -436,7 +439,7 @@ static void DoEmitAvailabilityWarning(Sema &S, AvailabilityResult K,
   }
 
   if (!ShouldDiagnoseAvailabilityInContext(S, K, DeclVersion, IIEnv, Ctx,
-                                           OffendingDecl))
+                                           OffendingDecl, InAttrArg))
     return;
 
   SourceLocation Loc = Locs.front();
@@ -712,7 +715,7 @@ void Sema::handleDelayedAvailabilityCheck(DelayedDiagnostic &DD, Decl *Ctx) {
       *this, DD.getAvailabilityResult(), Ctx, DD.getAvailabilityReferringDecl(),
       DD.getAvailabilityOffendingDecl(), DD.getAvailabilityMessage(),
       DD.getAvailabilitySelectorLocs(), DD.getUnknownObjCClass(),
-      DD.getObjCProperty(), false);
+      DD.getObjCProperty(), false, DD.getAvailabilityInAttrArg());
 }
 
 static void EmitAvailabilityWarning(Sema &S, AvailabilityResult AR,
@@ -723,19 +726,19 @@ static void EmitAvailabilityWarning(Sema &S, AvailabilityResult AR,
                                     const ObjCInterfaceDecl *UnknownObjCClass,
                                     const ObjCPropertyDecl *ObjCProperty,
                                     bool ObjCPropertyAccess) {
+  bool InAttrArg = S.isInsideAttrContext();
   // Delay if we're currently parsing a declaration.
   if (S.DelayedDiagnostics.shouldDelayDiagnostics()) {
-    S.DelayedDiagnostics.add(
-        DelayedDiagnostic::makeAvailability(
-            AR, Locs, ReferringDecl, OffendingDecl, UnknownObjCClass,
-            ObjCProperty, Message, ObjCPropertyAccess));
+    S.DelayedDiagnostics.add(DelayedDiagnostic::makeAvailability(
+        AR, Locs, ReferringDecl, OffendingDecl, UnknownObjCClass, ObjCProperty,
+        Message, ObjCPropertyAccess, InAttrArg));
     return;
   }
 
   Decl *Ctx = cast<Decl>(S.getCurLexicalContext());
-  DoEmitAvailabilityWarning(S, AR, Ctx, ReferringDecl, OffendingDecl,
-                            Message, Locs, UnknownObjCClass, ObjCProperty,
-                            ObjCPropertyAccess);
+  DoEmitAvailabilityWarning(S, AR, Ctx, ReferringDecl, OffendingDecl, Message,
+                            Locs, UnknownObjCClass, ObjCProperty,
+                            ObjCPropertyAccess, InAttrArg);
 }
 
 namespace {
@@ -905,7 +908,8 @@ void DiagnoseUnguardedAvailability::DiagnoseDeclAvailability(
     // emit a diagnostic.
     if (!ShouldDiagnoseAvailabilityInContext(SemaRef, Result, Introduced,
                                              AA->getEffectiveEnvironment(), Ctx,
-                                             OffendingDecl))
+                                             OffendingDecl,
+                                             /*InAttrArg=*/false))
       return;
 
     const TargetInfo &TI = SemaRef.getASTContext().getTargetInfo();
diff --git a/clang/test/SemaCXX/attr-deprecated.cpp b/clang/test/SemaCXX/attr-deprecated.cpp
index 0286cb0cfc09a..19f1ce7e531ee 100644
--- a/clang/test/SemaCXX/attr-deprecated.cpp
+++ b/clang/test/SemaCXX/attr-deprecated.cpp
@@ -266,3 +266,30 @@ template <typename T> struct D : T {
 };
 D<A> da; // expected-note {{in instantiation of template class}}
 } // namespace test8
+
+namespace test9 {
+__attribute__((deprecated)) const char check = 1; // #TEST9_CHECK
+
+#define DIAGNOSE_IF(Expr)                                                      \
+  __attribute__((diagnose_if(Expr, "", "warning"))) __attribute__((deprecated))
+
+// expected-note@#TEST9_CHECK {{'check' has been explicitly marked deprecated here}}
+// expected-warning at +1 {{'check' is deprecated}}
+DIAGNOSE_IF(check != 1) void old_func();
+
+namespace inner {
+// expected-note@#TEST9_CHECK {{'check' has been explicitly marked deprecated here}}
+// expected-warning at +1 {{'check' is deprecated}}
+DIAGNOSE_IF(check != 1) void old_inner_func();
+} // namespace inner
+
+struct S {
+  // expected-note@#TEST9_CHECK {{'check' has been explicitly marked deprecated here}}
+  // expected-warning at +1 {{'check' is deprecated}}
+  DIAGNOSE_IF(check != 1) void old_member();
+};
+
+// expected-note@#TEST9_CHECK {{'check' has been explicitly marked deprecated here}}
+// expected-warning at +1 {{'check' is deprecated}}
+template <typename T> DIAGNOSE_IF(check != 1) void old_tmpl();
+} // namespace test9



More information about the cfe-commits mailing list