[clang] [clang] Diagnose [[nodiscard]] return types in Objective-C++ (PR #142541)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Jun 5 19:38:37 PDT 2025
https://github.com/halbi2 updated https://github.com/llvm/llvm-project/pull/142541
>From c683b2aa84cba1b7057592e50c542cd5645adde5 Mon Sep 17 00:00:00 2001
From: halbi2 <hehiralbi at 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 <hehiralbi at 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>;
+
+ at 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}}
+ at 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 <hehiralbi at 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;
+
+ at 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}}
+ at 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];
+}
More information about the cfe-commits
mailing list