Author: halbi2 Date: 2025-08-20T14:14:35Z New Revision: 2f237670b1f1ed792a3b1b6a12f8e9af08685983
URL: https://github.com/llvm/llvm-project/commit/2f237670b1f1ed792a3b1b6a12f8e9af08685983 DIFF: https://github.com/llvm/llvm-project/commit/2f237670b1f1ed792a3b1b6a12f8e9af08685983.diff LOG: [Clang] [Sema] Enable nodiscard warnings for function pointers (#154250) 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 Added: Modified: clang/docs/ReleaseNotes.rst clang/lib/AST/Expr.cpp clang/test/Sema/c2x-nodiscard.c clang/test/SemaCXX/warn-unused-result.cpp clang/test/SemaObjC/attr-nodiscard.m clang/test/SemaObjCXX/attr-nodiscard.mm Removed: ################################################################################ diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index e88d68fa99664..6d60b03d9f62b 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -216,8 +216,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/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..098817729efb1 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()(); // expected-warning {{ignoring return value of type 'NoDiscard' declared with 'nodiscard' attribute}} + pp_return_warnunused()(); // no warning + 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}} + 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}} } _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits