https://github.com/halbi2 updated https://github.com/llvm/llvm-project/pull/154250
>From 1efa70c6702f7506580a5b8c49ea3df779d31163 Mon Sep 17 00:00:00 2001 From: halbi2 <hehira...@gmail.com> Date: Mon, 18 Aug 2025 21:46:47 -0400 Subject: [PATCH 1/2] [clang] [test] Better test coverage for nodiscard attributes --- clang/test/Sema/c2x-nodiscard.c | 17 +++++ clang/test/SemaCXX/warn-unused-result.cpp | 85 +++++++++++++++++++++++ clang/test/SemaObjC/attr-nodiscard.m | 9 ++- clang/test/SemaObjCXX/attr-nodiscard.mm | 9 ++- 4 files changed, 118 insertions(+), 2 deletions(-) diff --git a/clang/test/Sema/c2x-nodiscard.c b/clang/test/Sema/c2x-nodiscard.c index e2537bcf1d29d..852c74721693b 100644 --- a/clang/test/Sema/c2x-nodiscard.c +++ b/clang/test/Sema/c2x-nodiscard.c @@ -41,6 +41,10 @@ void f2(void) { (void)get_s3(); (void)get_i(); (void)get_e(); + + One; // expected-warning {{expression result unused}} + (enum E2)(0); // expected-warning {{expression result unused}} + (struct S4){1}; // expected-warning {{expression result unused}} } struct [[nodiscard]] error_info{ @@ -60,3 +64,16 @@ void GH104391() { #define M (unsigned int) f3() M; // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}} } + +[[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; +NoDInt get_nodint(); +WUInt get_wuint(); +WURInt get_wurint(); + +void f4(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}} +} diff --git a/clang/test/SemaCXX/warn-unused-result.cpp b/clang/test/SemaCXX/warn-unused-result.cpp index 1f7913f1aa994..bd46fdfd57061 100644 --- a/clang/test/SemaCXX/warn-unused-result.cpp +++ b/clang/test/SemaCXX/warn-unused-result.cpp @@ -407,3 +407,88 @@ void doGccThings() { } } // namespace BuildStringOnClangScope + +namespace candiscard { + +struct [[nodiscard]] NoDiscard { + [[nodiscard]] NoDiscard(int); + NoDiscard(const char *); +}; + +struct [[gnu::warn_unused]] WarnUnused { + [[gnu::warn_unused]] WarnUnused(int); // expected-warning {{'gnu::warn_unused' attribute only applies to structs, unions, and classes}} + WarnUnused(const char*); +}; + +struct [[gnu::warn_unused_result]] WarnUnusedResult { + [[gnu::warn_unused_result]] WarnUnusedResult(int); + WarnUnusedResult(const char*); +}; + +NoDiscard return_nodiscard(); +WarnUnused return_warnunused(); +WarnUnusedResult return_warnunusedresult(); + +NoDiscard (*p_return_nodiscard)(); +WarnUnused (*p_return_warnunused)(); +WarnUnusedResult (*p_return_warnunusedresult)(); + +NoDiscard (*(*pp_return_nodiscard)())(); +WarnUnused (*(*pp_return_warnunused)())(); +WarnUnusedResult (*(*pp_return_warnunusedresult)())(); + +template <class T> T from_a_template(); + +void test() { + // Unused but named variables + NoDiscard unused_variable1(1); // no warning + NoDiscard unused_variable2(""); // no warning + WarnUnused unused_variable3(1); // no warning + WarnUnused unused_variable4(""); // no warning + WarnUnusedResult unused_variable5(1); // no warning + WarnUnusedResult unused_variable6(""); // no warning + + // Constructor return values + NoDiscard(1); // expected-warning {{ignoring temporary created by a constructor declared with 'nodiscard' attribute}} + NoDiscard(""); // expected-warning {{ignoring temporary of type 'NoDiscard' declared with 'nodiscard' attribute}} + WarnUnused(1); // expected-warning {{expression result unused}} + WarnUnused(""); // expected-warning {{expression result unused}} + WarnUnusedResult(1); // expected-warning {{ignoring temporary created by a constructor declared with 'gnu::warn_unused_result' attribute}} + WarnUnusedResult(""); // expected-warning {{ignoring temporary of type 'WarnUnusedResult' declared with 'gnu::warn_unused_result' attribute}} + + NoDiscard{1}; // expected-warning {{ignoring temporary created by a constructor declared with 'nodiscard' attribute}} + NoDiscard{""}; // expected-warning {{ignoring temporary of type 'NoDiscard' declared with 'nodiscard' attribute}} + WarnUnused{1}; // expected-warning {{expression result unused}} + WarnUnused{""}; // expected-warning {{expression result unused}} + WarnUnusedResult{1}; // expected-warning {{ignoring temporary created by a constructor declared with 'gnu::warn_unused_result' attribute}} + WarnUnusedResult{""}; // expected-warning {{ignoring temporary of type 'WarnUnusedResult' declared with 'gnu::warn_unused_result' attribute}} + + static_cast<NoDiscard>(1); // expected-warning {{ignoring temporary created by a constructor declared with 'nodiscard' attribute}} + static_cast<NoDiscard>(""); // expected-warning {{ignoring temporary of type 'NoDiscard' declared with 'nodiscard' attribute}} + static_cast<WarnUnused>(1); // expected-warning {{expression result unused}} + static_cast<WarnUnused>(""); // expected-warning {{expression result unused}} + static_cast<WarnUnusedResult>(1); // expected-warning {{ignoring temporary created by a constructor declared with 'gnu::warn_unused_result' attribute}} + 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}} + + // 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}} + + // Function pointer expression return values + pp_return_nodiscard()(); // no warning: TODO + pp_return_warnunused()(); // no warning + pp_return_warnunusedresult()(); // no warning: TODO + + // From a template + 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}} +} + +} // namespace candiscard diff --git a/clang/test/SemaObjC/attr-nodiscard.m b/clang/test/SemaObjC/attr-nodiscard.m index 6d04665da25ca..26bbd247d4a3d 100644 --- a/clang/test/SemaObjC/attr-nodiscard.m +++ b/clang/test/SemaObjC/attr-nodiscard.m @@ -4,6 +4,9 @@ typedef struct expected E; +[[nodiscard]] typedef int NI; // expected-warning {{'[[nodiscard]]' attribute ignored when applied to a typedef}} +typedef __attribute__((warn_unused_result)) int WUR; + @interface INTF - (int) a [[nodiscard]]; + (int) b [[nodiscard]]; @@ -12,6 +15,8 @@ + (struct expected) d; - (E) e; + (E) f; - (void) g [[nodiscard]]; // expected-warning {{attribute 'nodiscard' cannot be applied to Objective-C method without return value}} +- (NI) h; +- (WUR) i; @end void foo(INTF *a) { @@ -21,5 +26,7 @@ void foo(INTF *a) { [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]; + [a g]; // no warning because g returns void + [a h]; // no warning because attribute is ignored when applied to a typedef + [a i]; // expected-warning {{ignoring return value of type 'WUR' declared with 'warn_unused_result' attribute}} } diff --git a/clang/test/SemaObjCXX/attr-nodiscard.mm b/clang/test/SemaObjCXX/attr-nodiscard.mm index e1eefb74d3961..18d829632e428 100644 --- a/clang/test/SemaObjCXX/attr-nodiscard.mm +++ b/clang/test/SemaObjCXX/attr-nodiscard.mm @@ -5,6 +5,9 @@ using E = expected<int>; +using NI [[nodiscard]] = int; // expected-warning {{'[[nodiscard]]' attribute ignored when applied to a typedef}} +using WURI [[clang::warn_unused_result]] = int; + @interface INTF - (int) a [[nodiscard]]; + (int) b [[nodiscard]]; @@ -13,6 +16,8 @@ + (int) b [[nodiscard]]; - (E) e; + (E) f; - (void) g [[nodiscard]]; // expected-warning {{attribute 'nodiscard' cannot be applied to Objective-C method without return value}} +- (NI) h; +- (WURI) i; @end void foo(INTF *a) { @@ -22,5 +27,7 @@ void foo(INTF *a) { [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]; + [a g]; // no warning because g returns void + [a h]; // no warning because attribute is ignored + [a i]; // expected-warning {{ignoring return value of type 'WURI' declared with 'clang::warn_unused_result' attribute}} } >From 7f5eb56f6f88ce708f3071a573c346c95f8ec736 Mon Sep 17 00:00:00 2001 From: halbi2 <hehira...@gmail.com> Date: Mon, 18 Aug 2025 21:47:32 -0400 Subject: [PATCH 2/2] [clang] [Sema] Enable nodiscard warnings for function pointers A call through a function pointer has no associated FunctionDecl, but it still might have a nodiscard return type. Ensure there is a codepath to emit the nodiscard warning in this case. Fixes #142453 --- clang/docs/ReleaseNotes.rst | 4 +-- clang/lib/AST/Expr.cpp | 33 +++++++++++------------ clang/test/SemaCXX/warn-unused-result.cpp | 4 +-- 3 files changed, 20 insertions(+), 21 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index b86a9c437ffb1..f21801017d492 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -212,8 +212,8 @@ Bug Fixes to Compiler Builtins Bug Fixes to Attribute Support ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- ``[[nodiscard]]`` is now respected on Objective-C and Objective-C++ methods. - (#GH141504) +- ``[[nodiscard]]`` is now respected on Objective-C and Objective-C++ methods + (#GH141504) and on types returned from indirect calls (#GH142453). - Fixes some late parsed attributes, when applied to function definitions, not being parsed in function try blocks, and some situations where parsing of the function body is skipped, such as error recovery and code completion. (#GH153551) diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index e14cff552c922..340de6d4be934 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -2776,23 +2776,22 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, case UserDefinedLiteralClass: { // If this is a direct call, get the callee. const CallExpr *CE = cast<CallExpr>(this); - if (const Decl *FD = CE->getCalleeDecl()) { - // If the callee has attribute pure, const, or warn_unused_result, warn - // about it. void foo() { strlen("bar"); } should warn. - // - // Note: If new cases are added here, DiagnoseUnusedExprResult should be - // updated to match for QoI. - if (CE->hasUnusedResultAttr(Ctx) || - FD->hasAttr<PureAttr>() || FD->hasAttr<ConstAttr>()) { - WarnE = this; - Loc = CE->getCallee()->getBeginLoc(); - R1 = CE->getCallee()->getSourceRange(); - - if (unsigned NumArgs = CE->getNumArgs()) - R2 = SourceRange(CE->getArg(0)->getBeginLoc(), - CE->getArg(NumArgs - 1)->getEndLoc()); - return true; - } + // If the callee has attribute pure, const, or warn_unused_result, warn + // about it. void foo() { strlen("bar"); } should warn. + // Note: If new cases are added here, DiagnoseUnusedExprResult should be + // updated to match for QoI. + const Decl *FD = CE->getCalleeDecl(); + bool PureOrConst = + FD && (FD->hasAttr<PureAttr>() || FD->hasAttr<ConstAttr>()); + if (CE->hasUnusedResultAttr(Ctx) || PureOrConst) { + WarnE = this; + Loc = getBeginLoc(); + R1 = getSourceRange(); + + if (unsigned NumArgs = CE->getNumArgs()) + R2 = SourceRange(CE->getArg(0)->getBeginLoc(), + CE->getArg(NumArgs - 1)->getEndLoc()); + return true; } return false; } diff --git a/clang/test/SemaCXX/warn-unused-result.cpp b/clang/test/SemaCXX/warn-unused-result.cpp index bd46fdfd57061..098817729efb1 100644 --- a/clang/test/SemaCXX/warn-unused-result.cpp +++ b/clang/test/SemaCXX/warn-unused-result.cpp @@ -481,9 +481,9 @@ void test() { p_return_warnunusedresult(); // expected-warning {{ignoring return value of type 'WarnUnusedResult' declared with 'gnu::warn_unused_result' attribute}} // Function pointer expression return values - pp_return_nodiscard()(); // no warning: TODO + pp_return_nodiscard()(); // expected-warning {{ignoring return value of type 'NoDiscard' declared with 'nodiscard' attribute}} pp_return_warnunused()(); // no warning - pp_return_warnunusedresult()(); // no warning: TODO + pp_return_warnunusedresult()(); // expected-warning {{ignoring return value of type 'WarnUnusedResult' declared with 'gnu::warn_unused_result' attribute}} // From a template from_a_template<NoDiscard>(); // expected-warning {{ignoring return value of type 'NoDiscard' declared with 'nodiscard' attribute}} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits