https://github.com/halbi2 updated 
https://github.com/llvm/llvm-project/pull/154943

>From bb04ff2cf154b1c5547f7d5236e8fe769b9a54dd Mon Sep 17 00:00:00 2001
From: halbi2 <hehira...@gmail.com>
Date: Fri, 22 Aug 2025 09:13:17 -0400
Subject: [PATCH 1/2] [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 <hehira...@gmail.com>
Date: Fri, 22 Aug 2025 09:24:19 -0400
Subject: [PATCH 2/2] 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>())

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to