[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