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

>From c683b2aa84cba1b7057592e50c542cd5645adde5 Mon Sep 17 00:00:00 2001
From: halbi2 <hehira...@gmail.com>
Date: Mon, 26 May 2025 15:35:13 -0400
Subject: [PATCH 1/3] [clang] [test] More coverage of [[nodiscard]]

---
 clang/test/SemaCXX/warn-unused-result.cpp | 25 +++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/clang/test/SemaCXX/warn-unused-result.cpp 
b/clang/test/SemaCXX/warn-unused-result.cpp
index 5105f347db8b5..a1d201ec861d5 100644
--- a/clang/test/SemaCXX/warn-unused-result.cpp
+++ b/clang/test/SemaCXX/warn-unused-result.cpp
@@ -364,3 +364,28 @@ void id_print_name() {
     ((int(*)())f)();
 }
 } // namespace GH117975
+
+namespace inheritance {
+// Test that [[nodiscard]] is not inherited by derived class types,
+// but is inherited by member functions
+struct [[nodiscard]] E {
+  [[nodiscard]] explicit E(int);
+  explicit E(const char*);
+  [[nodiscard]] int f();
+};
+struct F : E {
+  using E::E;
+};
+E e();
+F f();
+void test() {
+  e();     // expected-warning {{ignoring return value of type 'E' declared 
with 'nodiscard' attribute}}
+  f();     // no warning: derived class type does not inherit the attribute
+  E(1);    // expected-warning {{ignoring temporary created by a constructor 
declared with 'nodiscard' attribute}}
+  E("x");  // expected-warning {{ignoring temporary of type 'E' declared with 
'nodiscard' attribute}}
+  F(1);    // no warning: inherited constructor does not inherit the attribute 
either
+  F("x");  // no warning
+  e().f(); // expected-warning {{ignoring return value of function declared 
with 'nodiscard' attribute}}
+  f().f(); // expected-warning {{ignoring return value of function declared 
with 'nodiscard' attribute}}
+}
+} // namespace inheritance

>From 0c5aa2275ded4152281487dd262a4110dd1cc743 Mon Sep 17 00:00:00 2001
From: halbi2 <hehira...@gmail.com>
Date: Mon, 26 May 2025 15:35:54 -0400
Subject: [PATCH 2/3] [clang] Diagnose nodiscard return types in Objective-C++

Fixes #141504
---
 clang/include/clang/AST/ExprObjC.h      | 11 +++++++++++
 clang/lib/AST/Expr.cpp                  | 11 +++++------
 clang/lib/AST/ExprObjC.cpp              | 21 ++++++++++++++++++++
 clang/lib/Sema/SemaStmt.cpp             | 13 ++++++-------
 clang/test/SemaObjCXX/attr-nodiscard.mm | 26 +++++++++++++++++++++++++
 5 files changed, 69 insertions(+), 13 deletions(-)
 create mode 100644 clang/test/SemaObjCXX/attr-nodiscard.mm

diff --git a/clang/include/clang/AST/ExprObjC.h 
b/clang/include/clang/AST/ExprObjC.h
index f87fa85569c44..a6b2fca3df13a 100644
--- a/clang/include/clang/AST/ExprObjC.h
+++ b/clang/include/clang/AST/ExprObjC.h
@@ -1236,6 +1236,17 @@ class ObjCMessageExpr final
   /// of `instancetype` (in that case it's an expression type).
   QualType getCallReturnType(ASTContext &Ctx) const;
 
+  /// Returns the WarnUnusedResultAttr that is either declared on the called
+  /// method, or its return type declaration, together with a NamedDecl that
+  /// refers to the declaration the attribute is attached onto.
+  std::pair<const NamedDecl *, const Attr *>
+  getUnusedResultAttr(ASTContext &Ctx) const;
+
+  /// Returns true if this message send should warn on unused results.
+  bool hasUnusedResultAttr(ASTContext &Ctx) const {
+    return getUnusedResultAttr(Ctx).second != nullptr;
+  }
+
   /// Source range of the receiver.
   SourceRange getReceiverRange() const;
 
diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp
index fe874ccd7b60f..1142745a25f1a 100644
--- a/clang/lib/AST/Expr.cpp
+++ b/clang/lib/AST/Expr.cpp
@@ -2869,12 +2869,11 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, 
SourceLocation &Loc,
       return true;
     }
 
-    if (const ObjCMethodDecl *MD = ME->getMethodDecl())
-      if (MD->hasAttr<WarnUnusedResultAttr>()) {
-        WarnE = this;
-        Loc = getExprLoc();
-        return true;
-      }
+    if (ME->hasUnusedResultAttr(Ctx)) {
+      WarnE = this;
+      Loc = getExprLoc();
+      return true;
+    }
 
     return false;
   }
diff --git a/clang/lib/AST/ExprObjC.cpp b/clang/lib/AST/ExprObjC.cpp
index 79b5db301d414..1de679ab87140 100644
--- a/clang/lib/AST/ExprObjC.cpp
+++ b/clang/lib/AST/ExprObjC.cpp
@@ -12,6 +12,7 @@
 
 #include "clang/AST/ExprObjC.h"
 #include "clang/AST/ASTContext.h"
+#include "clang/AST/Attr.h"
 #include "clang/AST/ComputeDependence.h"
 #include "clang/AST/SelectorLocationsKind.h"
 #include "clang/AST/Type.h"
@@ -272,6 +273,26 @@ QualType ObjCMessageExpr::getCallReturnType(ASTContext 
&Ctx) const {
   return Ctx.getReferenceQualifiedType(this);
 }
 
+std::pair<const NamedDecl *, const Attr *>
+ObjCMessageExpr::getUnusedResultAttr(ASTContext &Ctx) const {
+  // If the callee is marked nodiscard, return that attribute
+  if (const ObjCMethodDecl *MD = getMethodDecl())
+    if (const auto *A = MD->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 = getCallReturnType(Ctx)->getAsTagDecl())
+    if (const auto *A = TD->getAttr<WarnUnusedResultAttr>())
+      return {TD, A};
+
+  for (const auto *TD = getCallReturnType(Ctx)->getAs<TypedefType>(); TD;
+       TD = TD->desugar()->getAs<TypedefType>())
+    if (const auto *A = TD->getDecl()->getAttr<WarnUnusedResultAttr>())
+      return {TD->getDecl(), A};
+  return {nullptr, nullptr};
+}
+
 SourceRange ObjCMessageExpr::getReceiverRange() const {
   switch (getReceiverKind()) {
   case Instance:
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index c943645c3ab9d..91a3b3eb43b1e 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -344,13 +344,12 @@ void DiagnoseUnused(Sema &S, const Expr *E, 
std::optional<unsigned> DiagID) {
       S.Diag(Loc, diag::err_arc_unused_init_message) << R1;
       return;
     }
-    const ObjCMethodDecl *MD = ME->getMethodDecl();
-    if (MD) {
-      if (DiagnoseNoDiscard(S, nullptr, MD->getAttr<WarnUnusedResultAttr>(),
-                            Loc, R1, R2,
-                            /*isCtor=*/false))
-        return;
-    }
+
+    auto [OffendingDecl, A] = ME->getUnusedResultAttr(S.Context);
+    if (DiagnoseNoDiscard(S, OffendingDecl,
+                          cast_or_null<WarnUnusedResultAttr>(A), Loc, R1, R2,
+                          /*isCtor=*/false))
+      return;
   } else if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E)) {
     const Expr *Source = POE->getSyntacticForm();
     // Handle the actually selected call of an OpenMP specialized call.
diff --git a/clang/test/SemaObjCXX/attr-nodiscard.mm 
b/clang/test/SemaObjCXX/attr-nodiscard.mm
new file mode 100644
index 0000000000000..e1eefb74d3961
--- /dev/null
+++ b/clang/test/SemaObjCXX/attr-nodiscard.mm
@@ -0,0 +1,26 @@
+// RUN: %clang_cc1  -fsyntax-only -verify %s
+
+template<class T>
+struct [[nodiscard]] expected {};
+
+using E = expected<int>;
+
+@interface INTF
+- (int) a [[nodiscard]];
++ (int) b [[nodiscard]];
+- (expected<int>) c;
++ (expected<int>) d;
+- (E) e;
++ (E) f;
+- (void) g [[nodiscard]]; // expected-warning {{attribute 'nodiscard' cannot 
be applied to Objective-C method without return value}}
+@end
+
+void foo(INTF *a) {
+  [a a]; // expected-warning {{ignoring return value of function declared with 
'nodiscard' attribute}}
+  [INTF b]; // expected-warning {{ignoring return value of function declared 
with 'nodiscard' attribute}}
+  [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}}
+  [INTF f]; // expected-warning {{ignoring return value of type 
'expected<int>' declared with 'nodiscard' attribute}}
+  [a g];
+}

>From c0e5f84b2fd2372b36ec8932dc220b0a789ddeb4 Mon Sep 17 00:00:00 2001
From: halbi2 <hehira...@gmail.com>
Date: Thu, 5 Jun 2025 22:38:04 -0400
Subject: [PATCH 3/3] Review comments

---
 clang/docs/ReleaseNotes.rst          |  3 +++
 clang/test/SemaObjC/attr-nodiscard.m | 25 +++++++++++++++++++++++++
 2 files changed, 28 insertions(+)
 create mode 100644 clang/test/SemaObjC/attr-nodiscard.m

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 780716b089e41..fa25d4ca30917 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -710,6 +710,9 @@ Bug Fixes to Attribute Support
 - Clang will warn if a complete type specializes a deprecated partial 
specialization.
   (#GH44496)
 
+- ``[[nodiscard]]`` is now respected on Objective-C and Objective-C++ methods.
+  (#GH141504)
+
 Bug Fixes to C++ Support
 ^^^^^^^^^^^^^^^^^^^^^^^^
 
diff --git a/clang/test/SemaObjC/attr-nodiscard.m 
b/clang/test/SemaObjC/attr-nodiscard.m
new file mode 100644
index 0000000000000..9bef558855512
--- /dev/null
+++ b/clang/test/SemaObjC/attr-nodiscard.m
@@ -0,0 +1,25 @@
+// RUN: %clang_cc1  -fsyntax-only -verify %s
+
+struct [[nodiscard]] expected {};
+
+typedef expected E;
+
+@interface INTF
+- (int) a [[nodiscard]];
++ (int) b [[nodiscard]];
+- (expected) c;
++ (expected) d;
+- (E) e;
++ (E) f;
+- (void) g [[nodiscard]]; // expected-warning {{attribute 'nodiscard' cannot 
be applied to Objective-C method without return value}}
+@end
+
+void foo(INTF *a) {
+  [a a]; // expected-warning {{ignoring return value of function declared with 
'nodiscard' attribute}}
+  [INTF b]; // expected-warning {{ignoring return value of function declared 
with 'nodiscard' attribute}}
+  [a c]; // expected-warning {{ignoring return value of type 'expected' 
declared with 'nodiscard' attribute}}
+  [INTF d]; // expected-warning {{ignoring return value of type 'expected' 
declared with 'nodiscard' attribute}}
+  [a e]; // expected-warning {{ignoring return value of type 'expected' 
declared with 'nodiscard' attribute}}
+  [INTF f]; // expected-warning {{ignoring return value of type 'expected' 
declared with 'nodiscard' attribute}}
+  [a g];
+}

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

Reply via email to