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