https://github.com/halbi2 updated https://github.com/llvm/llvm-project/pull/139651
>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 23c5b44123961f252103d84b6f862242fdf54819 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/lib/AST/Expr.cpp | 10 ++++++++++ clang/test/SemaCXX/warn-unused-result.cpp | 4 ++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/clang/lib/AST/Expr.cpp b/clang/lib/AST/Expr.cpp index e14cff552c922..36e091915e0c8 100644 --- a/clang/lib/AST/Expr.cpp +++ b/clang/lib/AST/Expr.cpp @@ -2794,6 +2794,16 @@ bool Expr::isUnusedResultAWarning(const Expr *&WarnE, SourceLocation &Loc, return true; } } + if (CE->hasUnusedResultAttr(Ctx)) { + 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