[clang] [clang] Add the candiscard attribute to suppress nodiscard (PR #154943)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Aug 25 22:11:20 PDT 2025
https://github.com/halbi2 updated https://github.com/llvm/llvm-project/pull/154943
>From bb04ff2cf154b1c5547f7d5236e8fe769b9a54dd Mon Sep 17 00:00:00 2001
From: halbi2 <hehiralbi at gmail.com>
Date: Fri, 22 Aug 2025 09:13:17 -0400
Subject: [PATCH 1/4] [clang] Add the candiscard attribute to suppress
nodiscard
Fulfills the requirement that @huixie90 stated in #139651
that Clang should have a way to disable [[nodiscard]] on a
function by function basis.
This will allow us finally to resume fixing #130656.
---
clang/docs/ReleaseNotes.rst | 4 ++
clang/include/clang/Basic/Attr.td | 8 ++++
clang/include/clang/Basic/AttrDocs.td | 38 ++++++++++++++++++
clang/lib/AST/Expr.cpp | 31 ++++++++++-----
...a-attribute-supported-attributes-list.test | 1 +
clang/test/Sema/c2x-nodiscard.c | 18 ++++++++-
clang/test/SemaCXX/warn-unused-result.cpp | 39 +++++++++++++++++++
clang/test/SemaObjC/attr-nodiscard.m | 10 +++++
clang/test/SemaObjCXX/attr-nodiscard.mm | 16 ++++++++
9 files changed, 155 insertions(+), 10 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 0d85b6f426995..f9d9b4355cef7 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -199,6 +199,10 @@ Removed Compiler Flags
Attribute Changes in Clang
--------------------------
+- A new attribute ``[[clang::candiscard]]`` can be applied to a function returning a nodiscard type
+ to suppress the nodiscard warning on that function in particular. Also, it can be applied to
+ a typedef alias to suppress the nodiscard warning on all functions returning values of the
+ typedef type.
Improvements to Clang's diagnostics
-----------------------------------
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 29364c5903d31..c92a962802594 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -3646,6 +3646,14 @@ def Unavailable : InheritableAttr {
let MeaningfulToClassTemplateDefinition = 1;
}
+def CanDiscard : InheritableAttr {
+ let Spellings = [CXX11<"clang", "candiscard">,
+ GCC<"candiscard">];
+ let Subjects = SubjectList<[ObjCMethod, FunctionLike, TypedefName]>;
+ let Documentation = [CanDiscardDocs];
+ let SimpleHandler = 1;
+}
+
def DiagnoseIf : InheritableAttr {
// Does not have a [[]] spelling because this attribute requires the ability
// to parse function arguments but the attribute is not written in the type
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index b9405c5fc86c1..524bcb0e74cab 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -2452,6 +2452,44 @@ use the annotated ``[[nodiscard]]`` constructor or result in an annotated type.
}];
}
+def CanDiscardDocs : Documentation {
+ let Category = DocCatFunction;
+ let Heading = "candiscard";
+ let Content = [{
+A function whose return type is marked with ``[[nodiscard]]`` generally cannot have
+its return value discarded, even though this may be safe in some rare situations.
+Clang allows an individual function to be marked with ``[[clang::candiscard]]``
+or ``__attribute__((candiscard))`` to override the effect of a ``[[nodiscard]]``
+return type.
+
+.. code-block:: c++
+
+ struct [[nodiscard]] error_info { /*...*/ };
+ error_info enable_missile_safety_mode();
+ [[clang::candiscard]] error_info reload_missiles();
+
+ void test_missiles() {
+ enable_missile_safety_mode(); // diagnoses
+ reload_missiles(); // does not diagnose
+ }
+
+Also, a type alias can be marked with ``[[clang::candiscard]]`` to mask the
+effect of ``[[nodiscard]]`` on the underlying type.
+
+.. code-block:: c++
+
+ struct [[nodiscard]] error_info { /*...*/ };
+ using informational_error_info [[clang::candiscard]] = error_info;
+ error_info enable_missile_safety_mode();
+ informational_error_info reload_missiles();
+
+ void test_missiles() {
+ enable_missile_safety_mode(); // diagnoses
+ reload_missiles(); // does not diagnose
+ }
+ }];
+}
+
def FallthroughDocs : Documentation {
let Category = DocCatStmt;
let Heading = "fallthrough";
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index 340de6d4be934..ee1a3e5f4961b 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -1632,21 +1632,34 @@ QualType CallExpr::getCallReturnType(const ASTContext &Ctx) const {
std::pair<const NamedDecl *, const WarnUnusedResultAttr *>
Expr::getUnusedResultAttrImpl(const Decl *Callee, QualType ReturnType) {
- // If the callee is marked nodiscard, return that attribute
- if (Callee != nullptr)
+ // If the callee is marked nodiscard, return that attribute for the diagnostic.
+ // If the callee is marked candiscard, do not diagnose.
+ // If seen on the same level, candiscard beats nodiscard.
+ if (Callee != nullptr) {
+ if (const auto *A = Callee->getAttr<CanDiscardAttr>())
+ return {nullptr, nullptr};
if (const auto *A = Callee->getAttr<WarnUnusedResultAttr>())
return {nullptr, A};
+ }
- // If the return type is a struct, union, or enum that is marked nodiscard,
- // then return the return type attribute.
- if (const TagDecl *TD = ReturnType->getAsTagDecl())
- if (const auto *A = TD->getAttr<WarnUnusedResultAttr>())
- return {TD, A};
-
+ // Walk the return type's (chain of) type aliases. The first alias
+ // that is marked either nodiscard or candiscard ends the walk.
for (const auto *TD = ReturnType->getAs<TypedefType>(); TD;
- TD = TD->desugar()->getAs<TypedefType>())
+ TD = TD->desugar()->getAs<TypedefType>()) {
+ if (const auto *A = TD->getDecl()->getAttr<CanDiscardAttr>())
+ return {nullptr, nullptr};
if (const auto *A = TD->getDecl()->getAttr<WarnUnusedResultAttr>())
return {TD->getDecl(), A};
+ }
+
+ // Check whether the return type's class declaration is marked nodiscard.
+ if (const TagDecl *TD = ReturnType->getAsTagDecl()) {
+ if (const auto *A = TD->getAttr<CanDiscardAttr>())
+ return {nullptr, nullptr};
+ if (const auto *A = TD->getAttr<WarnUnusedResultAttr>())
+ return {TD, A};
+ }
+
return {nullptr, nullptr};
}
diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
index 37ff33e5a1523..38a771156c3cc 100644
--- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -48,6 +48,7 @@
// CHECK-NEXT: CallableWhen (SubjectMatchRule_function_is_member)
// CHECK-NEXT: Callback (SubjectMatchRule_function)
// CHECK-NEXT: CalledOnce (SubjectMatchRule_variable_is_parameter)
+// CHECK-NEXT: CanDiscard (SubjectMatchRule_objc_method, SubjectMatchRule_hasType_functionType, SubjectMatchRule_type_alias)
// CHECK-NEXT: Capability (SubjectMatchRule_record, SubjectMatchRule_type_alias)
// CHECK-NEXT: CarriesDependency (SubjectMatchRule_variable_is_parameter, SubjectMatchRule_objc_method, SubjectMatchRule_function)
// CHECK-NEXT: Cleanup (SubjectMatchRule_variable_is_local)
diff --git a/clang/test/Sema/c2x-nodiscard.c b/clang/test/Sema/c2x-nodiscard.c
index 852c74721693b..07d76485c3702 100644
--- a/clang/test/Sema/c2x-nodiscard.c
+++ b/clang/test/Sema/c2x-nodiscard.c
@@ -65,15 +65,31 @@ void GH104391() {
M; // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
}
+struct S4 get_s_ignored(void) __attribute__((candiscard));
+enum E2 get_e_ignored(void) __attribute__((candiscard));
+typedef __attribute__((candiscard)) enum E2 EIgnored;
+EIgnored get_e_ignored2();
+
+void f4(void) {
+ get_s_ignored();
+ get_e_ignored();
+ get_e_ignored2();
+}
+
[[nodiscard]] typedef int NoDInt; // expected-warning {{'[[nodiscard]]' attribute ignored when applied to a typedef}}
typedef __attribute__((warn_unused)) int WUInt; // expected-warning {{'warn_unused' attribute only applies to structs, unions, and classes}}
typedef __attribute__((warn_unused_result)) int WURInt;
+typedef __attribute__((candiscard)) WURInt WURIntIgnored;
NoDInt get_nodint();
WUInt get_wuint();
WURInt get_wurint();
+WURIntIgnored get_wurint_ignored();
+WURIntIgnored get_wurint_ignored2() __attribute__((candiscard));
-void f4(void) {
+void f5(void) {
get_nodint(); // no warning because attribute is ignored
get_wuint(); // no warning because attribute is ignored
get_wurint(); // expected-warning {{ignoring return value of type 'WURInt' declared with 'warn_unused_result' attribute}}
+ get_wurint_ignored(); // no warning
+ get_wurint_ignored2(); // no warning
}
diff --git a/clang/test/SemaCXX/warn-unused-result.cpp b/clang/test/SemaCXX/warn-unused-result.cpp
index 098817729efb1..48963449155a4 100644
--- a/clang/test/SemaCXX/warn-unused-result.cpp
+++ b/clang/test/SemaCXX/warn-unused-result.cpp
@@ -425,19 +425,36 @@ struct [[gnu::warn_unused_result]] WarnUnusedResult {
WarnUnusedResult(const char*);
};
+using NoDIgnored [[clang::candiscard]] = NoDiscard;
+using WUIgnored [[clang::candiscard]] = WarnUnused;
+using WURIgnored [[clang::candiscard]] = WarnUnusedResult;
+
NoDiscard return_nodiscard();
WarnUnused return_warnunused();
WarnUnusedResult return_warnunusedresult();
+NoDIgnored return_nodiscard_ignored();
+WUIgnored return_warnunused_ignored();
+WURIgnored return_warnunusedresult_ignored();
+[[clang::candiscard]] NoDiscard return_nodiscard_ignored2();
+[[clang::candiscard]] WarnUnused return_warnunused_ignored2();
+[[clang::candiscard]] WarnUnusedResult return_warnunusedresult_ignored2();
NoDiscard (*p_return_nodiscard)();
WarnUnused (*p_return_warnunused)();
WarnUnusedResult (*p_return_warnunusedresult)();
+NoDIgnored (*p_return_nodiscard_ignored)();
+WUIgnored (*p_return_warnunused_ignored)();
+WURIgnored (*p_return_warnunusedresult_ignored)();
+[[clang::candiscard]] NoDiscard (*p_return_nodiscard_ignored2)();
+[[clang::candiscard]] WarnUnused (*p_return_warnunused_ignored2)();
+[[clang::candiscard]] WarnUnusedResult (*p_return_warnunusedresult_ignored2)();
NoDiscard (*(*pp_return_nodiscard)())();
WarnUnused (*(*pp_return_warnunused)())();
WarnUnusedResult (*(*pp_return_warnunusedresult)())();
template <class T> T from_a_template();
+template <class T> [[clang::candiscard]] T from_a_template_ignored();
void test() {
// Unused but named variables
@@ -474,11 +491,23 @@ void test() {
return_nodiscard(); // expected-warning {{ignoring return value of type 'NoDiscard' declared with 'nodiscard' attribute}}
return_warnunused(); // no warning
return_warnunusedresult(); // expected-warning {{ignoring return value of type 'WarnUnusedResult' declared with 'gnu::warn_unused_result' attribute}}
+ return_nodiscard_ignored(); // no warning
+ return_warnunused_ignored(); // no warning
+ return_warnunusedresult_ignored(); // no warning
+ return_nodiscard_ignored2(); // no warning
+ return_warnunused_ignored2(); // no warning
+ return_warnunusedresult_ignored2(); // no warning
// Function pointer return values
p_return_nodiscard(); // expected-warning {{ignoring return value of type 'NoDiscard' declared with 'nodiscard' attribute}}
p_return_warnunused(); // no warning
p_return_warnunusedresult(); // expected-warning {{ignoring return value of type 'WarnUnusedResult' declared with 'gnu::warn_unused_result' attribute}}
+ p_return_nodiscard_ignored(); // no warning
+ p_return_warnunused_ignored(); // no warning
+ p_return_warnunusedresult_ignored(); // no warning
+ p_return_nodiscard_ignored2(); // no warning
+ p_return_warnunused_ignored2(); // no warning
+ p_return_warnunusedresult_ignored2(); // no warning
// Function pointer expression return values
pp_return_nodiscard()(); // expected-warning {{ignoring return value of type 'NoDiscard' declared with 'nodiscard' attribute}}
@@ -489,6 +518,16 @@ void test() {
from_a_template<NoDiscard>(); // expected-warning {{ignoring return value of type 'NoDiscard' declared with 'nodiscard' attribute}}
from_a_template<WarnUnused>(); // no warning
from_a_template<WarnUnusedResult>(); // expected-warning {{ignoring return value of type 'WarnUnusedResult' declared with 'gnu::warn_unused_result' attribute}}
+
+ // In a template instantiation the information about the typedef is lost,
+ // so the candiscard attribute is lost, so the diagnostic is not suppressed
+ from_a_template<NoDIgnored>(); // expected-warning {{ignoring return value of type 'NoDiscard' declared with 'nodiscard' attribute}}
+ from_a_template<WUIgnored>(); // no warning
+ from_a_template<WURIgnored>(); // expected-warning {{ignoring return value of type 'WarnUnusedResult' declared with 'gnu::warn_unused_result' attribute}}
+
+ from_a_template_ignored<NoDiscard>(); // no warning
+ from_a_template_ignored<WarnUnused>(); // no warning
+ from_a_template_ignored<WarnUnusedResult>(); // no warning
}
} // namespace candiscard
diff --git a/clang/test/SemaObjC/attr-nodiscard.m b/clang/test/SemaObjC/attr-nodiscard.m
index 26bbd247d4a3d..71bb2bb355220 100644
--- a/clang/test/SemaObjC/attr-nodiscard.m
+++ b/clang/test/SemaObjC/attr-nodiscard.m
@@ -6,6 +6,8 @@
[[nodiscard]] typedef int NI; // expected-warning {{'[[nodiscard]]' attribute ignored when applied to a typedef}}
typedef __attribute__((warn_unused_result)) int WUR;
+typedef __attribute__((candiscard)) struct expected EIgnored;
+typedef __attribute__((candiscard)) WUR WURIgnored;
@interface INTF
- (int) a [[nodiscard]];
@@ -13,10 +15,15 @@ + (int) b [[nodiscard]];
- (struct expected) c;
+ (struct expected) d;
- (E) e;
+- (EIgnored) e_ignored;
+- (E) e_ignored2 __attribute__((candiscard));
+ (E) f;
- (void) g [[nodiscard]]; // expected-warning {{attribute 'nodiscard' cannot be applied to Objective-C method without return value}}
- (NI) h;
+- (NI) h_ignored __attribute__((candiscard));
- (WUR) i;
+- (WURIgnored) i_ignored;
+- (WUR) i_ignored2 __attribute__((candiscard));
@end
void foo(INTF *a) {
@@ -28,5 +35,8 @@ void foo(INTF *a) {
[INTF f]; // expected-warning {{ignoring return value of type 'expected' declared with 'nodiscard' attribute}}
[a g]; // no warning because g returns void
[a h]; // no warning because attribute is ignored when applied to a typedef
+ [a h_ignored]; // no warning
[a i]; // expected-warning {{ignoring return value of type 'WUR' declared with 'warn_unused_result' attribute}}
+ [a i_ignored]; // no warning
+ [a i_ignored2]; // no warning
}
diff --git a/clang/test/SemaObjCXX/attr-nodiscard.mm b/clang/test/SemaObjCXX/attr-nodiscard.mm
index 18d829632e428..7a4e101049093 100644
--- a/clang/test/SemaObjCXX/attr-nodiscard.mm
+++ b/clang/test/SemaObjCXX/attr-nodiscard.mm
@@ -8,16 +8,26 @@
using NI [[nodiscard]] = int; // expected-warning {{'[[nodiscard]]' attribute ignored when applied to a typedef}}
using WURI [[clang::warn_unused_result]] = int;
+using EIgnored [[clang::candiscard]] = E;
+using NIIgnored [[clang::candiscard]] = NI;
+using WURIgnored [[clang::candiscard]] = WURI;
+
@interface INTF
- (int) a [[nodiscard]];
+ (int) b [[nodiscard]];
- (expected<int>) c;
+ (expected<int>) d;
- (E) e;
+- (EIgnored) e_ignored;
+- (E) e_ignored2 [[clang::candiscard]];
+ (E) f;
- (void) g [[nodiscard]]; // expected-warning {{attribute 'nodiscard' cannot be applied to Objective-C method without return value}}
- (NI) h;
+- (NIIgnored) h_ignored;
+- (NI) h_ignored2 [[clang::candiscard]];
- (WURI) i;
+- (WURIgnored) i_ignored;
+- (WURI) i_ignored2 [[clang::candiscard]];
@end
void foo(INTF *a) {
@@ -26,8 +36,14 @@ void foo(INTF *a) {
[a c]; // expected-warning {{ignoring return value of type 'expected<int>' declared with 'nodiscard' attribute}}
[INTF d]; // expected-warning {{ignoring return value of type 'expected<int>' declared with 'nodiscard' attribute}}
[a e]; // expected-warning {{ignoring return value of type 'expected<int>' declared with 'nodiscard' attribute}}
+ [a e_ignored]; // no warning
+ [a e_ignored2]; // no warning
[INTF f]; // expected-warning {{ignoring return value of type 'expected<int>' declared with 'nodiscard' attribute}}
[a g]; // no warning because g returns void
[a h]; // no warning because attribute is ignored
+ [a h_ignored]; // no warning
+ [a h_ignored2]; // no warning
[a i]; // expected-warning {{ignoring return value of type 'WURI' declared with 'clang::warn_unused_result' attribute}}
+ [a i_ignored]; // no warning
+ [a i_ignored2]; // no warning
}
>From d9c99df1b0342cf7e160286fde988785270ccd83 Mon Sep 17 00:00:00 2001
From: halbi2 <hehiralbi at gmail.com>
Date: Fri, 22 Aug 2025 09:24:19 -0400
Subject: [PATCH 2/4] fix formatting
---
clang/lib/AST/Expr.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index ee1a3e5f4961b..a479dca6674a4 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -1632,8 +1632,8 @@ QualType CallExpr::getCallReturnType(const ASTContext &Ctx) const {
std::pair<const NamedDecl *, const WarnUnusedResultAttr *>
Expr::getUnusedResultAttrImpl(const Decl *Callee, QualType ReturnType) {
- // If the callee is marked nodiscard, return that attribute for the diagnostic.
- // If the callee is marked candiscard, do not diagnose.
+ // If the callee is marked nodiscard, return that attribute for the
+ // diagnostic. If the callee is marked candiscard, do not diagnose.
// If seen on the same level, candiscard beats nodiscard.
if (Callee != nullptr) {
if (const auto *A = Callee->getAttr<CanDiscardAttr>())
>From 8555df9d6525b3af472c08747d7d5d2e7ffa2926 Mon Sep 17 00:00:00 2001
From: halbi2 <hehiralbi at gmail.com>
Date: Sun, 24 Aug 2025 21:34:45 -0400
Subject: [PATCH 3/4] Review comments
---
clang/include/clang/Basic/Attr.td | 3 +-
clang/test/SemaCXX/warn-unused-result.cpp | 44 +++++++++++++++++++++++
2 files changed, 45 insertions(+), 2 deletions(-)
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index c92a962802594..81d262988aac2 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -3647,8 +3647,7 @@ def Unavailable : InheritableAttr {
}
def CanDiscard : InheritableAttr {
- let Spellings = [CXX11<"clang", "candiscard">,
- GCC<"candiscard">];
+ let Spellings = [Clang<"candiscard">];
let Subjects = SubjectList<[ObjCMethod, FunctionLike, TypedefName]>;
let Documentation = [CanDiscardDocs];
let SimpleHandler = 1;
diff --git a/clang/test/SemaCXX/warn-unused-result.cpp b/clang/test/SemaCXX/warn-unused-result.cpp
index 48963449155a4..fcf107f188404 100644
--- a/clang/test/SemaCXX/warn-unused-result.cpp
+++ b/clang/test/SemaCXX/warn-unused-result.cpp
@@ -456,6 +456,34 @@ WarnUnusedResult (*(*pp_return_warnunusedresult)())();
template <class T> T from_a_template();
template <class T> [[clang::candiscard]] T from_a_template_ignored();
+struct A {
+ using NoDInt [[clang::warn_unused_result]] = int;
+ struct [[nodiscard]] NoDClass {};
+ template <class T> using NoDTemplate [[clang::warn_unused_result]] = T;
+};
+
+template <class T>
+struct B {
+ using NoDInt = typename T::NoDInt;
+ using NoDIntIgnored [[clang::candiscard]] = typename T::NoDInt;
+ using NoDClass = typename T::NoDClass;
+ using NoDClassIgnored [[clang::candiscard]] = typename T::NoDClass;
+ template <class U> using NoDInt2 = typename U::NoDInt;
+ template <class U> using NoDIntIgnored2 [[clang::candiscard]] = typename U::NoDInt;
+ template <class U> using NoDClass2 = typename U::NoDClass;
+ template <class U> using NoDClassIgnored2 [[clang::candiscard]] = typename U::NoDClass;
+};
+
+A::NoDTemplate<int> return_nodtemplate();
+B<A>::NoDInt return_nodint();
+B<A>::NoDIntIgnored return_nodint_ignored();
+B<A>::NoDClass return_nodclass();
+B<A>::NoDClassIgnored return_nodclass_ignored();
+B<A>::NoDInt2<A> return_nodint2();
+B<A>::NoDIntIgnored2<A> return_nodint_ignored2();
+B<A>::NoDClass2<A> return_nodclass2();
+B<A>::NoDClassIgnored2<A> return_nodclass_ignored2();
+
void test() {
// Unused but named variables
NoDiscard unused_variable1(1); // no warning
@@ -528,6 +556,22 @@ void test() {
from_a_template_ignored<NoDiscard>(); // no warning
from_a_template_ignored<WarnUnused>(); // no warning
from_a_template_ignored<WarnUnusedResult>(); // no warning
+
+ return_nodint(); // expected-warning {{ignoring return value of type 'NoDInt' declared with 'clang::warn_unused_result' attribute}}
+ return_nodint_ignored(); // no warning
+ return_nodclass(); // expected-warning {{ignoring return value of type 'NoDClass' declared with 'nodiscard' attribute}}
+ return_nodclass_ignored(); // no warning
+
+ // In an alias template the information about the typedef is lost,
+ // so the diagnostic is not issued. GH#68456
+ return_nodtemplate();
+
+ // In an alias template the information about the typedef is lost,
+ // so the diagnostic is not suppressed. GH#68456
+ return_nodint2(); // expected-warning {{ignoring return value of type 'NoDInt' declared with 'clang::warn_unused_result' attribute}}
+ return_nodint_ignored2(); // expected-warning {{ignoring return value of type 'NoDInt' declared with 'clang::warn_unused_result' attribute}}
+ return_nodclass2(); // expected-warning {{ignoring return value of type 'NoDClass' declared with 'nodiscard' attribute}}
+ return_nodclass_ignored2(); // expected-warning {{ignoring return value of type 'NoDClass' declared with 'nodiscard' attribute}}
}
} // namespace candiscard
>From cb0ee6d337a5168c369c109542a9d8183b63e38b Mon Sep 17 00:00:00 2001
From: halbi2 <hehiralbi at gmail.com>
Date: Tue, 26 Aug 2025 01:10:32 -0400
Subject: [PATCH 4/4] Review comments: both attributes, member fn ptrs
---
clang/test/SemaCXX/warn-unused-result.cpp | 47 +++++++++++++++++++----
1 file changed, 40 insertions(+), 7 deletions(-)
diff --git a/clang/test/SemaCXX/warn-unused-result.cpp b/clang/test/SemaCXX/warn-unused-result.cpp
index fcf107f188404..f9c676faa5464 100644
--- a/clang/test/SemaCXX/warn-unused-result.cpp
+++ b/clang/test/SemaCXX/warn-unused-result.cpp
@@ -484,7 +484,17 @@ B<A>::NoDIntIgnored2<A> return_nodint_ignored2();
B<A>::NoDClass2<A> return_nodclass2();
B<A>::NoDClassIgnored2<A> return_nodclass_ignored2();
-void test() {
+NoDiscard (A::*mp_return_nodiscard)();
+WarnUnused (A::*mp_return_warnunused)();
+WarnUnusedResult (A::*mp_return_warnunusedresult)();
+NoDIgnored (A::*mp_return_nodiscard_ignored)();
+WUIgnored (A::*mp_return_warnunused_ignored)();
+WURIgnored (A::*mp_return_warnunusedresult_ignored)();
+[[clang::candiscard]] NoDiscard (A::*mp_return_nodiscard_ignored2)();
+[[clang::candiscard]] WarnUnused (A::*mp_return_warnunused_ignored2)();
+[[clang::candiscard]] WarnUnusedResult (A::*mp_return_warnunusedresult_ignored2)();
+
+void test(A *pa) {
// Unused but named variables
NoDiscard unused_variable1(1); // no warning
NoDiscard unused_variable2(""); // no warning
@@ -516,9 +526,9 @@ void test() {
static_cast<WarnUnusedResult>(""); // expected-warning {{ignoring temporary of type 'WarnUnusedResult' declared with 'gnu::warn_unused_result' attribute}}
// Function return values
- return_nodiscard(); // expected-warning {{ignoring return value of type 'NoDiscard' declared with 'nodiscard' attribute}}
- return_warnunused(); // no warning
- return_warnunusedresult(); // expected-warning {{ignoring return value of type 'WarnUnusedResult' declared with 'gnu::warn_unused_result' attribute}}
+ return_nodiscard(); // expected-warning {{ignoring return value of type 'NoDiscard' declared with 'nodiscard' attribute}}
+ return_warnunused(); // no warning
+ return_warnunusedresult(); // expected-warning {{ignoring return value of type 'WarnUnusedResult' declared with 'gnu::warn_unused_result' attribute}}
return_nodiscard_ignored(); // no warning
return_warnunused_ignored(); // no warning
return_warnunusedresult_ignored(); // no warning
@@ -527,9 +537,9 @@ void test() {
return_warnunusedresult_ignored2(); // no warning
// Function pointer return values
- p_return_nodiscard(); // expected-warning {{ignoring return value of type 'NoDiscard' declared with 'nodiscard' attribute}}
- p_return_warnunused(); // no warning
- p_return_warnunusedresult(); // expected-warning {{ignoring return value of type 'WarnUnusedResult' declared with 'gnu::warn_unused_result' attribute}}
+ p_return_nodiscard(); // expected-warning {{ignoring return value of type 'NoDiscard' declared with 'nodiscard' attribute}}
+ p_return_warnunused(); // no warning
+ p_return_warnunusedresult(); // expected-warning {{ignoring return value of type 'WarnUnusedResult' declared with 'gnu::warn_unused_result' attribute}}
p_return_nodiscard_ignored(); // no warning
p_return_warnunused_ignored(); // no warning
p_return_warnunusedresult_ignored(); // no warning
@@ -537,6 +547,17 @@ void test() {
p_return_warnunused_ignored2(); // no warning
p_return_warnunusedresult_ignored2(); // no warning
+ // Member function pointer return values
+ (pa->*mp_return_nodiscard)(); // expected-warning {{ignoring return value of type 'NoDiscard' declared with 'nodiscard' attribute}}
+ (pa->*mp_return_warnunused)(); // no warning
+ (pa->*mp_return_warnunusedresult)(); // expected-warning {{ignoring return value of type 'WarnUnusedResult' declared with 'gnu::warn_unused_result' attribute}}
+ (pa->*mp_return_nodiscard_ignored)(); // no warning
+ (pa->*mp_return_warnunused_ignored)(); // no warning
+ (pa->*mp_return_warnunusedresult_ignored)(); // no warning
+ (pa->*mp_return_nodiscard_ignored2)(); // no warning
+ (pa->*mp_return_warnunused_ignored2)(); // no warning
+ (pa->*mp_return_warnunusedresult_ignored2)(); // no warning
+
// Function pointer expression return values
pp_return_nodiscard()(); // expected-warning {{ignoring return value of type 'NoDiscard' declared with 'nodiscard' attribute}}
pp_return_warnunused()(); // no warning
@@ -574,4 +595,16 @@ void test() {
return_nodclass_ignored2(); // expected-warning {{ignoring return value of type 'NoDClass' declared with 'nodiscard' attribute}}
}
+using BothAttributes [[clang::warn_unused_result, clang::candiscard]] = int;
+
+BothAttributes return_bothattributes1();
+[[nodiscard, clang::candiscard]] int return_bothattributes2();
+[[nodiscard]] NoDIgnored return_nodignored_nodiscard();
+
+void testBothAttributes() {
+ return_bothattributes1(); // no warning because candiscard takes priority
+ return_bothattributes2(); // no warning because candiscard takes priority
+ return_nodignored_nodiscard(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+}
+
} // namespace candiscard
More information about the cfe-commits
mailing list